Simple Logging Facade for Java (SLF4J) API - a facade/abstraction layer for various logging frameworks.
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.
Logger methods that return LoggingEventBuilder instances for chainable operations.
/**
* Entry point for fluent-logging for TRACE level
* @return LoggingEventBuilder instance as appropriate for level TRACE
*/
LoggingEventBuilder atTrace();
/**
* Entry point for fluent-logging for DEBUG level
* @return LoggingEventBuilder instance as appropriate for level DEBUG
*/
LoggingEventBuilder atDebug();
/**
* Entry point for fluent-logging for INFO level
* @return LoggingEventBuilder instance as appropriate for level INFO
*/
LoggingEventBuilder atInfo();
/**
* Entry point for fluent-logging for WARN level
* @return LoggingEventBuilder instance as appropriate for level WARN
*/
LoggingEventBuilder atWarn();
/**
* Entry point for fluent-logging for ERROR level
* @return LoggingEventBuilder instance as appropriate for level ERROR
*/
LoggingEventBuilder atError();
/**
* Entry point for fluent-logging for any level
* @param level desired level for the event builder
* @return LoggingEventBuilder instance as appropriate for the specified level
*/
LoggingEventBuilder atLevel(Level level);
/**
* Check if logger is enabled for the specified level
* @param level the level to check
* @return true if enabled, false otherwise
*/
boolean isEnabledForLevel(Level level);Main interface for building structured log events with method chaining.
/**
* Main interface for creating logging events with fluent API
*/
public interface LoggingEventBuilder {
/**
* Set the cause for the logging event being built
* @param cause a throwable
* @return a LoggingEventBuilder, usually this
*/
LoggingEventBuilder setCause(Throwable cause);
/**
* Add a Marker to the event being built
* @param marker a Marker instance to add
* @return a LoggingEventBuilder, usually this
*/
LoggingEventBuilder addMarker(Marker marker);
/**
* Add an argument to the event being built
* @param p an Object to add
* @return a LoggingEventBuilder, usually this
*/
LoggingEventBuilder addArgument(Object p);
/**
* Add an argument supplier to the event being built
* @param objectSupplier an Object supplier to add
* @return a LoggingEventBuilder, usually this
*/
LoggingEventBuilder addArgument(Supplier<?> objectSupplier);
/**
* Add a key value pair to the event being built
* @param key the key of the key value pair
* @param value the value of the key value pair
* @return a LoggingEventBuilder, usually this
*/
LoggingEventBuilder addKeyValue(String key, Object value);
/**
* Add a key value pair to the event being built
* @param key the key of the key value pair
* @param valueSupplier a supplier of a value for the key value pair
* @return a LoggingEventBuilder, usually this
*/
LoggingEventBuilder addKeyValue(String key, Supplier<Object> valueSupplier);
/**
* Sets the message of the logging event
* @param message the message string
* @return a LoggingEventBuilder, usually this
*/
LoggingEventBuilder setMessage(String message);
/**
* Sets the message of the event via a message supplier
* @param messageSupplier supplies a String to be used as the message
* @return a LoggingEventBuilder, usually this
*/
LoggingEventBuilder setMessage(Supplier<String> messageSupplier);
/**
* After the logging event is built, performs actual logging
*/
void log();
/**
* Equivalent to calling setMessage(String) followed by log()
* @param message the message to log
*/
void log(String message);
/**
* Equivalent to calling setMessage(String) followed by addArgument(Object) and then log()
* @param message the message to log
* @param arg an argument to be used with the message to log
*/
void log(String message, Object arg);
/**
* Equivalent to calling setMessage(String) followed by two calls to addArgument(Object) and then log()
* @param message the message to log
* @param arg0 first argument to be used with the message to log
* @param arg1 second argument to be used with the message to log
*/
void log(String message, Object arg0, Object arg1);
/**
* Equivalent to calling setMessage(String) followed by zero or more calls to addArgument(Object) and then log()
* @param message the message to log
* @param args a list (actually an array) of arguments to be used with the message to log
*/
void log(String message, Object... args);
/**
* Equivalent to calling setMessage(Supplier) followed by log()
* @param messageSupplier a Supplier returning a message of type String
*/
void log(Supplier<String> messageSupplier);
}Enumeration representing logging levels.
/**
* SLF4J's internal representation of Level
*/
public enum Level {
ERROR, WARN, INFO, DEBUG, TRACE;
/**
* Get integer representation of the level
* @return the integer value
*/
int toInt();
/**
* Convert integer to Level
* @param levelInt the integer representation
* @return the corresponding Level
*/
static Level intToLevel(int levelInt);
}Usage Examples:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
import java.util.function.Supplier;
public class FluentLoggingExample {
private static final Logger logger = LoggerFactory.getLogger(FluentLoggingExample.class);
public void basicFluentLogging() {
// Simple fluent logging
logger.atInfo().log("Application started");
// With arguments
logger.atDebug()
.addArgument("user123")
.addArgument(42)
.log("User {} has {} active sessions");
// With key-value pairs for structured logging
logger.atInfo()
.addKeyValue("userId", "user123")
.addKeyValue("action", "login")
.addKeyValue("timestamp", System.currentTimeMillis())
.log("User login event");
}
public void advancedFluentLogging() {
// Complex event building
try {
processOrder();
} catch (Exception e) {
logger.atError()
.addKeyValue("orderId", "ORD-12345")
.addKeyValue("customerId", "CUST-67890")
.addKeyValue("errorType", e.getClass().getSimpleName())
.setCause(e)
.log("Failed to process order");
}
// Conditional logging with level checking
if (logger.isEnabledForLevel(Level.TRACE)) {
logger.atTrace()
.setMessage(() -> buildExpensiveDebugMessage())
.log();
}
// Using suppliers for lazy evaluation
logger.atDebug()
.addArgument(() -> getCurrentUser().getName())
.addKeyValue("memoryUsage", () -> getMemoryUsage())
.log("Processing request for user {}");
}
public void markerBasedFluentLogging() {
Marker securityMarker = MarkerFactory.getMarker("SECURITY");
Marker auditMarker = MarkerFactory.getMarker("AUDIT");
// Multiple markers with fluent API
logger.atWarn()
.addMarker(securityMarker)
.addMarker(auditMarker)
.addKeyValue("userId", "user123")
.addKeyValue("attemptedAction", "admin_access")
.log("Unauthorized access attempt");
}
private String buildExpensiveDebugMessage() {
// Expensive operation only executed if TRACE is enabled
return "Complex debug info: " + performExpensiveCalculation();
}
private long getMemoryUsage() {
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}
}The fluent API enables structured logging through key-value pairs:
// Traditional logging - unstructured
logger.info("User john.doe logged in from IP 192.168.1.100 at 2023-12-01T10:30:00Z");
// Fluent API - structured logging
logger.atInfo()
.addKeyValue("event", "user_login")
.addKeyValue("username", "john.doe")
.addKeyValue("sourceIp", "192.168.1.100")
.addKeyValue("timestamp", "2023-12-01T10:30:00Z")
.log("User login event");The fluent API supports lazy evaluation using Supplier interfaces:
// Expensive operation only executed if logging level is enabled
logger.atTrace()
.addArgument(() -> performExpensiveDatabaseQuery())
.addKeyValue("metrics", () -> calculateComplexMetrics())
.setMessage(() -> generateDetailedReport())
.log();
// Conditional logging is handled automatically
logger.atDebug()
.addArgument(() -> {
// This lambda is only executed if DEBUG level is enabled
return performExpensiveFormatting(data);
})
.log("Processed data: {}");The fluent API provides several performance advantages:
The fluent API can be gradually adopted alongside traditional logging:
// Traditional approach
if (logger.isDebugEnabled()) {
logger.debug("Processing order {} for customer {} with total {}",
order.getId(), customer.getName(), order.getTotal());
}
// Fluent approach - level checking is automatic
logger.atDebug()
.addArgument(order.getId())
.addArgument(customer.getName())
.addArgument(order.getTotal())
.log("Processing order {} for customer {} with total {}");
// Or more structured
logger.atDebug()
.addKeyValue("orderId", order.getId())
.addKeyValue("customerId", customer.getId())
.addKeyValue("customerName", customer.getName())
.addKeyValue("orderTotal", order.getTotal())
.log("Processing order");/**
* Key-value pair for structured logging
*/
public class KeyValuePair {
public final String key;
public final Object value;
/**
* Create a key-value pair
* @param key the key
* @param value the value
*/
public KeyValuePair(String key, Object value);
}/**
* Integer constants for log levels
*/
public class EventConstants {
public static final int ERROR_INT = 40;
public static final int WARN_INT = 30;
public static final int INFO_INT = 20;
public static final int DEBUG_INT = 10;
public static final int TRACE_INT = 0;
}Install with Tessl CLI
npx tessl i tessl/maven-org-slf4j--slf4j-api