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.
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.
There are several possible solutions to your problem. I mention three of them in order of preference:
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?”
If yes, then they should:
LoggingSystem
supports multiple implementations via abstraction.If no, then the library should use SLF4J or another API only — and let the application decide the backend (Logback, Log4j 2 Core, etc.).
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.
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:
log4j-over-slf4j
redirects Log4j 1.x calls to SLF4J.log4j-1.2-api
redirects Log4j 1.x calls to Log4j API and has a limited support for Log4j Core reconfiguration through the Log4j 1.x methods.However, this can be fragile and difficult to maintain, especially if the original code uses deep Logback features.
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.
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.