SLF4J binding implementation that bridges SLF4J API with Java Util Logging (JUL) framework
npx @tessl/cli install tessl/maven-org-slf4j--slf4j-jdk14@2.0.0SLF4J JDK14 Provider is a binding implementation that bridges the SLF4J (Simple Logging Facade for Java) API with Java Util Logging (JUL), the built-in logging framework in the JDK. This lightweight adapter enables SLF4J-based applications to output logs through the standard java.util.logging framework without requiring additional logging dependencies.
Maven Dependency:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>2.0.17</version>
</dependency>import org.slf4j.jul.JULServiceProvider;
import org.slf4j.jul.JDK14LoggerFactory;
import org.slf4j.jul.JDK14LoggerAdapter;The slf4j-jdk14 provider is automatically discovered via Java's ServiceLoader mechanism when present on the classpath. No explicit configuration is required for basic usage:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Example {
private static final Logger logger = LoggerFactory.getLogger(Example.class);
public void demonstrateLogging() {
logger.trace("Trace level message");
logger.debug("Debug level message");
logger.info("Info level message");
logger.warn("Warning level message");
logger.error("Error level message", new RuntimeException("Example error"));
}
}The provider consists of three main components:
Level Mapping: SLF4J levels are mapped to JUL levels:
The main service provider that enables automatic SLF4J integration.
public class JULServiceProvider implements SLF4JServiceProvider {
// API version this implementation is compiled against
public static String REQUESTED_API_VERSION = "2.0.99";
public JULServiceProvider();
public ILoggerFactory getLoggerFactory();
public IMarkerFactory getMarkerFactory();
public MDCAdapter getMDCAdapter();
public String getRequestedApiVersion();
public void initialize();
}Usage: This class is automatically instantiated by SLF4J's ServiceLoader mechanism. Manual instantiation is not typically needed.
Factory implementation that creates and manages logger instances.
public class JDK14LoggerFactory implements ILoggerFactory {
public JDK14LoggerFactory();
public Logger getLogger(String name);
}Usage:
JDK14LoggerFactory factory = new JDK14LoggerFactory();
Logger logger = factory.getLogger("com.example.MyClass");Logger adapter that bridges SLF4J Logger interface to java.util.logging.Logger.
public final class JDK14LoggerAdapter extends LegacyAbstractLogger
implements LocationAwareLogger {
// Constants for stack trace analysis
static final int MAX_SEARCH_DEPTH = 12;
static String SELF = JDK14LoggerAdapter.class.getName();
static String[] BARRIER_CLASSES;
// Constructor
JDK14LoggerAdapter(java.util.logging.Logger logger);
// Level check methods
public boolean isTraceEnabled();
public boolean isDebugEnabled();
public boolean isInfoEnabled();
public boolean isWarnEnabled();
public boolean isErrorEnabled();
// Logging methods (inherited from parent classes)
// All standard SLF4J Logger interface methods available
// LocationAwareLogger method
public void log(Marker marker, String callerFQCN, int slf4jLevelInt,
String message, Object[] arguments, Throwable throwable);
// LoggingEvent support
public void log(LoggingEvent event);
// Level conversion utilities
private static Level slf4jLevelToJULLevel(org.slf4j.event.Level slf4jLevel);
private static Level slf4jLevelIntToJULLevel(int levelInt);
// Event processing
private LogRecord eventToRecord(LoggingEvent event, Level julLevel);
}Usage: Logger instances are typically obtained through SLF4J's LoggerFactory, not directly instantiated:
Logger logger = LoggerFactory.getLogger(MyClass.class);
// Level checks
if (logger.isDebugEnabled()) {
logger.debug("Debug message with parameter: {}", value);
}
// Direct logging
logger.info("Application started");
logger.warn("Configuration missing, using defaults");
logger.error("Failed to process request", exception);
// LoggingEvent support (typically used by SLF4J internally)
LoggingEvent event = new LoggingEvent(Level.INFO, logger);
event.setMessage("Event-based logging");
logger.log(event);All logger adapters provide level checking methods for performance optimization.
// Check if specific logging levels are enabled
boolean isTraceEnabled();
boolean isDebugEnabled();
boolean isInfoEnabled();
boolean isWarnEnabled();
boolean isErrorEnabled();Usage:
// Avoid expensive string operations when level is disabled
if (logger.isDebugEnabled()) {
logger.debug("Processing item: {}", expensiveToString(item));
}The provider includes utility methods for converting SLF4J levels to JUL levels.
// Internal level conversion methods
private static Level slf4jLevelToJULLevel(org.slf4j.event.Level slf4jLevel);
private static Level slf4jLevelIntToJULLevel(int levelInt);Level Mapping:
TRACE → Level.FINESTDEBUG → Level.FINEINFO → Level.INFOWARN → Level.WARNINGERROR → Level.SEVEREThe adapter preserves caller information through stack trace analysis with configurable depth limits.
// Stack trace analysis constants
static final int MAX_SEARCH_DEPTH = 12;
static String SELF = "org.slf4j.jul.JDK14LoggerAdapter";
static String[] BARRIER_CLASSES; // Contains known SLF4J framework classesCaller Detection: The adapter analyzes up to 12 stack frames to identify the actual calling class and method, filtering out known SLF4J framework classes to provide accurate source location information in log records.
The adapter supports SLF4J's LoggingEvent API for structured logging scenarios.
// LoggingEvent processing
public void log(LoggingEvent event);
private LogRecord eventToRecord(LoggingEvent event, Level julLevel);LoggingEvent Usage:
// Create and configure a logging event
LoggingEvent event = new LoggingEvent();
event.setLevel(Level.INFO);
event.setLoggerName("com.example.MyClass");
event.setMessage("Processing completed for user: {}");
event.setArgumentArray(new Object[]{"john.doe"});
event.setTimeStamp(System.currentTimeMillis());
// Log through the adapter
logger.log(event);Event to Record Conversion:
The eventToRecord method converts SLF4J LoggingEvent objects to JUL LogRecord objects, handling:
The provider is automatically discovered through Java's ServiceLoader mechanism via the configuration file:
File Structure:
src/main/resources/
└── META-INF/
└── services/
└── org.slf4j.spi.SLF4JServiceProviderFile Content: org.slf4j.jul.JULServiceProvider
This configuration enables automatic discovery of the JUL provider when slf4j-jdk14 is present on the classpath.
For Java 9+ module system:
module org.slf4j.jul {
requires org.slf4j;
requires java.logging;
exports org.slf4j.jul;
opens org.slf4j.jul to org.slf4j;
provides org.slf4j.spi.SLF4JServiceProvider
with org.slf4j.jul.JULServiceProvider;
}These types are provided by the SLF4J API dependency:
// From org.slf4j.spi
interface SLF4JServiceProvider {
ILoggerFactory getLoggerFactory();
IMarkerFactory getMarkerFactory();
MDCAdapter getMDCAdapter();
String getRequestedApiVersion();
void initialize();
}
interface ILoggerFactory {
Logger getLogger(String name);
}
interface LocationAwareLogger extends Logger {
void log(Marker marker, String fqcn, int level, String message,
Object[] argArray, Throwable t);
}
// From org.slf4j
interface Logger {
// Standard logging methods
void trace(String msg);
void debug(String msg);
void info(String msg);
void warn(String msg);
void error(String msg);
// Parameterized logging methods
void trace(String format, Object... arguments);
void debug(String format, Object... arguments);
void info(String format, Object... arguments);
void warn(String format, Object... arguments);
void error(String format, Object... arguments);
// Exception logging methods
void trace(String msg, Throwable t);
void debug(String msg, Throwable t);
void info(String msg, Throwable t);
void warn(String msg, Throwable t);
void error(String msg, Throwable t);
// Level check methods
boolean isTraceEnabled();
boolean isDebugEnabled();
boolean isInfoEnabled();
boolean isWarnEnabled();
boolean isErrorEnabled();
}
interface Marker {
String getName();
void add(Marker reference);
boolean remove(Marker reference);
boolean contains(Marker other);
boolean contains(String name);
}
interface IMarkerFactory {
Marker getMarker(String name);
boolean exists(String name);
boolean detachMarker(String name);
}
interface MDCAdapter {
void put(String key, String val);
String get(String key);
void remove(String key);
void clear();
}
class LoggingEvent {
Level getLevel();
String getLoggerName();
String getMessage();
Object[] getArgumentArray();
Throwable getThrowable();
long getTimeStamp();
}Key JUL types used by the implementation:
// From java.util.logging
class Logger {
static Logger getLogger(String name);
boolean isLoggable(Level level);
void log(LogRecord record);
String getName();
}
class LogRecord {
LogRecord(Level level, String msg);
void setLoggerName(String name);
void setThrown(Throwable ex);
void setSourceClassName(String sourceClassName);
void setSourceMethodName(String sourceMethodName);
void setMillis(long millis);
}
class Level {
static final Level FINEST;
static final Level FINE;
static final Level INFO;
static final Level WARNING;
static final Level SEVERE;
}The provider handles several error scenarios:
slf4jLevelToJULLevel receives an unknown SLF4J level enum valuejava.util.logging.Logger.getLogger("")MAX_SEARCH_DEPTH (12) stack frames to prevent excessive performance overheadeventToRecord, throws IllegalArgumentException if both event throwable and formatted message throwable are presentSince this provider delegates to java.util.logging, configure JUL using standard mechanisms:
logging.properties:
# Root logger level
.level = INFO
# Console handler
handlers = java.util.logging.ConsoleHandler
# Console handler level and format
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# Package-specific levels
com.example.level = DEBUGProgrammatic Configuration:
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.logging.ConsoleHandler;
Logger julLogger = Logger.getLogger("com.example");
julLogger.setLevel(Level.FINE);
julLogger.addHandler(new ConsoleHandler());