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).
Example Configuration
This recipe has no required configuration options. It can be activated by adding a dependency on `org.openrewrite.recipe:rewrite-logging-frameworks` in your build file or by running a shell command (in which case no build changes are needed):
- Gradle
- Gradle init script
- Maven POM
- Maven Command Line
- Moderne CLI
- Add the following to your
build.gradlefile:build.gradleplugins {
id("org.openrewrite.rewrite") version("latest.release")
}
rewrite {
activeRecipe("org.openrewrite.java.logging.slf4j.Log4jToSlf4j")
}
repositories {
mavenCentral()
}
dependencies {
rewrite("org.openrewrite.recipe:rewrite-logging-frameworks:3.26.0")
} - Run
gradle rewriteRunto run the recipe.
- Create a file named
init.gradlein the root of your project.init.gradleinitscript {
repositories {
maven { url "https://plugins.gradle.org/m2" }
}
dependencies { classpath("org.openrewrite:plugin:7.28.2") }
}
rootProject {
plugins.apply(org.openrewrite.gradle.RewritePlugin)
dependencies {
rewrite("org.openrewrite.recipe:rewrite-logging-frameworks:3.26.0")
}
rewrite {
activeRecipe("org.openrewrite.java.logging.slf4j.Log4jToSlf4j")
}
afterEvaluate {
if (repositories.isEmpty()) {
repositories {
mavenCentral()
}
}
}
} - Run the recipe.shell
gradle --init-script init.gradle rewriteRun
- Add the following to your
pom.xmlfile:pom.xml<project>
<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>6.34.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>3.26.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project> - Run
mvn rewrite:runto run the recipe.
You will need to have Maven installed on your machine before you can run the following command.
mvn -U org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-logging-frameworks:RELEASE -Drewrite.activeRecipes=org.openrewrite.java.logging.slf4j.Log4jToSlf4j
You will need to have configured the Moderne CLI on your machine before you can run the following command.
mod run . --recipe Log4jToSlf4j
If the recipe is not available locally, then you can install it using:
mod config recipes jar install org.openrewrite.recipe:rewrite-logging-frameworks:3.26.0
For the full list of changes this recipe will make, see the recipe reference page.
Once you've configured your project, 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.
Before and After
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.
.toString()
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"));
}
}
FATAL to ERROR
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");
}
}
Migrate using type information
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) {
}
}
Handle Exception Migrations
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);
}
}
}
For the full list of changes, see the recipe's reference documentation.
Known Limitations
The following is a list of known limitations/issues:
- Currently,
log4jproperties and configuration files are not migrated. Log4jconfiguration customizations which do not have anSLF4Jequivalent 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 or submit a PR to help out the community.