Java Logging Ecosystem: A Comparative Guide to JUL, Log4j, Logback, and SLF4J

This guide provides a concise, implementation-focused comparison of Java’s major logging frameworks and abstraction layers—covering java.util.logging (JUL), Log4j 1.x and 2.x, Logback, and the SLF4J API—along with their interoperability patterns.

JDK Built-in Logger (JUL)

Available since Java 1.4, JUL requires no external dependencies. Its configuration is controlled via $JAVA_HOME/jre/lib/logging.properties or overridden using -Djava.util.logging.config.file=....

JUL defines seven severity levels in ascending order:

  • ALL, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, OFF

Only log statements at or above the configured level are emitted. Handlers (e.g., ConsoleHandler) can be attached independently with their own level filters:

import java.util.logging.*;

public class JulDemo {
    private static final Logger logger = Logger.getLogger(JulDemo.class.getName());

    static {
        ConsoleHandler handler1 = new ConsoleHandler();
        handler1.setLevel(Level.SEVERE);
        logger.addHandler(handler1);

        ConsoleHandler handler2 = new ConsoleHandler();
        handler2.setLevel(Level.INFO);
        logger.addHandler(handler2);
    }

    public static void main(String[] args) {
        logger.setLevel(Level.INFO);
        logger.info("This appears twice");     // INFO ≥ INFO → shown on both handlers
        logger.severe("This appears once");     // SEVERE ≥ SEVERE only → shown once
    }
}

Log4j 1.x

A mature, highly configurable framework supporting multiple appenders (console, file, DB, network), custom layouts, and hierarchical loggers.

Add to pom.xml:

<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

Configure via log4j.properties:

log4j.rootLogger=DEBUG, console, debugFile, errorFile

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%p] %d{HH:mm:ss} %c{1} - %m%n

log4j.appender.debugFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.debugFile.File=logs/debug.log
log4j.appender.debugFile.Threshold=DEBUG
log4j.appender.debugFile.layout=org.apache.log4j.PatternLayout
log4j.appender.debugFile.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c - %m%n

log4j.appender.errorFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.errorFile.File=logs/error.log
log4j.appender.errorFile.Threshold=ERROR
log4j.appender.errorFile.layout=org.apache.log4j.PatternLayout
log4j.appender.errorFile.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c - %m%n

Usage:

import org.apache.log4j.Logger;

public class Log4j1Demo {
    private static final Logger log = Logger.getLogger(Log4j1Demo.class);

    public static void main(String[] args) {
        log.debug("Debug message");
        log.info("Info message");
        log.error("Error message");
    }
}

Log4j 2.x

A complete rewrite offering improved performance, asynchronous logging, and enhanced configuration flexibility (XML, JSON, YAML).

Dependencies:

<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>2.20.0</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.20.0</version>
</dependency>

Example usage with parameterized messages:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4j2Demo {
    private static final Logger logger = LogManager.getLogger();

    public static void main(String[] args) {
        logger.info("User {} logged in from {}", "alice", "192.168.1.1");
        logger.error("Failed to process request ID: {}", "req-7b3a");
    }
}

Logback

Designed by the original author of Log4j, Logback offers native SLF4J integration, faster execution, automatic reloadable configuration, and advanced features like conditional configuration and SiftingAppender.

Core modules:

  • logback-core: Foundation for other modules
  • logback-classic: SLF4J binding + enhanced JUL replacement
  • logback-access: HTTP-access logging (for servlet containers)

Key advantages over Log4j 1.x:

  • ~10× faster logging calls and logger instantiation
  • Automatic config reloading without background threads
  • Built-in compression and time/size-based rolling
  • Rich filtering (e.g., MDC-based routing)
  • Full documentation and open licensing

Minimal logback.xml example:

<configuration scan="true" scanPeriod="30 seconds">
  <property name="LOG_DIR" value="logs"/>

  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_DIR}/app.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${LOG_DIR}/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
      <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <maxFileSize>10MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      <maxHistory>7</maxHistory>
    </rollingPolicy>
    <encoder>
      <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="INFO">
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="FILE"/>
  </root>
</configuration>

Code usage (via SLF4J):

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogbackDemo {
    private static final Logger log = LoggerFactory.getLogger(LogbackDemo.class);

    public static void main(String[] args) {
        log.info("Application started");
        log.warn("Deprecated feature used");
        log.error("Unexpected failure", new RuntimeException("Boom"));
    }
}

SLF4J: The Abstraction Layer

SLF4J is not a logging implementation—it's a compile-time binding facade. Applications depand solely on slf4j-api; the concrete back end is selected at deployment time via binding JARs.

Common bindings:

  • logback-classic → native SLF4J support
  • slf4j-log4j12 → bridges to Log4j 1.x
  • slf4j-jdk14 → bridges to JUL
  • jul-to-slf4j → redirects JUL calls to SLF4J
  • log4j-over-slf4j → redirects Log4j 1.x calls to SLF4J

Crucial rule: Only one binding JAR must be present on the classpath. Multiple bindings trigger warnings and undefined behavior.

Sample dependency set for Logback-backed application:

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>2.0.9</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.4.11</version>
</dependency>

Apache Commons Logging (JCL)

An older facade that uses runtime classloader discovery to locate logging implementations. While historically popular, it suffers from OSGi incompatibility and unpredictable resolution order.

Configuration options include:

  • commons-logging.properties with org.apache.commons.logging.Log=...
  • Service loader mechanism (META-INF/services/org.apache.commons.logging.LogFactory)
  • Fallback to Log4j → JUL → SimpleLog

Usage remains straightforward but is largely superseded by SLF4J in modern applications:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class JclDemo {
    private static final Log log = LogFactory.getLog(JclDemo.class);

    public static void main(String[] args) {
        log.info("JCL is active");
    }
}

Choosing the Right Stack

  • New projects: Prefer slf4j-api + logback-classic
  • Legacy systems requiring Log4j 1.x: Use slf4j-api + slf4j-log4j12 + log4j
  • Spring Boot 2+ apps: Leverage built-in spring-boot-starter-logging (defaults to Logback)
  • Minimalist deployments: slf4j-simple (no configuration needed)
  • OSGi environments: Avoid JCL; use SLF4J with appropriate binding

Tags: SLF4J logback Log4j2 jul java-logging

Posted on Mon, 02 Nov 2026 16:37:46 +0000 by learningsql