Skip to content

Commit

Permalink
Add Log4j 1 to Log4j 2 configuration file conversion
Browse files Browse the repository at this point in the history
This PR uses the [Log4j Configuration Converter API](https://logging.staged.apache.org/log4j/transform/log4j-converter-config.html) to add a
`Log4j1ConfigurationToLog4jCore2` rule, which converts `log4j.properties` and `log4j.xml` configuration files into the equivalent `log4j2.xml` configuration files.

It also refactors the `Log4j1toLog4j2` recipe into three parts:

- `Log4j1toLog4jAPI` performs the migration from the "Log4j 1 API" to Log4j API 2,
as described in [Log4j 1 API migration](https://logging.apache.org/log4j/2.x/migrate-from-log4j1.html#api-migration). This recipe performs almost exclusively Java code changes and is usually not required in applications that use JCL or SLF4J as logging interface.
- `Log4j1ConfigurationToLog4jCore2` migrates configuration files, as described in the [Log4j 1 Configuration file migration](https://logging.apache.org/log4j/2.x/migrate-from-log4j1.html#configuration-file-migration) section.
- `Log4j1ToLog4jCore2` migrates the runtime dependencies to use Log4j Core 2 instead of Log4j 1, as described in [Log4j 1 Backend migration](https://logging.apache.org/log4j/2.x/migrate-from-log4j1.html#backend-migration) and also calls the previous recipe. This is probably the most useful recipe for users that no longer user Log4j 1 directly, but use it as logging implementation.

Closes openrewrite#154.
Related to apache/logging-log4j2#3220
  • Loading branch information
ppkarwasz committed Dec 18, 2024
1 parent 6a714a1 commit 367362b
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 28 deletions.
12 changes: 10 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ recipeDependencies {
parserClasspath("org.projectlombok:lombok:1.18.+")
}

repositories {
mavenCentral()
mavenLocal()
maven("https://repository.apache.org/snapshots")
}

dependencies {
compileOnly("org.projectlombok:lombok:latest.release")
annotationProcessor("org.projectlombok:lombok:latest.release")
Expand All @@ -27,13 +33,15 @@ dependencies {

implementation(platform("org.openrewrite:rewrite-bom:${rewriteVersion}"))
implementation("org.openrewrite:rewrite-java")
implementation("org.openrewrite:rewrite-maven")
implementation("org.openrewrite.recipe:rewrite-java-dependencies:${rewriteVersion}")
implementation("org.openrewrite.recipe:rewrite-static-analysis:${rewriteVersion}")
runtimeOnly("org.openrewrite:rewrite-java-17")

implementation("log4j:log4j:1.+")
implementation("org.apache.logging.log4j:log4j-core:2.+")
implementation("org.apache.logging.log4j:log4j-core:2.24.3")
implementation("org.slf4j:slf4j-api:2.+")
implementation("org.apache.logging.log4j:log4j-converter-config:0.3.0-SNAPSHOT")

annotationProcessor("org.openrewrite:rewrite-templating:$rewriteVersion")
implementation("org.openrewrite:rewrite-templating:$rewriteVersion")
Expand All @@ -46,7 +54,7 @@ dependencies {
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:latest.release")

testImplementation("org.openrewrite:rewrite-kotlin:${rewriteVersion}")
testImplementation("org.openrewrite:rewrite-maven")
testImplementation("org.openrewrite:rewrite-properties:${rewriteVersion}")
testImplementation("org.openrewrite:rewrite-test")
testImplementation("org.openrewrite:rewrite-java-tck")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package org.openrewrite.java.logging;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import lombok.AllArgsConstructor;
import org.apache.logging.converter.config.ConfigurationConverter;
import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.FindSourceFiles;
import org.openrewrite.NlsRewrite.Description;
import org.openrewrite.NlsRewrite.DisplayName;
import org.openrewrite.Option;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.SourceFile;
import org.openrewrite.Tree;
import org.openrewrite.TreeVisitor;
import org.openrewrite.text.PlainText;

/**
* Converts a logging configuration file from one format to another.
*/
@AllArgsConstructor
public class ConvertConfiguration extends Recipe {

@Option(displayName = "Pattern for the files to convert",
description = "If set, only the files that match this pattern will be converted.",
required = false)
@Nullable
String filePattern;

@Option(displayName = "Input format", description = "The id of the input logging configuration format. See [Log4j documentation](https://logging.staged.apache.org/log4j/transform/log4j-converter-config.html#formats) for a list of supported formats.", example = "v1:properties")
String inputFormat;

@Option(displayName = "Output format", description = "The id of the output logging configuration format. See [Log4j documentation](https://logging.staged.apache.org/log4j/transform/log4j-converter-config.html#formats) for a list of supported formats.", example = "v2:xml")
String outputFormat;

private static final ConfigurationConverter converter = ConfigurationConverter.getInstance();

@Override
public @DisplayName String getDisplayName() {
return "Convert logging configuration";
}

@Override
public @Description String getDescription() {
return "Converts the configuration of a logging backend from one format to another. For example it can convert a Log4j 1 properties configuration file into a Log4j Core 2 XML configuration file.";
}

@Override
public int maxCycles() {
return 1;
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return
Preconditions.check(new FindSourceFiles(filePattern),
new TreeVisitor<Tree, ExecutionContext>() {

@Override
public boolean isAcceptable(SourceFile sourceFile, ExecutionContext executionContext) {
return super.isAcceptable(sourceFile, executionContext);
}

@Override
public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext executionContext) {
if (tree instanceof SourceFile) {
SourceFile sourceFile = (SourceFile) tree;
ByteArrayInputStream inputStream = new ByteArrayInputStream(sourceFile.printAllAsBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

converter.convert(inputStream, inputFormat, outputStream, outputFormat);

String utf8 = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
if (tree instanceof PlainText) {
return ((PlainText) tree).withText(utf8).withCharset(StandardCharsets.UTF_8);
}
return PlainText.builder()
.id(sourceFile.getId())
.charsetBomMarked(sourceFile.isCharsetBomMarked())
.charsetName(StandardCharsets.UTF_8.name())
.checksum(sourceFile.getChecksum())
.fileAttributes(sourceFile.getFileAttributes())
.markers(sourceFile.getMarkers())
.sourcePath(sourceFile.getSourcePath())
.text(utf8)
.build();
}
return super.visit(tree, executionContext);
}
});
}
}
102 changes: 83 additions & 19 deletions src/main/resources/META-INF/rewrite/log4j.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.logging.log4j.ParameterizedLogging
displayName: Parameterize Log4j 2.x logging statements
description: Use Log4j 2.x parameterized logging, which can significantly boost performance for messages that
displayName: Parameterize Log4j API 2 logging statements
description: Use Log4j API 2 parameterized logging, which can significantly boost performance for messages that
otherwise would be assembled with String concatenation. Particularly impactful when the log level is not enabled, as
no work is done to assemble the message.
tags:
Expand Down Expand Up @@ -57,8 +57,22 @@ recipeList:
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.logging.log4j.Log4j1ToLog4j2
displayName: Migrate Log4j 1.x to Log4j 2.x
description: Migrates Log4j 1.x to Log4j 2.x.
displayName: Migrate Log4j 1 to Log4j API/Log4j Core 2
description: Transforms code written using Log4j 1 to use Log4j API 2
and switches the logging backend from Log4j 1 to Log4j Core 2.
tags:
- logging
- log4j
recipeList:
- org.openrewrite.java.logging.log4j.Log4j1ToLog4jApi
- org.openrewrite.java.logging.log4j.Log4j1ToLog4jCore2
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.logging.log4j.Log4j1ToLog4jApi
displayName: Migrate Log4j 1 to Log4j API 2
description: "Transforms code written using Log4j 1 to use Log4j API 2.
Should be used together with a recipe that switches the logging implementation to a more modern one:
e.g. JBoss LogManager, Log4j Core 2 or Logback."
tags:
- logging
- log4j
Expand All @@ -71,7 +85,6 @@ recipeList:
- org.openrewrite.java.ChangeMethodTargetToStatic:
methodPattern: org.apache.log4j.Logger getRootLogger()
fullyQualifiedTargetTypeName: org.apache.logging.log4j.LogManager

- org.openrewrite.java.logging.log4j.LoggerSetLevelToConfiguratorRecipe
- org.openrewrite.java.ChangeMethodName:
methodPattern: org.apache.log4j.Priority isGreaterOrEqual(org.apache.log4j.Priority)
Expand All @@ -80,7 +93,6 @@ recipeList:
- org.openrewrite.java.ChangeType:
oldFullyQualifiedTypeName: org.apache.log4j.Priority
newFullyQualifiedTypeName: org.apache.logging.log4j.Level

- org.openrewrite.java.ChangeMethodTargetToStatic:
methodPattern: org.apache.log4j.Category getInstance(Class)
fullyQualifiedTargetTypeName: org.apache.logging.log4j.LogManager
Expand All @@ -93,7 +105,6 @@ recipeList:
- org.openrewrite.java.ChangeType:
oldFullyQualifiedTypeName: org.apache.log4j.Category
newFullyQualifiedTypeName: org.apache.logging.log4j.Logger

- org.openrewrite.java.ChangePackage:
oldPackageName: org.apache.log4j
newPackageName: org.apache.logging.log4j
Expand All @@ -103,27 +114,41 @@ recipeList:
artifactId: log4j-api
version: 2.x
onlyIfUsing: org.apache.log4j.*
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.logging.log4j.Log4j1ToLog4jCore2
displayName: Migrate Log4j 1 to Log4j Core 2
description: Replaces Log4j 1 as logging implementation with Log4j Core 2.
This recipe does not replace code occurrences of Log4j 1 and should be only used if Log4j 1 is not used
directly as logging API.
recipeList:
# Guarantees alignment between Log4j API and Log4j Core version, regardless of the Maven conflict resolution
# algorithm used.
- org.openrewrite.maven.AddManagedDependency:
groupId: org.apache.logging.log4j
artifactId: log4j-bom
version: 2.x
type: pom
scope: import
- org.openrewrite.java.dependencies.AddDependency:
groupId: org.apache.logging.log4j
artifactId: log4j-core
version: 2.x
onlyIfUsing: org.apache.log4j.*
scope: runtime
# Removes Log4j 1 and replacements
- org.openrewrite.java.dependencies.RemoveDependency:
groupId: log4j
artifactId: log4j
- org.openrewrite.java.dependencies.RemoveDependency:
groupId: org.apache.logging.log4j
artifactId: log4j-1.2-api
- org.openrewrite.java.dependencies.RemoveDependency:
groupId: org.slf4j
artifactId: log4j-over-slf4j
- org.openrewrite.java.dependencies.RemoveDependency:
groupId: ch.qos.reload4j
artifactId: reload4j
- org.openrewrite.java.dependencies.AddDependency:
groupId: org.apache.logging.log4j
artifactId: log4j-api
version: 2.x
onlyIfUsing: org.apache.logging.log4j.*
- org.openrewrite.java.dependencies.AddDependency:
groupId: org.apache.logging.log4j
artifactId: log4j-core
version: 2.x
onlyIfUsing: org.apache.logging.log4j.*
# Switch the target of SLF4J-to-X bridges
- org.openrewrite.java.dependencies.ChangeDependency:
oldGroupId: org.slf4j
oldArtifactId: slf4j-log4j12
Expand All @@ -136,8 +161,47 @@ recipeList:
newGroupId: org.apache.logging.log4j
newArtifactId: log4j-slf4j-impl
newVersion: 2.x
#
- org.openrewrite.java.dependencies.UpgradeDependencyVersion:
groupId: commons-logging
artifactId: commons-logging
newVersion: latest.release
- org.openrewrite.java.logging.log4j.UpgradeLog4J2DependencyVersion

- org.openrewrite.java.logging.log4j.Log4j1ConfigurationToLog4jCore2
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.logging.log4j.Log4j1ConfigurationToLog4jCore2
displayName: Migrate Log4j 1 configuration to Log4j Core 2 format
description: Migrates Log4j 1 configuration files to the Log4j Core 2 XML format.
recipeList:
- org.openrewrite.java.logging.log4j.V1PropertiesToV2Xml
- org.openrewrite.java.logging.log4j.V1XmlToV2Xml
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.logging.log4j.V1PropertiesToV2Xml
displayName: Migrate `log4j.properties` files to `log4j2.xml` format
description: Migrates `log4j.properties` files to the `log4j2.xml` format.
recipeList:
- org.openrewrite.java.logging.ConvertConfiguration:
filePattern: "**/log4j.properties"
inputFormat: "v1:properties"
outputFormat: "v2:xml"
- org.openrewrite.RenameFile:
fileMatcher: "**/log4j.properties"
fileName: "log4j2.xml"
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.logging.log4j.V1XmlToV2Xml
displayName: Migrate `log4j.xml` files to `log4j2.xml` format
description: Migrates `log4j.xml` files to the `log4j2.xml` format.
recipeList:
- org.openrewrite.java.logging.ConvertConfiguration:
filePattern: "**/log4j.xml"
inputFormat: "v1:xml"
outputFormat: "v2:xml"
- org.openrewrite.RenameFile:
fileMatcher: "**/log4j.xml"
fileName: "log4j2.xml"
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.logging.log4j.UpgradeLog4J2DependencyVersion
Expand Down
6 changes: 4 additions & 2 deletions src/main/resources/META-INF/rewrite/logback.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.logging.logback.Log4jToLogback
displayName: Migrate Log4j 2.x to Logback
description: Migrates usage of Apache Log4j 2.x to using `logback` as an SLF4J implementation directly. Note, this currently does not modify `log4j.properties` files.
displayName: Migrate Log4j API/Log4j Core 2 to SLF4J/Logback
description: |
Migrates usage of Apache Log4j API with Log4j Core 2 implementation to SLF4J with Logback implementation.
Note, this currently does not modify the Log4j Core 2 configuration files.
tags:
- logging
- log4j
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.openrewrite.java.logging;

import static org.openrewrite.test.SourceSpecs.text;

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.test.RewriteTest;

class ConvertConfigurationTest implements RewriteTest {

@DocumentExample
@Test
void convertsLog4j1ToLog4j2Configuration() {
rewriteRun(spec -> spec.recipe(new ConvertConfiguration("file.txt", "v1:properties", "v2:xml")),
text("""
# Console appender
log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Follow = true
log4j.appender.CONSOLE.Target = System.err
log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern = %d [%t] %-5p %c - %m%n%ex
# Rolling file appender
log4j.appender.ROLLING = org.apache.log4j.RollingFileAppender
log4j.appender.ROLLING.File = file.log
log4j.appender.ROLLING.MaxBackupIndex = 30
# Exactly 10 GiB
log4j.appender.ROLLING.MaxFileSize = 10737418240
log4j.appender.ROLLING.layout = org.apache.log4j.SimpleLayout
# Loggers
log4j.rootLogger = INFO, CONSOLE
log4j.logger.org.openrewrite = DEBUG, CONSOLE, ROLLING
log4j.additivity.org.openrewrite = false
""",
"""
<?xml version='1.0' encoding='UTF-8'?>
<Configuration xmlns="https://logging.apache.org/xml/ns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-config-2.xsd">
<Properties/>
<Appenders>
<RollingFile append="true" bufferSize="8192" bufferedIo="false" fileName="file.log" filePattern="file.log.%i" immediateFlush="true" name="ROLLING">
<PatternLayout alwaysWriteExceptions="false" pattern="%p - %m%n"/>
<SizeBasedTriggeringPolicy size="10.00 GB"/>
<DefaultRolloverStrategy fileIndex="min" max="30"/>
</RollingFile>
<Console follow="true" immediateFlush="true" name="CONSOLE" target="SYSTEM_ERR">
<PatternLayout pattern="%d [%t] %-5p %c - %m%n%ex"/>
</Console>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="CONSOLE"/>
</Root>
<Logger additivity="false" level="DEBUG" name="org.openrewrite">
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="ROLLING"/>
</Logger>
</Loggers>
</Configuration>
"""));
}
}
Loading

0 comments on commit 367362b

Please sign in to comment.