CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-slf4j--slf4j-api

Simple Logging Facade for Java (SLF4J) API - a facade/abstraction layer for various logging frameworks.

Overview
Eval results
Files

service-providers.mddocs/

Service Provider Interface

SLF4J uses a service provider architecture that allows different logging framework implementations to be plugged in at runtime. The Service Provider Interface (SPI) defines the contracts that logging implementations must fulfill to integrate with SLF4J.

Capabilities

SLF4J Service Provider

Main service provider interface for pluggable logging implementations.

/**
 * This interface based on ServiceLoader paradigm. It replaces the old static-binding mechanism used in SLF4J versions 1.0.x to 1.7.x.
 */
public interface SLF4JServiceProvider {
    /**
     * Return the instance of ILoggerFactory that LoggerFactory class should bind to
     * @return instance of ILoggerFactory
     */
    ILoggerFactory getLoggerFactory();
    
    /**
     * Return the instance of IMarkerFactory that MarkerFactory class should bind to
     * @return instance of IMarkerFactory
     */
    IMarkerFactory getMarkerFactory();
    
    /**
     * Return the instance of MDCAdapter that MDC should bind to
     * @return instance of MDCAdapter
     */
    MDCAdapter getMDCAdapter();
    
    /**
     * Return the maximum API version for SLF4J that the logging implementation supports
     * @return the string API version, for example "2.0.1"
     */
    String getRequestedApiVersion();
    
    /**
     * Initialize the logging back-end
     * WARNING: This method is intended to be called once by LoggerFactory class and from nowhere else
     */
    void initialize();
}

Logger Factory Interface

Factory interface for creating Logger instances.

/**
 * ILoggerFactory instances manufacture Logger instances by name
 */
public interface ILoggerFactory {
    /**
     * Return an appropriate Logger instance as specified by the name parameter
     * @param name the name of the Logger to return
     * @return a Logger instance
     */
    Logger getLogger(String name);
}

Marker Factory Interface

Factory interface for creating and managing Marker instances.

/**
 * Implementations of this interface are used to manufacture Marker instances
 */
public interface IMarkerFactory {
    /**
     * Manufacture a Marker instance by name. If the instance has been created earlier, return the previously created instance
     * @param name the name of the marker to be created, null value is not allowed
     * @return a Marker instance
     */
    Marker getMarker(String name);
    
    /**
     * Checks if the marker with the name already exists
     * @param name marker name
     * @return true if the marker exists, false otherwise
     */
    boolean exists(String name);
    
    /**
     * Detach an existing marker
     * @param name The name of the marker to detach
     * @return whether the marker could be detached or not
     */
    boolean detachMarker(String name);
    
    /**
     * Create a marker which is detached from this IMarkerFactory
     * @param name marker name
     * @return a marker detached from this factory
     */
    Marker getDetachedMarker(String name);
}

Service Provider Discovery

How SLF4J discovers and loads service providers.

/**
 * LoggerFactory methods for service provider management
 */
public final class LoggerFactory {
    /**
     * Explicitly set the provider class via system property
     */
    public static final String PROVIDER_PROPERTY_KEY = "slf4j.provider";
    
    /**
     * Return the SLF4JServiceProvider in use
     * @return provider in use
     */
    static SLF4JServiceProvider getProvider();
    
    /**
     * Return the ILoggerFactory instance in use
     * @return the ILoggerFactory instance in use
     */
    public static ILoggerFactory getILoggerFactory();
}

Usage Examples:

// Implementing a custom service provider
public class MyLoggingServiceProvider implements SLF4JServiceProvider {
    private ILoggerFactory loggerFactory;
    private IMarkerFactory markerFactory;
    private MDCAdapter mdcAdapter;
    
    public MyLoggingServiceProvider() {
        // Initialize factories during construction
        this.loggerFactory = new MyLoggerFactory();
        this.markerFactory = new MyMarkerFactory();
        this.mdcAdapter = new MyMDCAdapter();
    }
    
    @Override
    public ILoggerFactory getLoggerFactory() {
        return loggerFactory;
    }
    
    @Override
    public IMarkerFactory getMarkerFactory() {
        return markerFactory;
    }
    
    @Override
    public MDCAdapter getMDCAdapter() {
        return mdcAdapter;
    }
    
    @Override
    public String getRequestedApiVersion() {
        return "2.0.17";
    }
    
    @Override
    public void initialize() {
        // Perform any necessary initialization
        System.out.println("MyLoggingServiceProvider initialized");
    }
}

// Custom logger factory implementation
public class MyLoggerFactory implements ILoggerFactory {
    private final Map<String, Logger> loggerMap = new ConcurrentHashMap<>();
    
    @Override
    public Logger getLogger(String name) {
        return loggerMap.computeIfAbsent(name, this::createLogger);
    }
    
    private Logger createLogger(String name) {
        return new MyLogger(name);
    }
}

Location-Aware Logger Interface

Extended logger interface that provides location information.

/**
 * An optional interface helping integration with logging systems capable of extracting location information
 */
public interface LocationAwareLogger extends Logger {
    // Level constants
    public final static int TRACE_INT = 00;
    public final static int DEBUG_INT = 10;  
    public final static int INFO_INT = 20;
    public final static int WARN_INT = 30;
    public final static int ERROR_INT = 40;
    
    /**
     * Printing method which can be used by implementation classes to receive location information
     * @param marker The marker to be used for this event, may be null
     * @param fqcn The fully qualified class name of the logger instance
     * @param level One of the level integers defined in this interface
     * @param message The message for the log event
     * @param argArray An array of arguments to be used in conjunction with the message
     * @param t The throwable associated with the log event, may be null
     */
    public void log(Marker marker, String fqcn, int level, String message, Object[] argArray, Throwable t);
}

Caller Boundary Awareness

Interface for loggers that support caller boundary detection.

/**
 * This interface is used by LoggingEventBuilder implementations to determine the caller boundary
 */
public interface CallerBoundaryAware {
    /**
     * Return the caller boundary for this Logger
     * @return the caller boundary. Null by default.
     */
    default String getCallerBoundary() {
        return null;
    }
}

Logging Event Awareness

Interface for loggers that can handle LoggingEvent objects directly.

/**
 * Logger implementations which are capable of handling LoggingEvents should implement this interface
 */
public interface LoggingEventAware {
    /**
     * Log a LoggingEvent
     * @param event the LoggingEvent to log
     */
    void log(LoggingEvent event);
}

Built-in Implementations

No-Operation Implementations

Default implementations that perform no actual logging.

/**
 * NOPLogger is an implementation of Logger that performs no operation
 */
public class NOPLogger implements Logger {
    public String getName() { return "NOP"; }
    public boolean isTraceEnabled() { return false; }
    public void trace(String msg) { }
    // ... all methods do nothing
}

/**
 * NOPLoggerFactory is a factory for NOPLogger instances
 */
public class NOPLoggerFactory implements ILoggerFactory {
    public Logger getLogger(String name) {
        return NOPLogger.NOP_LOGGER;
    }
}

/**
 * This implementation ignores all MDC operations
 */
public class NOPMDCAdapter implements MDCAdapter {
    public void put(String key, String val) { }
    public String get(String key) { return null; }
    public void remove(String key) { }
    public void clear() { }
    // ... all methods do nothing
}

Basic Implementations

Simple working implementations for basic scenarios.

/**
 * A simple implementation of MDC using InheritableThreadLocal
 */
public class BasicMDCAdapter implements MDCAdapter {
    private InheritableThreadLocal<Map<String, String>> inheritableThreadLocal = 
        new InheritableThreadLocal<Map<String, String>>();
    
    public void put(String key, String val);
    public String get(String key);
    public void remove(String key);
    public void clear();
    public Map<String, String> getCopyOfContextMap();
    public void setContextMap(Map<String, String> contextMap);
    // ... implementation details
}

/**
 * BasicMarker holds references to other markers
 */
public class BasicMarker implements Marker {
    private final String name;
    private final List<Marker> referenceList = new ArrayList<>();
    
    BasicMarker(String name);
    public String getName();
    public void add(Marker reference);
    public boolean remove(Marker reference);
    // ... implementation details
}

/**
 * BasicMarkerFactory is a simple implementation of IMarkerFactory
 */
public class BasicMarkerFactory implements IMarkerFactory {
    private final Map<String, Marker> markerMap = new ConcurrentHashMap<>();
    
    public Marker getMarker(String name);
    public boolean exists(String name);
    public Marker getDetachedMarker(String name);
}

Substitute Implementations

Temporary implementations used during SLF4J initialization.

/**
 * A logger implementation which logs via a delegate logger but can also buffer logging events when the delegate is null
 */
public class SubstituteLogger implements Logger {
    private final String name;
    private volatile Logger _delegate;
    private final Queue<SubstituteLoggingEvent> eventQueue = new LinkedBlockingQueue<>();
    
    public SubstituteLogger(String name);
    public String getName();
    void setDelegate(Logger delegate);
    // ... delegates to _delegate when available, queues events otherwise
}

/**
 * SubstituteLoggerFactory creates SubstituteLogger instances
 */
public class SubstituteLoggerFactory implements ILoggerFactory {
    private final Map<String, SubstituteLogger> loggers = new ConcurrentHashMap<>();
    
    public Logger getLogger(String name);
    public List<SubstituteLogger> getLoggers();
    public void postInitialization();
    public void clear();
}

Service Provider Registration

Java ServiceLoader

Register service providers using the standard Java ServiceLoader mechanism:

  1. Create a file named org.slf4j.spi.SLF4JServiceProvider in META-INF/services/
  2. Add the fully qualified class name of your service provider implementation
# META-INF/services/org.slf4j.spi.SLF4JServiceProvider
com.example.logging.MyLoggingServiceProvider

Explicit Provider Configuration

Override automatic discovery using system property:

// Set system property to explicitly specify provider
System.setProperty("slf4j.provider", "com.example.logging.MyLoggingServiceProvider");

// Or via command line
// -Dslf4j.provider=com.example.logging.MyLoggingServiceProvider

Implementation Guidelines

Service Provider Implementation

public class CustomServiceProvider implements SLF4JServiceProvider {
    private static final String REQUESTED_API_VERSION = "2.0.17";
    
    private final ILoggerFactory loggerFactory;
    private final IMarkerFactory markerFactory;
    private final MDCAdapter mdcAdapter;
    
    public CustomServiceProvider() {
        // Initialize all components in constructor
        this.loggerFactory = new CustomLoggerFactory();
        this.markerFactory = new CustomMarkerFactory();
        this.mdcAdapter = new CustomMDCAdapter();
    }
    
    @Override
    public ILoggerFactory getLoggerFactory() {
        return loggerFactory;
    }
    
    @Override
    public IMarkerFactory getMarkerFactory() {
        return markerFactory;
    }
    
    @Override
    public MDCAdapter getMDCAdapter() {
        return mdcAdapter;
    }
    
    @Override
    public String getRequestedApiVersion() {
        return REQUESTED_API_VERSION;
    }
    
    @Override
    public void initialize() {
        // Perform initialization tasks
        // This method is called once by LoggerFactory
        configureLogging();
        validateConfiguration();
    }
    
    private void configureLogging() {
        // Configure your logging backend
    }
    
    private void validateConfiguration() {
        // Validate configuration and throw exceptions if invalid
    }
}

Logger Implementation

public class CustomLogger implements Logger, LocationAwareLogger, LoggingEventAware {
    private final String name;
    private final CustomLoggingBackend backend;
    
    public CustomLogger(String name, CustomLoggingBackend backend) {
        this.name = name;
        this.backend = backend;
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public boolean isDebugEnabled() {
        return backend.isLevelEnabled(name, Level.DEBUG);
    }
    
    @Override
    public void debug(String msg) {
        if (isDebugEnabled()) {
            backend.log(name, Level.DEBUG, msg, null, null);
        }
    }
    
    @Override
    public void debug(String format, Object arg) {
        if (isDebugEnabled()) {
            FormattingTuple ft = MessageFormatter.format(format, arg);
            backend.log(name, Level.DEBUG, ft.getMessage(), null, ft.getThrowable());
        }
    }
    
    // LocationAwareLogger implementation
    @Override
    public void log(Marker marker, String fqcn, int level, String message, Object[] argArray, Throwable t) {
        if (backend.isLevelEnabled(name, Level.intToLevel(level))) {
            backend.log(name, marker, fqcn, Level.intToLevel(level), message, argArray, t);
        }
    }
    
    // LoggingEventAware implementation
    @Override
    public void log(LoggingEvent event) {
        if (backend.isLevelEnabled(name, event.getLevel())) {
            backend.log(event);
        }
    }
    
    // ... implement all other Logger methods
}

MDC Adapter Implementation

public class CustomMDCAdapter implements MDCAdapter {
    private final ThreadLocal<Map<String, String>> threadLocalMap = new ThreadLocal<>();
    private final ThreadLocal<Map<String, Deque<String>>> threadLocalDequeMap = new ThreadLocal<>();
    
    @Override
    public void put(String key, String val) {
        if (key == null) {
            throw new IllegalArgumentException("key cannot be null");
        }
        
        Map<String, String> map = threadLocalMap.get();
        if (map == null) {
            map = new HashMap<>();
            threadLocalMap.set(map);
        }
        map.put(key, val);
    }
    
    @Override
    public String get(String key) {
        Map<String, String> map = threadLocalMap.get();
        return (map != null) ? map.get(key) : null;
    }
    
    @Override
    public void remove(String key) {
        Map<String, String> map = threadLocalMap.get();
        if (map != null) {
            map.remove(key);
            if (map.isEmpty()) {
                threadLocalMap.remove();
            }
        }
    }
    
    @Override
    public void clear() {
        threadLocalMap.remove();
        threadLocalDequeMap.remove();
    }
    
    @Override
    public void pushByKey(String key, String value) {
        Map<String, Deque<String>> dequeMap = threadLocalDequeMap.get();
        if (dequeMap == null) {
            dequeMap = new HashMap<>();
            threadLocalDequeMap.set(dequeMap);
        }
        
        Deque<String> deque = dequeMap.computeIfAbsent(key, k -> new ArrayDeque<>());
        deque.push(value);
    }
    
    @Override
    public String popByKey(String key) {
        Map<String, Deque<String>> dequeMap = threadLocalDequeMap.get();
        if (dequeMap != null) {
            Deque<String> deque = dequeMap.get(key);
            if (deque != null && !deque.isEmpty()) {
                return deque.pop();
            }
        }
        return null;
    }
    
    // ... implement remaining methods
}

Version Compatibility

SLF4J service providers should declare their API compatibility:

@Override
public String getRequestedApiVersion() {
    // Declare the SLF4J API version this provider supports
    return "2.0.17";
}

Version compatibility matrix:

  • "2.0": Supports SLF4J 2.0.x features including fluent API
  • "1.8": Basic SLF4J 1.x compatibility
  • "1.7": Legacy SLF4J 1.7.x compatibility

Multiple Provider Handling

When multiple providers are found on the classpath:

  1. SLF4J reports the ambiguity with a warning message
  2. The first provider found is used
  3. All other providers are ignored
  4. Applications can override using the slf4j.provider system property

Migration from SLF4J 1.x Bindings

SLF4J 2.0 replaces the old static binding mechanism:

  • Old: StaticLoggerBinder and StaticMarkerBinder classes
  • New: SLF4JServiceProvider interface with ServiceLoader
  • Compatibility: SLF4J 2.0 detects and warns about old-style bindings
  • Migration: Implement SLF4JServiceProvider and register via ServiceLoader

Install with Tessl CLI

npx tessl i tessl/maven-org-slf4j--slf4j-api

docs

basic-logging.md

fluent-logging.md

index.md

markers.md

mdc.md

service-providers.md

tile.json