Comment on page
Migrate to SLF4J from Log4J
In this guide, we'll use OpenRewrite to perform an automated migration from Apache
Log4j
(handling both log4j 1.x
or log4j 2.x
) to the Simple Logging Facade for Java (SLF4J
).The org.openrewrite.java.logging.slf4j.Log4jToSlf4j recipe has no required configuration options and can be activated directly after taking a dependency on rewrite-logging-frameworks in your build file:
Gradle
Maven
build.gradle
plugins {
id("org.openrewrite.rewrite") version("6.5.6")
}
rewrite {
activeRecipe("org.openrewrite.java.logging.slf4j.Log4jToSlf4j")
}
repositories {
mavenCentral()
}
dependencies {
rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.5.0"))
rewrite("org.openrewrite.recipe:rewrite-logging-frameworks")
}
pom.xml
<project>
<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>5.13.0</version>
<configuration>
<activeRecipes>
<recipe>org.openrewrite.java.logging.slf4j.Log4jToSlf4j</recipe>
</activeRecipes>
</configuration>
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-logging-frameworks</artifactId>
<version>2.0.4</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
At this point, you're ready to execute the migration by running
mvn rewrite:run
or gradlew rewriteRun
. After running the migration you can inspect the results with git diff
(or equivalent), manually fix anything that wasn't able to be migrated automatically, and commit the results.Similar to the
SLF4J Migrator
, the goal of the Log4jToSlf4j
recipe is to significantly reduce the amount of work involved in migrating Log4j
to SLF4J
. Because OpenRewrite works by leveraging type-attributed Lossless Semantic Tree (LST) representing your source code, the Log4jToSlf4j
recipe can overcome several limitations discussed in the SLF4J Migrator
guide.Because
Log4j
logging statements are automatically migrated to use the SLF4J Parameterized Logging
equivalent(see "Use SLF4J Parameterized Logging"), messages of type String
are supported. Therefore, there is no need to add a .toString()
method invocation on the object.Object Parameters (Before)
Object Parameters (After)
import org.apache.log4j.Logger;
class Example {
Logger logger = Logger.getLogger(Example.class);
void method(Example example) {
logger.info(example);
logger.info(new Object());
logger.info(new StringBuilder("append0"));
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class Example {
Logger logger = LoggerFactory.getLogger(Example.class);
void method(Example example) {
logger.info("{}", example);
logger.info("{}", new Object());
logger.info("{}", new StringBuilder("append0"));
}
}
The
Log4j
log level FATAL
is not supported in SLF4J
. Therefore, FATAL
level log statements are migrated to ERROR
level.Migrate fatal to error (Before)
Migrate fatal to error (After)
import org.apache.log4j.Logger;
class Example {
Logger logger = Logger.getLogger(Example.class);
void method() {
logger.fatal("uh oh");
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class Example {
Logger logger = LoggerFactory.getLogger(Example.class);
void method() {
logger.error("uh oh");
}
}
A method declaring multiple loggers on the same line will have a complete conversion.
Same Line Loggers (Before)
Same Line Loggers (After)
import org.apache.log4j.Logger;
class Example {
public void someMethod(Logger l1, Logger l2) {
}
}
// Notice the import change Logger to the SLF4J type equivalent:
import org.slf4j.Logger;
class Example {
public void someMethod(Logger l1, Logger l2) {
}
}
Additionally,
Log4j
ERROR
log level exceptions are migrated to their SLF4J
equivalent:Migrates Exceptions (Before)
Migrates Exceptions (After)
import org.apache.log4j.Logger;
class Example {
Logger logger = Logger.getLogger(Example.class);
void method(String numberString) {
try {
Integer i = Integer.valueOf(numberString);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class Example {
Logger logger = LoggerFactory.getLogger(Example.class);
void method(String numberString) {
try {
Integer i = Integer.valueOf(numberString);
} catch (Exception e) {
logger.error("{}", e.getMessage(), e);
}
}
}
The following is a list of known limitations/issues:
- Currently,
log4j
properties and configuration files are not migrated. Log4j
configuration customizations which do not have anSLF4J
equivalent cannot be migrated.
We are always looking for feedback on which tasks should be prioritized. If you have a specific use case that is not yet covered by this project, please reach out to our team!
Last modified 10d ago