Migrating from Logback
Logback is a logging implementation for the SLF4J logging API, just like Log4j Core is a logging implementation for the Log4j API. In this page we will guide you through migrating from Logback to Log4j Core as your logging implementation.
Instead of migrating your logging implementation, Logback, are you looking for migrating your logging API, SLF4J? Please refer to Migrating from SLF4J. |
Struggling with the logging API, implementation, and bridge concepts? Click for an introduction.
- Logging API
-
A logging API is an interface your code or your dependencies directly logs against. It is required at compile-time. It is implementation agnostic to ensure that your application can write logs, but is not tied to a specific logging implementation. Log4j API, SLF4J, JUL (Java Logging), JCL (Apache Commons Logging), JPL (Java Platform Logging) and JBoss Logging are major logging APIs.
- Logging implementation
-
A logging implementation is only required at runtime and can be changed without the need to recompile your software. Log4j Core, JUL (Java Logging), Logback are the most well-known logging implementations.
- Logging bridge
-
Logging implementations accept input from a single logging API of their preference; Log4j Core from Log4j API, Logback from SLF4J, etc. A logging bridge is a simple logging implementation of a logging API that forwards all messages to a foreign logging API. Logging bridges allow a logging implementation to accept input from other logging APIs that are not their primary logging API. For instance,
log4j-slf4j2-impl
bridges SLF4J calls to Log4 API and effectively enables Log4j Core to accept input from SLF4J.
To make things a little bit more tangible, consider the following visualization of a typical Log4j Core installation with bridges for an application:
Migrating
You either have an application using Logback at runtime, or have a library using Logback for tests. In either case, you can replace Logback with Log4j Core as follows:
-
Remove
ch.qos.logback:logback-classic
dependency -
Remove
logback.xml
andlogback-test.xml
files -
Follow the instructions shared in the "Getting started" page
Next you need to re-organize your logging API bridges such that all foreign APIs are bridged to Log4j API, the logging API implemented by Log4j Core. This is explained in the next section.
Bridges
It is highly likely that you were bridging all logging APIs (including Log4j API!) to SLF4J, the logging API implemented by Logback. There are two particular approaches you can take here to ensure all logging APIs are instead bridged to Log4j API, the logging API implemented by Log4j Core:
Bridge all logging APIs to Log4j API
We strongly advise you to bridge all foreign logging APIs directly to Log4j API. You can use the cheat sheet shared below to implement that.
If dependency present | replace with |
---|---|
|
|
|
|
|
|
|
|
|
|
Bridge all logging APIs to SLF4J, and bridge SLF4J to Log4j API
You can implement this by replacing org.apache.logging.log4j:log4j-to-slf4j
dependency with org.apache.logging.log4j:log4j-slf4j2-impl
.
This approach is not recommended! It incurs certain drawbacks since some logging API calls will need to cross multiple bridges. For instance, a call to JUL will first be bridged to SLF4J, and then from there to Log4j API. |
Configuration
It might not always be trivial to match the contents of the newly created log4j2.xml
and log4j2-test.xml
files with your old logback.xml
and logback-test.xml
files.
While all Logback components have corresponding equivalents in Log4j Core, they might not be sharing the same name or configuration.
To assist with migrating Logback configuration components to Log4j Core, see the following pages:
For the complete list of all Log4j configuration knobs, see the Configuration page.
Parameterized logging
A common mistake in parameterized logging is to add a {}
placeholder for the exception associated with a log event:
} catch (Exception exception) {
logger.error("The foo process exited with an error: {}", exception);
}
Log4j Core and Logback differ in the way they treat this statement:
- Logback
-
Logback interprets the
exception
argument as throwable and removes it from the list of parameters. We end up with a parameterized statement with one placeholder, but zero parameters. The placeholder therefore remains as is:The foo process exited with and error: {} java.lang.RuntimeException: Message at example.MigrateFromLogback.doLogWrong(MigrateFromLogback.java:10) ...
- Log4j Core
-
Log4j Core first looks for the parameters of the message. Since the format string has one placeholder, the
exception
argument is interpreted as a parameter of the log message. The throwable associated to the log event isnull
, which results in a missing stack trace:The foo process exited with and error: java.lang.RuntimeException: Message
To fix this problem and get the same output in both backends, you should remove the placeholder from the format string:
} catch (Exception exception) {
logger.error("The foo process exited with an error.", exception);
}
After the change, the output will look us:
The foo process exited with and error.
java.lang.RuntimeException: Message
at example.MigrateFromLogback.doLogWrong(MigrateFromLogback.java:10)
...
As a temporary solution, the SLF4J-to-Log4j API bridges contain a special
|