0
# Fluent Logging API
1
2
SLF4J 2.0 introduces a modern fluent API that provides a chainable interface for building complex log statements. This API supports structured logging with key-value pairs, lazy evaluation, and builder pattern for enhanced logging capabilities.
3
4
## Capabilities
5
6
### Fluent Entry Points
7
8
Logger methods that return LoggingEventBuilder instances for chainable operations.
9
10
```java { .api }
11
/**
12
* Entry point for fluent-logging for TRACE level
13
* @return LoggingEventBuilder instance as appropriate for level TRACE
14
*/
15
LoggingEventBuilder atTrace();
16
17
/**
18
* Entry point for fluent-logging for DEBUG level
19
* @return LoggingEventBuilder instance as appropriate for level DEBUG
20
*/
21
LoggingEventBuilder atDebug();
22
23
/**
24
* Entry point for fluent-logging for INFO level
25
* @return LoggingEventBuilder instance as appropriate for level INFO
26
*/
27
LoggingEventBuilder atInfo();
28
29
/**
30
* Entry point for fluent-logging for WARN level
31
* @return LoggingEventBuilder instance as appropriate for level WARN
32
*/
33
LoggingEventBuilder atWarn();
34
35
/**
36
* Entry point for fluent-logging for ERROR level
37
* @return LoggingEventBuilder instance as appropriate for level ERROR
38
*/
39
LoggingEventBuilder atError();
40
41
/**
42
* Entry point for fluent-logging for any level
43
* @param level desired level for the event builder
44
* @return LoggingEventBuilder instance as appropriate for the specified level
45
*/
46
LoggingEventBuilder atLevel(Level level);
47
48
/**
49
* Check if logger is enabled for the specified level
50
* @param level the level to check
51
* @return true if enabled, false otherwise
52
*/
53
boolean isEnabledForLevel(Level level);
54
```
55
56
### LoggingEventBuilder Interface
57
58
Main interface for building structured log events with method chaining.
59
60
```java { .api }
61
/**
62
* Main interface for creating logging events with fluent API
63
*/
64
public interface LoggingEventBuilder {
65
/**
66
* Set the cause for the logging event being built
67
* @param cause a throwable
68
* @return a LoggingEventBuilder, usually this
69
*/
70
LoggingEventBuilder setCause(Throwable cause);
71
72
/**
73
* Add a Marker to the event being built
74
* @param marker a Marker instance to add
75
* @return a LoggingEventBuilder, usually this
76
*/
77
LoggingEventBuilder addMarker(Marker marker);
78
79
/**
80
* Add an argument to the event being built
81
* @param p an Object to add
82
* @return a LoggingEventBuilder, usually this
83
*/
84
LoggingEventBuilder addArgument(Object p);
85
86
/**
87
* Add an argument supplier to the event being built
88
* @param objectSupplier an Object supplier to add
89
* @return a LoggingEventBuilder, usually this
90
*/
91
LoggingEventBuilder addArgument(Supplier<?> objectSupplier);
92
93
/**
94
* Add a key value pair to the event being built
95
* @param key the key of the key value pair
96
* @param value the value of the key value pair
97
* @return a LoggingEventBuilder, usually this
98
*/
99
LoggingEventBuilder addKeyValue(String key, Object value);
100
101
/**
102
* Add a key value pair to the event being built
103
* @param key the key of the key value pair
104
* @param valueSupplier a supplier of a value for the key value pair
105
* @return a LoggingEventBuilder, usually this
106
*/
107
LoggingEventBuilder addKeyValue(String key, Supplier<Object> valueSupplier);
108
109
/**
110
* Sets the message of the logging event
111
* @param message the message string
112
* @return a LoggingEventBuilder, usually this
113
*/
114
LoggingEventBuilder setMessage(String message);
115
116
/**
117
* Sets the message of the event via a message supplier
118
* @param messageSupplier supplies a String to be used as the message
119
* @return a LoggingEventBuilder, usually this
120
*/
121
LoggingEventBuilder setMessage(Supplier<String> messageSupplier);
122
123
/**
124
* After the logging event is built, performs actual logging
125
*/
126
void log();
127
128
/**
129
* Equivalent to calling setMessage(String) followed by log()
130
* @param message the message to log
131
*/
132
void log(String message);
133
134
/**
135
* Equivalent to calling setMessage(String) followed by addArgument(Object) and then log()
136
* @param message the message to log
137
* @param arg an argument to be used with the message to log
138
*/
139
void log(String message, Object arg);
140
141
/**
142
* Equivalent to calling setMessage(String) followed by two calls to addArgument(Object) and then log()
143
* @param message the message to log
144
* @param arg0 first argument to be used with the message to log
145
* @param arg1 second argument to be used with the message to log
146
*/
147
void log(String message, Object arg0, Object arg1);
148
149
/**
150
* Equivalent to calling setMessage(String) followed by zero or more calls to addArgument(Object) and then log()
151
* @param message the message to log
152
* @param args a list (actually an array) of arguments to be used with the message to log
153
*/
154
void log(String message, Object... args);
155
156
/**
157
* Equivalent to calling setMessage(Supplier) followed by log()
158
* @param messageSupplier a Supplier returning a message of type String
159
*/
160
void log(Supplier<String> messageSupplier);
161
}
162
```
163
164
### Level Enumeration
165
166
Enumeration representing logging levels.
167
168
```java { .api }
169
/**
170
* SLF4J's internal representation of Level
171
*/
172
public enum Level {
173
ERROR, WARN, INFO, DEBUG, TRACE;
174
175
/**
176
* Get integer representation of the level
177
* @return the integer value
178
*/
179
int toInt();
180
181
/**
182
* Convert integer to Level
183
* @param levelInt the integer representation
184
* @return the corresponding Level
185
*/
186
static Level intToLevel(int levelInt);
187
}
188
```
189
190
**Usage Examples:**
191
192
```java
193
import org.slf4j.Logger;
194
import org.slf4j.LoggerFactory;
195
import org.slf4j.event.Level;
196
import java.util.function.Supplier;
197
198
public class FluentLoggingExample {
199
private static final Logger logger = LoggerFactory.getLogger(FluentLoggingExample.class);
200
201
public void basicFluentLogging() {
202
// Simple fluent logging
203
logger.atInfo().log("Application started");
204
205
// With arguments
206
logger.atDebug()
207
.addArgument("user123")
208
.addArgument(42)
209
.log("User {} has {} active sessions");
210
211
// With key-value pairs for structured logging
212
logger.atInfo()
213
.addKeyValue("userId", "user123")
214
.addKeyValue("action", "login")
215
.addKeyValue("timestamp", System.currentTimeMillis())
216
.log("User login event");
217
}
218
219
public void advancedFluentLogging() {
220
// Complex event building
221
try {
222
processOrder();
223
} catch (Exception e) {
224
logger.atError()
225
.addKeyValue("orderId", "ORD-12345")
226
.addKeyValue("customerId", "CUST-67890")
227
.addKeyValue("errorType", e.getClass().getSimpleName())
228
.setCause(e)
229
.log("Failed to process order");
230
}
231
232
// Conditional logging with level checking
233
if (logger.isEnabledForLevel(Level.TRACE)) {
234
logger.atTrace()
235
.setMessage(() -> buildExpensiveDebugMessage())
236
.log();
237
}
238
239
// Using suppliers for lazy evaluation
240
logger.atDebug()
241
.addArgument(() -> getCurrentUser().getName())
242
.addKeyValue("memoryUsage", () -> getMemoryUsage())
243
.log("Processing request for user {}");
244
}
245
246
public void markerBasedFluentLogging() {
247
Marker securityMarker = MarkerFactory.getMarker("SECURITY");
248
Marker auditMarker = MarkerFactory.getMarker("AUDIT");
249
250
// Multiple markers with fluent API
251
logger.atWarn()
252
.addMarker(securityMarker)
253
.addMarker(auditMarker)
254
.addKeyValue("userId", "user123")
255
.addKeyValue("attemptedAction", "admin_access")
256
.log("Unauthorized access attempt");
257
}
258
259
private String buildExpensiveDebugMessage() {
260
// Expensive operation only executed if TRACE is enabled
261
return "Complex debug info: " + performExpensiveCalculation();
262
}
263
264
private long getMemoryUsage() {
265
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
266
}
267
}
268
```
269
270
### Structured Logging with Key-Value Pairs
271
272
The fluent API enables structured logging through key-value pairs:
273
274
```java
275
// Traditional logging - unstructured
276
logger.info("User john.doe logged in from IP 192.168.1.100 at 2023-12-01T10:30:00Z");
277
278
// Fluent API - structured logging
279
logger.atInfo()
280
.addKeyValue("event", "user_login")
281
.addKeyValue("username", "john.doe")
282
.addKeyValue("sourceIp", "192.168.1.100")
283
.addKeyValue("timestamp", "2023-12-01T10:30:00Z")
284
.log("User login event");
285
```
286
287
### Lazy Evaluation
288
289
The fluent API supports lazy evaluation using Supplier interfaces:
290
291
```java
292
// Expensive operation only executed if logging level is enabled
293
logger.atTrace()
294
.addArgument(() -> performExpensiveDatabaseQuery())
295
.addKeyValue("metrics", () -> calculateComplexMetrics())
296
.setMessage(() -> generateDetailedReport())
297
.log();
298
299
// Conditional logging is handled automatically
300
logger.atDebug()
301
.addArgument(() -> {
302
// This lambda is only executed if DEBUG level is enabled
303
return performExpensiveFormatting(data);
304
})
305
.log("Processed data: {}");
306
```
307
308
### Performance Benefits
309
310
The fluent API provides several performance advantages:
311
312
1. **Level Checking**: If the logging level is disabled, a no-operation LoggingEventBuilder is returned, avoiding any parameter evaluation
313
2. **Lazy Evaluation**: Suppliers are only invoked if logging will actually occur
314
3. **Efficient Builder**: The builder pattern minimizes object creation compared to traditional string concatenation
315
4. **Structured Data**: Key-value pairs enable efficient processing by logging backends without string parsing
316
317
### Migration from Traditional API
318
319
The fluent API can be gradually adopted alongside traditional logging:
320
321
```java
322
// Traditional approach
323
if (logger.isDebugEnabled()) {
324
logger.debug("Processing order {} for customer {} with total {}",
325
order.getId(), customer.getName(), order.getTotal());
326
}
327
328
// Fluent approach - level checking is automatic
329
logger.atDebug()
330
.addArgument(order.getId())
331
.addArgument(customer.getName())
332
.addArgument(order.getTotal())
333
.log("Processing order {} for customer {} with total {}");
334
335
// Or more structured
336
logger.atDebug()
337
.addKeyValue("orderId", order.getId())
338
.addKeyValue("customerId", customer.getId())
339
.addKeyValue("customerName", customer.getName())
340
.addKeyValue("orderTotal", order.getTotal())
341
.log("Processing order");
342
```
343
344
## Types
345
346
### Key-Value Pair Support
347
348
```java { .api }
349
/**
350
* Key-value pair for structured logging
351
*/
352
public class KeyValuePair {
353
public final String key;
354
public final Object value;
355
356
/**
357
* Create a key-value pair
358
* @param key the key
359
* @param value the value
360
*/
361
public KeyValuePair(String key, Object value);
362
}
363
```
364
365
### Event Constants
366
367
```java { .api }
368
/**
369
* Integer constants for log levels
370
*/
371
public class EventConstants {
372
public static final int ERROR_INT = 40;
373
public static final int WARN_INT = 30;
374
public static final int INFO_INT = 20;
375
public static final int DEBUG_INT = 10;
376
public static final int TRACE_INT = 0;
377
}
378
```