javalogginglog4j2logbackslf4j

Is it possible to route Logback logging to SLF4J in a Log4j2-based project


My project uses SLF4J with Log4j2, but recently I introduced a dependency that uses Logback for logging and DOES NOT use SLF4J. The dependency's code includes the following import:

import ch.qos.logback.classic.Logger;

When I start my project, I get the following error:

Caused by: java.lang.NoClassDefFoundError: ch/qos/logback/core/Context

However, I DO NOT want my project to directly include Logback as a dependency. Is there a way to bridge Logback to SLF4J, similar to how Log4j can be bridged to SLF4J?


Update:

Below is my pom.xml where I have made equivalent replacements:

<!-- SLF4J API -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.36</version>
</dependency>


<!-- spring log4j2 starter -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-log4j2</artifactId>
  <version>2.5.12</version>
</dependency>

<!-- our company inner dependency -->
<dependency>
  <groupId>our.company.inner.project</groupId>
  <artifactId>some-jar</artifactId>
  <version>1.0</version>
  <!-- I don't want to include any transitive dependencies -->
  <exclusions>
    <exclusion>
      <groupId>*</groupId>
      <artifactId>*</artifactId>
    </exclusion>
  </exclusions>
</dependency>

The some-jar dependency includes Logback, and I must use this jar to fulfill my business requirements. However, I do not want multiple logging implementations in my system, so I manually excluded the Logback dependency. Despite this, I still encounter errors at runtime.


Solution

  • TL;DR: The best solution is to fix the library so it doesn’t depend directly on Logback.

    Modern logging frameworks are intentionally modular, separating API from implementation. This design is meant to promote flexibility and decoupling:

    Bridging between APIs (e.g. SLF4J → Log4j API or vice versa) is relatively straightforward. But bridging from an implementation back to an API (e.g. Logback → Log4j API or Log4j Core → SLF4J) is fundamentally flawed: it violates the abstraction and can’t be done cleanly.

    Possible solutions

    There are several possible solutions to your problem. I mention three of them in order of preference:

    1. Fix or isolate the Logback dependency (Recommended)

    The ideal solution is to eliminate the hard dependency on Logback.

    Direct use of ch.qos.logback.* classes usually points to one of two things:

    Ask your colleagues: “Is logging configuration part of this library’s business logic?”

    Small note: We're currently developing the Apache Logging Admin API, which will allow users to modify logging levels at runtime in an implementation-independent way. The initial release has been delayed as we focus on other priorities and gather community feedback.

    2. Replace Logback with a "mock" implementation

    Create a drop-in replacement for logback-classic that mimics its API but internally forwards to the Log4j 2 API. This is similar to how:

    However, this can be fragile and difficult to maintain, especially if the original code uses deep Logback features.

    3. Use a Logback Appender to forward logs to Log4j

    As @Slaw suggested, you can write a Logback appender that forwards events to the Log4j 2 API.

    This works, but comes with serious downsides:

    Use this only as a last resort. In practice, this approach is primarily used for java.util.logging (JUL), whose classes cannot be replaced. Examples include Log4jBridgeHandler and SLF4JBridgeHandler, which forward JUL events to other logging systems. These handlers are tightly coupled to their respective implementations (Log4j Core, Logback) to ensure disabled loggers are fast and efficient—something that's difficult to replicate with a generic forwarding appender.

    Summary

    There’s no “bridge” from Logback to Log4j API that works correctly because you’re starting from the implementation, not the API. Logging frameworks are designed to route top-down: from API to implementation, not the other way around.