Backend Development 15 min read

Performance Comparison and Usage Guide for Logback vs Log4j2 in Java Applications

This article compares the performance of Logback and Log4j2, presents benchmark results under various thread counts, explains their relationship with SLF4J, and provides step‑by‑step configuration and usage instructions for both frameworks in Spring Boot projects, including best practices and sample code.

Architect's Guide
Architect's Guide
Architect's Guide
Performance Comparison and Usage Guide for Logback vs Log4j2 in Java Applications

1. Introduction

Logback and Log4j2 are widely used Java logging frameworks. In production environments the performance of the logging subsystem can affect overall system cost, especially for large companies that run massive workloads. Choosing the right framework and configuring it properly is therefore important.

Relationship between SLF4J, Log4j and Logback

SLF4J is a logging façade that defines a set of logging APIs. Logback and Log4j2 are concrete implementations of those APIs. In other words, SLF4J provides the abstraction, while Logback and Log4j2 provide the actual logging functionality.

Obtaining a Logger

Method 1 – Lombok (recommended)

@Slf4j
public class Main {}

Method 2 – Direct usage

Use org.slf4j.LoggerFactory.getLogger(...) to obtain a logger instance, typically declared as:

private static final Logger LOG = LoggerFactory.getLogger(Main.class);

2. Performance Test Comparison

Benchmark Results

The tests show two clear conclusions:

Log4j2 outperforms Logback by roughly a factor of two.

Increasing the number of logging threads improves throughput up to about twice the number of CPU cores, after which the gain plateaus.

Tips

Including method names and line numbers in log messages significantly reduces throughput. Removing line numbers can double Log4j2 performance in single‑threaded scenarios.

Test Environment

Hardware : AMD Ryzen 5 3600 (6‑core, 3.95 GHz), 32 GB DDR4 2666 MHz.

JVM : Semeru JDK 11.0.20, -Xms1000m -Xmx1000m .

Library Versions : Log4j 2.22.1, Logback 1.4.14.

Thread counts : 1, 8, 32, 128.

Test method : Warm‑up phase, then three runs; the average of the formal runs is recorded.

Log pattern (example):

<!-- Log4j2 pattern -->
<Property name="log.pattern">[%d{yyyyMMdd HH:mm:ss.SSS}] [%t] [%level{length=4}] %c{1.}:%L %msg%n</Property>

<!-- Logback pattern -->
<pattern>[%date{yyyyMMdd HH:mm:ss.SSS}] [%thread] [%-4level] %logger{5}:%line %msg%n</pattern>

3. Usage Guide

3.1 Logback in a Spring Boot Project

Spring Boot uses Logback by default, so no additional configuration is required for a typical Spring Boot application. For non‑Spring projects you can add the following Maven dependency and place logback.xml under src/main/resources :

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>${logback.version}</version>
</dependency>

Sample logback.xml :

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <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/output.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>log/output.log.%i</fileNamePattern>
    </rollingPolicy>
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <MaxFileSize>1MB</MaxFileSize>
    </triggeringPolicy>
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

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

3.2 Log4j2 in a Spring Project

Spring Boot defaults to Logback, so you must exclude the default logging starter and add Log4j2 dependencies:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>${log4j.version}</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>${log4j.version}</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j2-impl</artifactId>
    <version>${log4j.version}</version>
</dependency>

Place log4j2.xml under src/main/resources . Sample configuration:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Properties>
    <Property name="log.pattern">%d{MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36}%n%msg%n%n</Property>
    <Property name="file.err.filename">log/err.log</Property>
    <Property name="file.err.pattern">log/err.%i.log.gz</Property>
  </Properties>
  <Appenders>
    <Console name="console" target="SYSTEM_OUT">
      <PatternLayout pattern="${log.pattern}"/>
    </Console>
    <RollingFile name="err" fileName="${file.err.filename}" filePattern="${file.err.pattern}" bufferedIO="true">
      <PatternLayout pattern="${log.pattern}"/>
      <Policies>
        <SizeBasedTriggeringPolicy size="1 MB"/>
      </Policies>
      <DefaultRolloverStrategy max="10"/>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="info">
      <AppenderRef ref="console" level="info"/>
      <AppenderRef ref="err" level="error"/>
    </Root>
  </Loggers>
</Configuration>

Best Practices

Configure rolling policies to prevent disks from filling up.

Standardize log format across services (e.g., timestamp, thread, level, logger, message).

Avoid logging method names or line numbers in high‑throughput paths, as they add noticeable overhead.

Ensure a uniform logging strategy company‑wide to simplify downstream ETL and analysis.

4. Appendices

4.1 Test Code

package com.winjeg.demo;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

@Slf4j
public class Main {
    private static final Logger LOG = LoggerFactory.getLogger(Main.class);
    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
        128, 256, 1L, TimeUnit.MINUTES,
        new ArrayBlockingQueue<>(512),
        new BasicThreadFactory.Builder().namingPattern("thread-%d").daemon(true).build()
    );

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        execute(8, 160_000);
        long first = System.currentTimeMillis();
        execute(8, 160_000);
        System.out.printf("time cost, preheat:%d\t, formal:%d\n", first - start, System.currentTimeMillis() - first);
    }

    private static void execute(int threadNum, int times) {
        List
> futures = new ArrayList<>();
        for (int i = 0; i < threadNum; i++) {
            Future
f = EXECUTOR.submit(() -> {
                for (long j = 0; j < times; j++) {
                    log.info("main - info level ...this is a demo script, pure string log will be used!");
                }
            });
            futures.add(f);
        }
        futures.forEach(f -> {
            try { f.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); }
        });
    }
}

4.2 References

Logback performance test: https://logback.qos.ch/performance.html

Log4j2 performance test: https://logging.apache.org/log4j/2.x/performance.html

Comparison article: https://zhuanlan.zhihu.com/p/472941897

Performance Testingspring-bootLogbackSlf4jlog4j2Java Logging
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.