You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by pk...@apache.org on 2023/01/10 19:48:30 UTC
[logging-log4j-transform] branch main updated: [LOG4J2-3638] Pre-compute location information
This is an automated email from the ASF dual-hosted git repository.
pkarwasz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/logging-log4j-transform.git
The following commit(s) were added to refs/heads/main by this push:
new 70e0328 [LOG4J2-3638] Pre-compute location information
70e0328 is described below
commit 70e0328139cd3c72f04b0a75cb14daed7019bc70
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Sun Jan 8 21:05:25 2023 +0100
[LOG4J2-3638] Pre-compute location information
Transforms `Logger` method calls to:
* use `LogBuilder` instead,
* use a pre-computed location value.
This closes #1.
---
log4j-transform-maven-plugin/pom.xml | 77 ++
.../src/it/location/pom.xml | 64 ++
.../logging/log4j/maven/it/location/Main.java | 35 +
.../apache/logging/log4j/maven/LocationMojo.java | 139 +++
.../maven/scan/ClassFileInclusionScanner.java | 32 +
.../log4j/maven/scan/SimpleInclusionScanner.java | 97 +++
log4j-transform-parent/pom.xml | 82 +-
log4j-weaver/pom.xml | 96 +++
.../log4j/weaver/ClassConversionHandler.java | 37 +
.../org/apache/logging/log4j/weaver/Constants.java | 75 ++
.../logging/log4j/weaver/ConversionException.java | 30 +
.../log4j/weaver/LocationCacheGenerator.java | 281 ++++++
.../log4j/weaver/LocationClassConverter.java | 47 +
.../logging/log4j/weaver/LocationClassVisitor.java | 88 ++
.../log4j/weaver/LocationMethodVisitor.java | 152 ++++
.../logging/log4j/weaver/SupplierLambdaType.java | 70 ++
.../weaver/log4j2/LogBuilderConversionHandler.java | 42 +
.../weaver/log4j2/LoggerConversionHandler.java | 343 ++++++++
.../weaver/AbstractConversionHandlerTest.java | 69 ++
.../logging/log4j/weaver/NoLoggingExample.java | 24 +
.../apache/logging/log4j/weaver/NoLoggingTest.java | 42 +
.../log4j2/LogBuilderConversionHandlerExample.java | 45 +
.../log4j2/LogBuilderConversionHandlerTest.java | 36 +
.../log4j/weaver/log4j2/LogBuilderMock.java | 44 +
.../log4j2/LoggerConversionHandlerExample.java | 943 +++++++++++++++++++++
.../weaver/log4j2/LoggerConversionHandlerTest.java | 58 ++
log4j-weaver/src/test/resources/log4j2-test.xml | 27 +
pom.xml | 22 +-
...638_Provide_Maven_plugin_to_inline_location.xml | 7 +
29 files changed, 3094 insertions(+), 10 deletions(-)
diff --git a/log4j-transform-maven-plugin/pom.xml b/log4j-transform-maven-plugin/pom.xml
new file mode 100644
index 0000000..a1e076c
--- /dev/null
+++ b/log4j-transform-maven-plugin/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache license, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the license for the specific language governing permissions and
+ ~ limitations under the license.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-transform-parent</artifactId>
+ <version>${revision}</version>
+ <relativePath>../log4j-transform-parent</relativePath>
+ </parent>
+ <artifactId>log4j-transform-maven-plugin</artifactId>
+ <packaging>maven-plugin</packaging>
+ <name>Apache Log4j Transform Maven plugin</name>
+ <description>The Apache Log4j Transform Maven plugin</description>
+ <properties>
+ <maven.version>3.8.6</maven.version>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-weaver</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-utils</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.plugin-tools</groupId>
+ <artifactId>maven-plugin-annotations</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-jar</id>
+ <configuration>
+ <archive combine.self="override"/>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/log4j-transform-maven-plugin/src/it/location/pom.xml b/log4j-transform-maven-plugin/src/it/location/pom.xml
new file mode 100644
index 0000000..acbda6d
--- /dev/null
+++ b/log4j-transform-maven-plugin/src/it/location/pom.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache license, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the license for the specific language governing permissions and
+ ~ limitations under the license.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.logging</groupId>
+ <artifactId>logging-parent</artifactId>
+ <version>7</version>
+ <relativePath/>
+ </parent>
+ <groupId>org.apache.logging.log4j.maven.it</groupId>
+ <artifactId>log4j-maven-it-location</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ <name>Apache Log4j Maven plugin test</name>
+ <description>The Apache Log4j Maven plugin test</description>
+ <packaging>jar</packaging>
+ <properties>
+ <log4jParentDir>${basedir}/../../../..</log4jParentDir>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <version>${project.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-maven-plugin</artifactId>
+ <version>${project.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-location</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/log4j-transform-maven-plugin/src/it/location/src/main/java/org/apache/logging/log4j/maven/it/location/Main.java b/log4j-transform-maven-plugin/src/it/location/src/main/java/org/apache/logging/log4j/maven/it/location/Main.java
new file mode 100644
index 0000000..e535c67
--- /dev/null
+++ b/log4j-transform-maven-plugin/src/it/location/src/main/java/org/apache/logging/log4j/maven/it/location/Main.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.maven.it.location;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public class Main implements Runnable {
+
+ private static final Logger logger = LogManager.getLogger();
+
+ public static void main(String[] args) {
+ logger.info("Application starts");
+ new Main().run();
+ logger.info("Application stops");
+ }
+
+ public void run() {
+ logger.info("Another log message");
+ }
+}
diff --git a/log4j-transform-maven-plugin/src/main/java/org/apache/logging/log4j/maven/LocationMojo.java b/log4j-transform-maven-plugin/src/main/java/org/apache/logging/log4j/maven/LocationMojo.java
new file mode 100644
index 0000000..b7acb51
--- /dev/null
+++ b/log4j-transform-maven-plugin/src/main/java/org/apache/logging/log4j/maven/LocationMojo.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.maven;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.logging.log4j.maven.scan.ClassFileInclusionScanner;
+import org.apache.logging.log4j.maven.scan.SimpleInclusionScanner;
+import org.apache.logging.log4j.weaver.LocationCacheGenerator;
+import org.apache.logging.log4j.weaver.LocationClassConverter;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+
+/**
+ * Generates location information for use with Log4j2.
+ */
+@Mojo(name = "generate-location", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true,
+ requiresDependencyResolution = ResolutionScope.COMPILE)
+public class LocationMojo extends AbstractMojo {
+
+ /**
+ * The directory containing class files to process.
+ */
+ @Parameter(defaultValue = "${project.build.outputDirectory}", required = true, readonly = true)
+ private File sourceDirectory;
+
+ /**
+ * The directory containing processed files.
+ */
+ @Parameter(defaultValue = "${project.build.outputDirectory}", required = true, readonly = true)
+ private File outputDirectory;
+
+ /**
+ * Sets the granularity in milliseconds of the last modification date for
+ * testing whether a class file needs weaving.
+ */
+ @Parameter(property = "lastModGranularityMs", defaultValue = "0")
+ private int staleMillis;
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ final Path sourceDirectory = this.sourceDirectory.toPath();
+ final Path outputDirectory = this.outputDirectory.toPath();
+ final LocationCacheGenerator locationCache = new LocationCacheGenerator();
+ final LocationClassConverter converter = new LocationClassConverter();
+
+ try {
+ final Set<Path> staleClassFiles = getClassFileInclusionScanner().getIncludedClassFiles(sourceDirectory,
+ outputDirectory);
+ staleClassFiles.stream()
+ .collect(Collectors.groupingBy(LocationCacheGenerator::getCacheClassFile))
+ .values()
+ .parallelStream()
+ .forEach(p -> convertClassfiles(p, converter, locationCache));
+
+ locationCache.generateClasses().forEach(this::saveClassFile);
+ } catch (WrappedIOException e) {
+ throw new MojoExecutionException("An I/O error occurred.", e.getCause());
+ }
+ }
+
+ private void convertClassfiles(List<Path> classFiles, LocationClassConverter converter,
+ LocationCacheGenerator locationCache) {
+ final Path sourceDirectory = this.sourceDirectory.toPath();
+ classFiles.sort(Path::compareTo);
+ final ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ try {
+ for (final Path classFile : classFiles) {
+ buf.reset();
+ try (final InputStream src = Files.newInputStream(sourceDirectory.resolve(classFile))) {
+ converter.convert(src, buf, locationCache);
+ }
+ saveClassFile(classFile, buf.toByteArray());
+ }
+ } catch (IOException e) {
+ throw new WrappedIOException(e);
+ }
+ }
+
+ private void saveClassFile(String internalClassName, byte[] data) {
+ try {
+ final Path outputDirectory = this.outputDirectory.toPath();
+ Files.write(outputDirectory.resolve(internalClassName + ".class"), data);
+ } catch (IOException e) {
+ throw new WrappedIOException(e);
+ }
+ }
+
+ private void saveClassFile(Path dest, byte[] data) {
+ try {
+ final Path outputDirectory = this.outputDirectory.toPath();
+ Files.write(outputDirectory.resolve(dest), data);
+ } catch (IOException e) {
+ throw new WrappedIOException(e);
+ }
+ }
+
+ protected ClassFileInclusionScanner getClassFileInclusionScanner() {
+ return new SimpleInclusionScanner(staleMillis, getLog());
+ }
+
+ private static class WrappedIOException extends RuntimeException {
+
+ private static final long serialVersionUID = 4290527889488735839L;
+
+ private WrappedIOException(IOException cause) {
+ super(cause);
+ }
+
+ }
+}
diff --git a/log4j-transform-maven-plugin/src/main/java/org/apache/logging/log4j/maven/scan/ClassFileInclusionScanner.java b/log4j-transform-maven-plugin/src/main/java/org/apache/logging/log4j/maven/scan/ClassFileInclusionScanner.java
new file mode 100644
index 0000000..fbefbc5
--- /dev/null
+++ b/log4j-transform-maven-plugin/src/main/java/org/apache/logging/log4j/maven/scan/ClassFileInclusionScanner.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.maven.scan;
+
+import java.nio.file.Path;
+import java.util.Set;
+
+public interface ClassFileInclusionScanner {
+
+ /**
+ * Finds class files matching a specific condition.
+ *
+ * @param sourceDir path to the folder where to search files
+ * @param targetDir an auxiliary folder
+ * @return a set of relative paths to file in {@code sourceDir}
+ */
+ Set<Path> getIncludedClassFiles(Path sourceDir, Path targetDir);
+}
diff --git a/log4j-transform-maven-plugin/src/main/java/org/apache/logging/log4j/maven/scan/SimpleInclusionScanner.java b/log4j-transform-maven-plugin/src/main/java/org/apache/logging/log4j/maven/scan/SimpleInclusionScanner.java
new file mode 100644
index 0000000..48ea78e
--- /dev/null
+++ b/log4j-transform-maven-plugin/src/main/java/org/apache/logging/log4j/maven/scan/SimpleInclusionScanner.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.maven.scan;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.logging.log4j.weaver.Constants;
+import org.apache.logging.log4j.weaver.LocationCacheGenerator;
+
+import org.apache.maven.plugin.logging.Log;
+import org.codehaus.plexus.util.DirectoryScanner;
+
+public class SimpleInclusionScanner implements ClassFileInclusionScanner {
+
+ private static final String DEFAULT_INCLUSION_PATTERN = "**/*.class";
+ private static final String DEFAULT_EXCLUSION_PATTERN = "**/*" + Constants.LOCATION_CACHE_SUFFIX + ".class";
+ private static final String[] EMPTY_ARRAY = new String[0];
+
+ private final long lastUpdatedWithinMsecs;
+ private final Set<String> sourceIncludes;
+ private final Set<String> sourceExcludes;
+ private final Log log;
+
+ public SimpleInclusionScanner(long lastUpdateWithinMsecs, Log log) {
+ this(lastUpdateWithinMsecs, Collections.singleton(DEFAULT_INCLUSION_PATTERN),
+ Collections.singleton(DEFAULT_EXCLUSION_PATTERN), log);
+ }
+
+ public SimpleInclusionScanner(long lastUpdateWithinMsecs, Set<String> sourceIncludes, Set<String> sourceExcludes,
+ Log log) {
+ this.lastUpdatedWithinMsecs = lastUpdateWithinMsecs;
+ this.sourceIncludes = new HashSet<>(sourceIncludes);
+ this.sourceExcludes = new HashSet<>(sourceExcludes);
+ this.log = log;
+ }
+
+ @Override
+ public Set<Path> getIncludedClassFiles(Path sourceDir, Path targetDir) {
+ final Set<Path> potentialSources = scanForSources(sourceDir, sourceIncludes, sourceExcludes);
+
+ return potentialSources.stream().filter(source -> isLocationCacheStale(sourceDir, targetDir, source))
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * @return a set of relative paths to class files
+ */
+ private static Set<Path> scanForSources(Path sourceDir, Set<String> sourceIncludes, Set<String> sourceExcludes) {
+ final DirectoryScanner scanner = new DirectoryScanner();
+ scanner.setBasedir(sourceDir.toFile());
+ scanner.setIncludes(sourceIncludes.toArray(EMPTY_ARRAY));
+ scanner.setExcludes(sourceExcludes.toArray(EMPTY_ARRAY));
+ scanner.scan();
+
+ return Stream.of(scanner.getIncludedFiles())
+ .map(sourceDir::resolve)
+ .collect(Collectors.toSet());
+ }
+
+ private boolean isLocationCacheStale(Path sourceDir, Path targetDir, Path source) {
+ try {
+ final Path target = targetDir.resolve(LocationCacheGenerator.getCacheClassFile(source));
+ if (!Files.exists(target)) {
+ return true;
+ }
+
+ final FileTime sourceModifiedTime = Files.getLastModifiedTime(sourceDir.resolve(source));
+ final FileTime targetModifiedTime = Files.getLastModifiedTime(target);
+ return targetModifiedTime.toMillis() - sourceModifiedTime.toMillis() > lastUpdatedWithinMsecs;
+ } catch (IOException e) {
+ log.warn("Unable to open file: " + source, e);
+ }
+ return false;
+ }
+}
diff --git a/log4j-transform-parent/pom.xml b/log4j-transform-parent/pom.xml
index 16e5c95..50d984d 100644
--- a/log4j-transform-parent/pom.xml
+++ b/log4j-transform-parent/pom.xml
@@ -24,38 +24,94 @@
<version>${revision}</version>
</parent>
<artifactId>log4j-transform-parent</artifactId>
- <version>${revision}</version>
<packaging>pom</packaging>
<properties>
<!-- project properties -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.resourceEncoding>UTF-8</project.build.resourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <!-- `minimalJavaBuildVersion` is employed by `org.apache:apache`, which parents `org.apache.logging:logging-parent`, which parents `org.apache.logging.log4j.tools:log4j-tools-bom`, which parents us.
- `minimalJavaBuildVersion` is used for enforcing the compiler version.
- We will use `java.version` to enforce the target JVM byte code, which is 8. -->
- <minimalJavaBuildVersion>[11,12)</minimalJavaBuildVersion>
- <java.version>8</java.version>
<!-- `project.build.outputTimestamp` is required for reproducible builds: https://maven.apache.org/guides/mini/guide-reproducible-builds.html -->
<project.build.outputTimestamp>0</project.build.outputTimestamp>
<!-- disable `maven-site-plugin`-->
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
<!-- dependency versions -->
+ <asm.version>9.4</asm.version>
+ <assertj.version>3.23.1</assertj.version>
+ <commons-lang.version>3.12.0</commons-lang.version>
+ <commons-logging.version>1.2</commons-logging.version>
<freemarker.version>2.3.31</freemarker.version>
+ <junit.version>5.9.1</junit.version>
+ <log4j2.version>2.19.1-SNAPSHOT</log4j2.version>
+ <maven.version>3.6.0</maven.version>
+ <plexus-utils.version>3.1.0</plexus-utils.version>
+ <slf4j.version>2.0.6</slf4j.version>
<spotbugs.version>4.7.3</spotbugs.version>
<!-- plugin versions -->
+ <bundle-plugin.version>5.1.8</bundle-plugin.version>
<error-prone.version>2.16</error-prone.version>
<findsecbugs-plugin.version>1.12.0</findsecbugs-plugin.version>
<spotbugs-maven-plugin.version>${spotbugs.version}.0</spotbugs-maven-plugin.version>
+ <surefire.version>3.0.0-M7</surefire.version>
</properties>
<dependencyManagement>
<dependencies>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-bom</artifactId>
+ <version>${asm.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit</groupId>
+ <artifactId>junit-bom</artifactId>
+ <version>${junit.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-bom</artifactId>
+ <version>${log4j2.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>${assertj.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>${commons-lang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>${commons-logging.version}</version>
+ </dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-api</artifactId>
+ <version>${maven.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-utils</artifactId>
+ <version>${plexus-utils.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
@@ -66,6 +122,11 @@
<build>
<pluginManagement>
<plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${bundle-plugin.version}</version>
+ </plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
@@ -156,6 +217,15 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <jdkToolchain>
+ <version>${java.version}</version>
+ </jdkToolchain>
+ </configuration>
+ </plugin>
<!-- Configuration here must match the one in `.editorconfig`! -->
<plugin>
<groupId>com.github.spotbugs</groupId>
diff --git a/log4j-weaver/pom.xml b/log4j-weaver/pom.xml
new file mode 100644
index 0000000..3b8b3d6
--- /dev/null
+++ b/log4j-weaver/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache license, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the license for the specific language governing permissions and
+ ~ limitations under the license.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-transform-parent</artifactId>
+ <version>${revision}</version>
+ <relativePath>../log4j-transform-parent</relativePath>
+ </parent>
+ <artifactId>log4j-weaver</artifactId>
+ <name>Apache Log4j Weaving Tools</name>
+ <description>The Apache Log4j Weaving Tools for logging frameworks</description>
+ <dependencies>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-commons</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.github.spotbugs</groupId>
+ <artifactId>spotbugs-annotations</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/ClassConversionHandler.java b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/ClassConversionHandler.java
new file mode 100644
index 0000000..e88f654
--- /dev/null
+++ b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/ClassConversionHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver;
+
+/**
+ * Handles rewriting the methods of a specific class.
+ */
+public interface ClassConversionHandler {
+
+ /**
+ * Gets the internal name of the class supported by this handler.
+ */
+ String getOwner();
+
+ /**
+ * Handles a method call
+ *
+ * @param mv a method visitor
+ * @param name the name of the method
+ * @param descriptor the descriptor of the method
+ */
+ void handleMethodInstruction(LocationMethodVisitor mv, String name, String descriptor);
+}
diff --git a/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/Constants.java b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/Constants.java
new file mode 100644
index 0000000..ec964ca
--- /dev/null
+++ b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/Constants.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver;
+
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.Method;
+
+public class Constants {
+
+ public static final Type[] EMPTY_ARRAY = new Type[0];
+ public static final String LOCATION_CACHE_SUFFIX = "$$Log4j2$$Cache";
+
+ // JDK types
+ public static final Type OBJECT_TYPE = Type.getType(Object.class);
+ public static final Type OBJECT_ARRAY_TYPE = Type.getType(Object[].class);
+ public static final Type STACK_TRACE_ELEMENT_TYPE = Type.getType(StackTraceElement.class);
+ public static final Type STACK_TRACE_ELEMENT_ARRAY_TYPE = Type.getType(StackTraceElement[].class);
+ public static final Type STRING_TYPE = Type.getType(String.class);
+ public static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
+
+ // Log4j2 types
+ public static final Type ENTRY_MESSAGE_TYPE = Type.getObjectType("org/apache/logging/log4j/message/EntryMessage");
+ public static final Type EXIT_MESSAGE_TYPE = Type.getObjectType("org/apache/logging/log4j/message/ExitMessage");
+ public static final Type FLOW_MESSAGE_FACTORY_TYPE = Type
+ .getObjectType("org/apache/logging/log4j/message/FlowMessageFactory");
+ public static final Type LOG_BUILDER_TYPE = Type.getObjectType("org/apache/logging/log4j/LogBuilder");
+ public static final Type LEVEL_TYPE = Type.getObjectType("org/apache/logging/log4j/Level");
+ public static final Type LOGGER_TYPE = Type.getObjectType("org/apache/logging/log4j/Logger");
+ public static final Type LOGGING_SYSTEM_TYPE = Type.getObjectType("org/apache/logging/log4j/spi/LoggingSystem");
+ public static final Type MARKER_TYPE = Type.getObjectType("org/apache/logging/log4j/Marker");
+ public static final Type MESSAGE_TYPE = Type.getObjectType("org/apache/logging/log4j/message/Message");
+ public static final Type MESSAGE_FACTORY_TYPE = Type
+ .getObjectType("org/apache/logging/log4j/message/MessageFactory");
+ public static final Type MESSAGE_SUPPLIER_TYPE = Type
+ .getObjectType("org/apache/logging/log4j/util/MessageSupplier");
+ public static final Type SUPPLIER_TYPE = Type.getObjectType("org/apache/logging/log4j/util/Supplier");
+ public static final Type SUPPLIER_ARRAY_TYPE = Type.getType("[" + SUPPLIER_TYPE.getDescriptor());
+
+ // LogBuilder methods types
+ private static final String NO_ARGS_DESC = Type.getMethodDescriptor(LOG_BUILDER_TYPE);
+ public static final Method AT_DEBUG_METHOD = new Method("atDebug", NO_ARGS_DESC);
+ public static final Method AT_ERROR_METHOD = new Method("atError", NO_ARGS_DESC);
+ public static final Method AT_FATAL_METHOD = new Method("atFatal", NO_ARGS_DESC);
+ public static final Method AT_INFO_METHOD = new Method("atInfo", NO_ARGS_DESC);
+ public static final Method AT_TRACE_METHOD = new Method("atTrace", NO_ARGS_DESC);
+ public static final Method AT_WARN_METHOD = new Method("atWarn", NO_ARGS_DESC);
+ public static final Method AT_LEVEL_METHOD = new Method("atLevel",
+ Type.getMethodDescriptor(LOG_BUILDER_TYPE, LEVEL_TYPE));
+ public static final Method WITH_LOCATION_METHOD = new Method("withLocation",
+ Type.getMethodDescriptor(LOG_BUILDER_TYPE, STACK_TRACE_ELEMENT_TYPE));
+ public static final Method WITH_MARKER_METHOD = new Method("withMarker",
+ Type.getMethodDescriptor(LOG_BUILDER_TYPE, MARKER_TYPE));
+ public static final Method WITH_THROWABLE_METHOD = new Method("withThrowable",
+ Type.getMethodDescriptor(LOG_BUILDER_TYPE, THROWABLE_TYPE));
+ public static final Method LOG_AND_GET_METHOD = new Method("logAndGet",
+ Type.getMethodDescriptor(MESSAGE_TYPE, SUPPLIER_TYPE));
+
+ private Constants() {
+ // prevent instantiation
+ }
+}
diff --git a/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/ConversionException.java b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/ConversionException.java
new file mode 100644
index 0000000..7620e7c
--- /dev/null
+++ b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/ConversionException.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver;
+
+/**
+ * Thrown by the converter, when it encounters some logging framework feature that is not supported.
+ */
+public class ConversionException extends RuntimeException {
+
+ private static final long serialVersionUID = -177281028782936849L;
+
+ public ConversionException(String message) {
+ super(message);
+ }
+
+}
diff --git a/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationCacheGenerator.java b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationCacheGenerator.java
new file mode 100644
index 0000000..91f5e67
--- /dev/null
+++ b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationCacheGenerator.java
@@ -0,0 +1,281 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver;
+
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.InstructionAdapter;
+
+import static org.apache.logging.log4j.weaver.Constants.ENTRY_MESSAGE_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.EXIT_MESSAGE_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.FLOW_MESSAGE_FACTORY_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.LOGGER_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.MESSAGE_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.OBJECT_ARRAY_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.OBJECT_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.STACK_TRACE_ELEMENT_ARRAY_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.STACK_TRACE_ELEMENT_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.STRING_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.SUPPLIER_ARRAY_TYPE;
+
+public class LocationCacheGenerator {
+
+ private static final Type LAMBDA_UTIL_TYPE = Type.getObjectType("org/apache/logging/log4j/util/LambdaUtil");
+ private static final Type STRING_FORMATTER_MESSAGE_FACTORY_TYPE = Type
+ .getObjectType("org/apache/logging/log4j/message/StringFormatterMessageFactory");
+ private static final String LOCATION_FIELD = "locations";
+
+ private final Map<String, LocationCacheContents> locationCacheClasses = new ConcurrentHashMap<>();
+
+ public LocationCacheValue addLocation(final String internalClassName, final String methodName,
+ final String fileName, final int lineNumber) {
+ final String cacheClassName = getCacheClassName(internalClassName);
+ final LocationCacheContents contents = locationCacheClasses.computeIfAbsent(cacheClassName,
+ k -> new LocationCacheContents());
+ final int index = contents.addLocation(internalClassName, methodName, fileName, lineNumber);
+ return new LocationCacheValue(cacheClassName, LOCATION_FIELD, index);
+ }
+
+ public Handle createLambda(String internalClassName, SupplierLambdaType type) {
+ final String cacheClassName = getCacheClassName(internalClassName);
+ final LocationCacheContents contents = locationCacheClasses.computeIfAbsent(cacheClassName,
+ k -> new LocationCacheContents());
+ contents.addLambda(type);
+ final String methodName = type.name().toLowerCase();
+ final String methodDescriptor = Type.getMethodDescriptor(MESSAGE_TYPE, type.getArgumentTypes());
+ switch (type) {
+ case FORMATTED_MESSAGE:
+ case ENTRY_MESSAGE_MESSAGE:
+ case ENTRY_MESSAGE_STRING_OBJECTS:
+ case ENTRY_MESSAGE_STRING_SUPPLIERS:
+ case EXIT_MESSAGE_ENTRY_MESSAGE:
+ case EXIT_MESSAGE_MESSAGE:
+ case EXIT_MESSAGE_OBJECT_ENTRY_MESSAGE:
+ case EXIT_MESSAGE_OBJECT_MESSAGE:
+ case EXIT_MESSAGE_STRING_OBJECT:
+ return new Handle(Opcodes.H_INVOKESTATIC, cacheClassName, methodName, methodDescriptor, false);
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public Map<String, byte[]> generateClasses() {
+ return locationCacheClasses.entrySet()
+ .parallelStream()
+ .collect(Collectors.toMap(Entry::getKey, e -> generateCacheClass(e.getKey(), e.getValue())));
+ }
+
+ private static byte[] generateCacheClass(final String innerClassName, final LocationCacheContents contents) {
+ final ClassWriter cv = new ClassWriter(0);
+ cv.visit(Opcodes.V1_8, 0, innerClassName, null, OBJECT_TYPE.getInternalName(), null);
+ // Write locations field
+ final List<StackTraceElement> locations = contents.getLocations();
+ writeLocations(innerClassName, cv, locations);
+ // We add lambdas to this class
+ final Set<SupplierLambdaType> lambdas = contents.getLambdas();
+ for (final SupplierLambdaType type : lambdas) {
+ final InstructionAdapter mv = new InstructionAdapter(cv.visitMethod(Opcodes.ACC_STATIC,
+ type.name().toLowerCase(), type.getImplementationMethodDescriptor(), null, null));
+ switch (type) {
+ case FORMATTED_MESSAGE:
+ writeFormattedMessage(mv);
+ break;
+ case ENTRY_MESSAGE_MESSAGE:
+ case ENTRY_MESSAGE_STRING_OBJECTS:
+ case EXIT_MESSAGE_ENTRY_MESSAGE:
+ case EXIT_MESSAGE_MESSAGE:
+ case EXIT_MESSAGE_OBJECT_ENTRY_MESSAGE:
+ case EXIT_MESSAGE_OBJECT_MESSAGE:
+ case EXIT_MESSAGE_STRING_OBJECT:
+ writeEntryExitMessage(mv, type);
+ break;
+ case ENTRY_MESSAGE_STRING_SUPPLIERS:
+ writeEntryMessageSuppliers(mv);
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+ cv.visitEnd();
+ return cv.toByteArray();
+ }
+
+ private static void writeLocations(final String innerClassName, final ClassVisitor cv,
+ final List<StackTraceElement> locations) {
+ cv.visitField(Opcodes.ACC_STATIC, LOCATION_FIELD, STACK_TRACE_ELEMENT_ARRAY_TYPE.getInternalName(), null, null)
+ .visitEnd();
+ final InstructionAdapter mv = new InstructionAdapter(
+ cv.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null));
+ mv.visitCode();
+ mv.visitMaxs(9, 0);
+ mv.iconst(locations.size());
+ mv.newarray(STACK_TRACE_ELEMENT_TYPE);
+ for (int i = 0; i < locations.size(); i++) {
+ final StackTraceElement location = locations.get(i);
+ mv.dup();
+ mv.iconst(i);
+ mv.anew(STACK_TRACE_ELEMENT_TYPE);
+ mv.dup();
+ mv.aconst(location.getClassName());
+ mv.aconst(location.getMethodName());
+ mv.aconst(location.getFileName());
+ mv.iconst(location.getLineNumber());
+ mv.invokespecial(STACK_TRACE_ELEMENT_TYPE.getInternalName(), "<init>",
+ Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE, STRING_TYPE, STRING_TYPE, Type.INT_TYPE),
+ false);
+ mv.visitInsn(Opcodes.AASTORE);
+ }
+ mv.putstatic(innerClassName, LOCATION_FIELD, STACK_TRACE_ELEMENT_ARRAY_TYPE.getInternalName());
+ mv.areturn(Type.VOID_TYPE);
+ mv.visitEnd();
+ }
+
+ private static void writeFormattedMessage(final InstructionAdapter mv) {
+ mv.visitCode();
+ mv.visitMaxs(3, 2);
+ mv.getstatic(STRING_FORMATTER_MESSAGE_FACTORY_TYPE.getInternalName(), "INSTANCE",
+ STRING_FORMATTER_MESSAGE_FACTORY_TYPE.getDescriptor());
+ mv.load(0, STRING_TYPE);
+ mv.load(1, OBJECT_ARRAY_TYPE);
+ mv.invokevirtual(STRING_FORMATTER_MESSAGE_FACTORY_TYPE.getInternalName(), "newMessage",
+ Type.getMethodType(MESSAGE_TYPE, STRING_TYPE, OBJECT_ARRAY_TYPE).getDescriptor(), false);
+ mv.areturn(MESSAGE_TYPE);
+ mv.visitEnd();
+ }
+
+ private static void writeEntryExitMessage(final InstructionAdapter mv, final SupplierLambdaType type) {
+ final Type[] args = type.getArgumentTypes();
+ mv.visitCode();
+ mv.visitMaxs(args.length, args.length);
+ mv.load(0, LOGGER_TYPE);
+ mv.invokeinterface(LOGGER_TYPE.getInternalName(), "getFlowMessageFactory",
+ Type.getMethodDescriptor(FLOW_MESSAGE_FACTORY_TYPE));
+ for (int i = 1; i < args.length; i++) {
+ mv.load(i, args[i]);
+ }
+ final boolean isEntry = type.name().startsWith("ENTRY");
+ final String methodName = isEntry ? "newEntryMessage" : "newExitMessage";
+ mv.invokeinterface(FLOW_MESSAGE_FACTORY_TYPE.getInternalName(), methodName, Type.getMethodDescriptor(
+ isEntry ? ENTRY_MESSAGE_TYPE : EXIT_MESSAGE_TYPE, Arrays.copyOfRange(args, 1, args.length)));
+ mv.areturn(MESSAGE_TYPE);
+ mv.visitEnd();
+ }
+
+ private static void writeEntryMessageSuppliers(final InstructionAdapter mv) {
+ mv.visitCode();
+ mv.visitMaxs(3, 3);
+ mv.load(0, LOGGER_TYPE);
+ mv.invokeinterface(LOGGER_TYPE.getInternalName(), "getFlowMessageFactory",
+ Type.getMethodDescriptor(FLOW_MESSAGE_FACTORY_TYPE));
+ mv.load(1, STRING_TYPE);
+ mv.load(2, SUPPLIER_ARRAY_TYPE);
+ mv.invokestatic(LAMBDA_UTIL_TYPE.getInternalName(), "getAll",
+ Type.getMethodDescriptor(OBJECT_ARRAY_TYPE, SUPPLIER_ARRAY_TYPE), false);
+ mv.invokeinterface(FLOW_MESSAGE_FACTORY_TYPE.getInternalName(), "newEntryMessage",
+ Type.getMethodDescriptor(ENTRY_MESSAGE_TYPE, STRING_TYPE, OBJECT_ARRAY_TYPE));
+ mv.areturn(MESSAGE_TYPE);
+ mv.visitEnd();
+ }
+
+ private static String getCacheClassName(final String internalClassName) {
+ return StringUtils.substringBefore(internalClassName, '$') + Constants.LOCATION_CACHE_SUFFIX;
+ }
+
+ public static Path getCacheClassFile(final Path classFile) {
+ final Path fileName = classFile.getFileName();
+ if (fileName == null) {
+ throw new IllegalArgumentException("The 'classFile' parameter is an empty path.");
+ }
+ final String cacheFileName = LocationCacheGenerator
+ .getCacheClassName(StringUtils.removeEnd(fileName.toString(), ".class")) + ".class";
+ return classFile.resolveSibling(cacheFileName);
+ }
+
+ public static class LocationCacheValue {
+ private final String internalClassName;
+ private final String fieldName;
+ private final int index;
+
+ private LocationCacheValue(String internalClassName, String fieldName, int index) {
+ super();
+ this.internalClassName = internalClassName;
+ this.fieldName = fieldName;
+ this.index = index;
+ }
+
+ public String getInternalClassName() {
+ return internalClassName;
+ }
+
+ public Type getType() {
+ return Type.getObjectType(internalClassName);
+ }
+
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+ }
+
+ /**
+ * Describes the methods and fields of a specific location cache class.
+ *
+ */
+ private static class LocationCacheContents {
+ private final List<StackTraceElement> locations = new CopyOnWriteArrayList<>();
+ private Set<SupplierLambdaType> lambdas = EnumSet.noneOf(SupplierLambdaType.class);
+
+ public int addLocation(final String internalClassName, final String methodName, final String fileName,
+ final int lineNumber) {
+ final StackTraceElement location = new StackTraceElement(internalClassName.replaceAll("/", "."), methodName,
+ fileName, lineNumber);
+ locations.add(location);
+ return locations.indexOf(location);
+ }
+
+ public List<StackTraceElement> getLocations() {
+ return locations;
+ }
+
+ public boolean addLambda(SupplierLambdaType type) {
+ return lambdas.add(type);
+ }
+
+ public Set<SupplierLambdaType> getLambdas() {
+ return lambdas;
+ }
+ }
+
+}
diff --git a/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationClassConverter.java b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationClassConverter.java
new file mode 100644
index 0000000..52a3377
--- /dev/null
+++ b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationClassConverter.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.logging.log4j.weaver.log4j2.LogBuilderConversionHandler;
+import org.apache.logging.log4j.weaver.log4j2.LoggerConversionHandler;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+public class LocationClassConverter {
+
+ /**
+ * Adds location information to a classfile.
+ *
+ * @param src original classfile
+ * @param dest transformed classfile
+ * @param locationCache a container for location data
+ */
+ public void convert(InputStream src, OutputStream dest, LocationCacheGenerator locationCache) throws IOException {
+ final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ final LocationClassVisitor converter = new LocationClassVisitor(writer, locationCache);
+ converter.addClassConversionHandler(new LoggerConversionHandler());
+ converter.addClassConversionHandler(new LogBuilderConversionHandler());
+ new ClassReader(src).accept(converter, ClassReader.EXPAND_FRAMES);
+
+ dest.write(writer.toByteArray());
+ }
+}
diff --git a/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationClassVisitor.java b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationClassVisitor.java
new file mode 100644
index 0000000..3655678
--- /dev/null
+++ b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationClassVisitor.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.weaver.LocationCacheGenerator.LocationCacheValue;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import static org.apache.logging.log4j.weaver.Constants.*;
+
+public class LocationClassVisitor extends ClassVisitor {
+
+ private final LocationCacheGenerator locationCache;
+ private final Map<String, ClassConversionHandler> conversionHandlers;
+
+ private String fileName;
+ private String declaringClass;
+ private String methodName;
+
+ protected LocationClassVisitor(ClassVisitor cv, LocationCacheGenerator locationCache) {
+ super(Opcodes.ASM9, cv);
+ this.locationCache = locationCache;
+ this.conversionHandlers = new HashMap<>();
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ this.declaringClass = name;
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ @Override
+ public void visitSource(String source, String debug) {
+ this.fileName = source;
+ super.visitSource(source, debug);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
+ String[] exceptions) {
+ this.methodName = name;
+ final MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
+ return mv != null
+ ? new LocationMethodVisitor(this, Collections.unmodifiableMap(conversionHandlers), mv, access, name,
+ descriptor)
+ : null;
+ }
+
+ public void addClassConversionHandler(final ClassConversionHandler handler) {
+ this.conversionHandlers.put(handler.getOwner(), handler);
+ }
+
+ public LocationCacheValue addStackTraceElement(final int lineNumber) {
+ return locationCache.addLocation(declaringClass, methodName, fileName, lineNumber);
+ }
+
+ public Handle createLambda(SupplierLambdaType type) {
+ switch (type) {
+ case MESSAGE_SUPPLIER:
+ return new Handle(Opcodes.H_INVOKEINTERFACE, MESSAGE_SUPPLIER_TYPE.getInternalName(), "get",
+ Type.getMethodDescriptor(MESSAGE_TYPE), true);
+ default:
+ return locationCache.createLambda(declaringClass, type);
+ }
+ }
+}
diff --git a/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationMethodVisitor.java b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationMethodVisitor.java
new file mode 100644
index 0000000..68e9d49
--- /dev/null
+++ b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/LocationMethodVisitor.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Map;
+
+import org.apache.logging.log4j.weaver.LocationCacheGenerator.LocationCacheValue;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.GeneratorAdapter;
+
+import static org.apache.logging.log4j.weaver.Constants.LOG_BUILDER_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.MESSAGE_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.OBJECT_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.STACK_TRACE_ELEMENT_ARRAY_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.STACK_TRACE_ELEMENT_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.STRING_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.WITH_LOCATION_METHOD;
+
+public class LocationMethodVisitor extends GeneratorAdapter {
+
+ // Programmatically define LAMBDA_METAFACTORY_HANDLE
+ private static Type SUPPLIER_OF_OBJECT_TYPE = Type.getMethodType(OBJECT_TYPE);
+ private static Type SUPPLIER_OF_MESSAGE_TYPE = Type.getMethodType(MESSAGE_TYPE);
+ private static final Type LAMBDA_METAFACTORY_TYPE = Type.getType(LambdaMetafactory.class);
+ private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
+ private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class);
+ private static final String LAMBDA_METAFACTORY_DESC = Type.getMethodDescriptor(Type.getType(CallSite.class),
+ Type.getType(MethodHandles.Lookup.class), STRING_TYPE, METHOD_TYPE_TYPE, METHOD_TYPE_TYPE,
+ METHOD_HANDLE_TYPE, METHOD_TYPE_TYPE);
+ private static final Handle LAMBDA_METAFACTORY_HANDLE = new Handle(Opcodes.H_INVOKESTATIC,
+ LAMBDA_METAFACTORY_TYPE.getInternalName(), "metafactory", LAMBDA_METAFACTORY_DESC, false);
+
+ private final LocationClassVisitor locationClassVisitor;
+ private final Map<String, ClassConversionHandler> handlers;
+
+ // A pool of local variables
+ private final Integer[] localVariables = new Integer[12];
+ private final Label[] startLabels = new Label[12];
+ // Next available variable index
+ private int nextVariable = 0;
+
+ private int lineNumber;
+ private Label currentLabel;
+
+ protected LocationMethodVisitor(final LocationClassVisitor locationClassVisitor,
+ final Map<String, ClassConversionHandler> handlers, final MethodVisitor mv, final int access,
+ final String name, final String descriptor) {
+ super(Opcodes.ASM9, mv, access, name, descriptor);
+ this.locationClassVisitor = locationClassVisitor;
+ this.handlers = handlers;
+ }
+
+ @Override
+ public void visitLineNumber(int line, Label start) {
+ this.lineNumber = line;
+ super.visitLineNumber(line, start);
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
+ resetLocals();
+ final ClassConversionHandler handler = handlers.get(owner);
+ if (handler != null) {
+ handler.handleMethodInstruction(this, name, descriptor);
+ } else {
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ }
+
+ /**
+ * Assuming the top of the stack holds a {@code LogBuilder}, add location
+ * information to it.
+ */
+ public void storeLocation() {
+ final LocationCacheValue location = locationClassVisitor.addStackTraceElement(lineNumber);
+ getStatic(location.getType(), location.getFieldName(), STACK_TRACE_ELEMENT_ARRAY_TYPE);
+ push(location.getIndex());
+ arrayLoad(STACK_TRACE_ELEMENT_TYPE);
+ invokeInterface(LOG_BUILDER_TYPE, WITH_LOCATION_METHOD);
+ }
+
+ @Override
+ @SuppressFBWarnings(value = {"EI_EXPOSE_REP2"})
+ public void visitLabel(Label label) {
+ currentLabel = label;
+ super.visitLabel(label);
+ }
+
+ @Override
+ public void visitEnd() {
+ for (int i = 0; i < startLabels.length; i++) {
+ final Label label = startLabels[i];
+ if (label != null) {
+ // the generator adapter uses different variable indexes
+ // so we use 'mv' directly
+ mv.visitLocalVariable("log4j2$$p" + i, OBJECT_TYPE.getDescriptor(), null, label, currentLabel,
+ localVariables[i]);
+ }
+ }
+ super.visitEnd();
+ }
+
+ private void resetLocals() {
+ nextVariable = 0;
+ }
+
+ public int nextLocal() {
+ Integer varIndex = localVariables[nextVariable];
+ if (varIndex == null) {
+ varIndex = newLocal(OBJECT_TYPE);
+ localVariables[nextVariable] = varIndex;
+ // remember first usage of variable
+ startLabels[nextVariable] = currentLabel;
+ }
+ nextVariable++;
+ return varIndex;
+ }
+
+ public void invokeSupplierLambda(SupplierLambdaType type) {
+ invokeDynamic("get",
+ type.getInvokedMethodDescriptor(),
+ LAMBDA_METAFACTORY_HANDLE,
+ SUPPLIER_OF_OBJECT_TYPE,
+ locationClassVisitor.createLambda(type),
+ SUPPLIER_OF_MESSAGE_TYPE);
+ }
+}
diff --git a/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/SupplierLambdaType.java b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/SupplierLambdaType.java
new file mode 100644
index 0000000..1a2503a
--- /dev/null
+++ b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/SupplierLambdaType.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver;
+
+import org.objectweb.asm.Type;
+
+import static org.apache.logging.log4j.weaver.Constants.ENTRY_MESSAGE_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.LOGGER_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.MESSAGE_SUPPLIER_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.MESSAGE_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.OBJECT_ARRAY_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.OBJECT_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.STRING_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.SUPPLIER_ARRAY_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.SUPPLIER_TYPE;
+
+/**
+ * An enumeration of {@code org.apache.logging.log4j.util.Supplier<Message>} lambdas, named after the type of message and parameter types.
+ *
+ */
+public enum SupplierLambdaType {
+ MESSAGE_SUPPLIER(MESSAGE_SUPPLIER_TYPE),
+ FORMATTED_MESSAGE(STRING_TYPE, OBJECT_ARRAY_TYPE),
+ ENTRY_MESSAGE_MESSAGE(LOGGER_TYPE, MESSAGE_TYPE),
+ ENTRY_MESSAGE_STRING_OBJECTS(LOGGER_TYPE, STRING_TYPE, OBJECT_ARRAY_TYPE),
+ EXIT_MESSAGE_ENTRY_MESSAGE(LOGGER_TYPE, ENTRY_MESSAGE_TYPE),
+ EXIT_MESSAGE_MESSAGE(LOGGER_TYPE, MESSAGE_TYPE),
+ EXIT_MESSAGE_OBJECT_ENTRY_MESSAGE(LOGGER_TYPE, OBJECT_TYPE, ENTRY_MESSAGE_TYPE),
+ EXIT_MESSAGE_OBJECT_MESSAGE(LOGGER_TYPE, OBJECT_TYPE, MESSAGE_TYPE),
+ EXIT_MESSAGE_STRING_OBJECT(LOGGER_TYPE, STRING_TYPE, OBJECT_TYPE),
+ ENTRY_MESSAGE_STRING_SUPPLIERS(LOGGER_TYPE, STRING_TYPE, SUPPLIER_ARRAY_TYPE);
+
+ private final Type[] argumentTypes;
+
+ private SupplierLambdaType(final Type... argumentTypes) {
+ this.argumentTypes = argumentTypes;
+ }
+
+ /**
+ * Returns the descriptor of the invokedynamic call.
+ */
+ public String getInvokedMethodDescriptor() {
+ return Type.getMethodDescriptor(SUPPLIER_TYPE, argumentTypes);
+ }
+
+ /**
+ * Returns the descriptor of the implementation method.
+ */
+ public String getImplementationMethodDescriptor() {
+ return Type.getMethodDescriptor(MESSAGE_TYPE, argumentTypes);
+ }
+
+ public Type[] getArgumentTypes() {
+ return argumentTypes.clone();
+ }
+}
diff --git a/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderConversionHandler.java b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderConversionHandler.java
new file mode 100644
index 0000000..4bf80af
--- /dev/null
+++ b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderConversionHandler.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver.log4j2;
+
+import org.apache.logging.log4j.weaver.ClassConversionHandler;
+import org.apache.logging.log4j.weaver.LocationMethodVisitor;
+
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.Method;
+
+import static org.apache.logging.log4j.weaver.Constants.LOG_BUILDER_TYPE;
+
+public class LogBuilderConversionHandler implements ClassConversionHandler {
+
+ @Override
+ public String getOwner() {
+ return LOG_BUILDER_TYPE.getInternalName();
+ }
+
+ @Override
+ public void handleMethodInstruction(LocationMethodVisitor mv, String name, String descriptor) {
+ if ("withLocation".equals(name) && Type.getMethodDescriptor(LOG_BUILDER_TYPE).equals(descriptor)) {
+ return;
+ }
+ mv.invokeInterface(LOG_BUILDER_TYPE, new Method(name, descriptor));
+ }
+
+}
diff --git a/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/log4j2/LoggerConversionHandler.java b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/log4j2/LoggerConversionHandler.java
new file mode 100644
index 0000000..f1c42d0
--- /dev/null
+++ b/log4j-weaver/src/main/java/org/apache/logging/log4j/weaver/log4j2/LoggerConversionHandler.java
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver.log4j2;
+
+import java.util.Arrays;
+
+import org.apache.logging.log4j.weaver.ClassConversionHandler;
+import org.apache.logging.log4j.weaver.Constants;
+import org.apache.logging.log4j.weaver.ConversionException;
+import org.apache.logging.log4j.weaver.LocationMethodVisitor;
+import org.apache.logging.log4j.weaver.SupplierLambdaType;
+
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.Method;
+
+import static org.apache.logging.log4j.weaver.Constants.AT_DEBUG_METHOD;
+import static org.apache.logging.log4j.weaver.Constants.AT_ERROR_METHOD;
+import static org.apache.logging.log4j.weaver.Constants.AT_FATAL_METHOD;
+import static org.apache.logging.log4j.weaver.Constants.AT_INFO_METHOD;
+import static org.apache.logging.log4j.weaver.Constants.AT_LEVEL_METHOD;
+import static org.apache.logging.log4j.weaver.Constants.AT_TRACE_METHOD;
+import static org.apache.logging.log4j.weaver.Constants.AT_WARN_METHOD;
+import static org.apache.logging.log4j.weaver.Constants.ENTRY_MESSAGE_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.LEVEL_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.LOGGER_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.LOG_AND_GET_METHOD;
+import static org.apache.logging.log4j.weaver.Constants.LOG_BUILDER_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.MARKER_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.MESSAGE_SUPPLIER_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.MESSAGE_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.OBJECT_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.STRING_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.SUPPLIER_ARRAY_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.SUPPLIER_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.THROWABLE_TYPE;
+import static org.apache.logging.log4j.weaver.Constants.WITH_MARKER_METHOD;
+import static org.apache.logging.log4j.weaver.Constants.WITH_THROWABLE_METHOD;
+
+public class LoggerConversionHandler implements ClassConversionHandler {
+
+ private static final String CATCHING = "Catching";
+ private static final String CATCHING_MARKER = "CATCHING_MARKER";
+ private static final String ENTRY_MARKER = "ENTRY_MARKER";
+ private static final String EXIT_MARKER = "EXIT_MARKER";
+ private static final String THROWING = "Throwing";
+ private static final String THROWING_MARKER = "THROWING_MARKER";
+ // Argument list of `LogBuilder.log(String, Supplier...)`
+ private static final Type[] LOG_BUILDER_LOG_STRING_SUPPLIER = new Type[] { STRING_TYPE, SUPPLIER_ARRAY_TYPE };
+ // Argument list of `LogBuilder.log(Supplier<Message>)`
+ private static final Type[] LOG_BUILDER_LOG_SUPPLIER_MESSAGE = new Type[] { SUPPLIER_TYPE };
+ private static final Method LOG_BUILDER_LOG_SUPPLIER_METHOD = new Method("log",
+ Type.getMethodDescriptor(Type.VOID_TYPE, LOG_BUILDER_LOG_SUPPLIER_MESSAGE));
+ private static final Method LOG_BUILDER_LOG_STRING_METHOD = new Method("log",
+ Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE));
+ private static final Type ABSTRACT_LOGGER_TYPE = Type.getObjectType("org/apache/logging/log4j/spi/AbstractLogger");
+ private static final Type[] MESSAGE_OBJECT_ARRAY = { MESSAGE_TYPE, OBJECT_TYPE };
+
+ @Override
+ public String getOwner() {
+ return Constants.LOGGER_TYPE.getInternalName();
+ }
+
+ private void createLogBuilder(LocationMethodVisitor mv, String methodName) {
+ final Method method;
+ switch (methodName) {
+ case "debug":
+ method = AT_DEBUG_METHOD;
+ break;
+ case "error":
+ method = AT_ERROR_METHOD;
+ break;
+ case "fatal":
+ method = AT_FATAL_METHOD;
+ break;
+ case "info":
+ method = AT_INFO_METHOD;
+ break;
+ case "log":
+ method = AT_LEVEL_METHOD;
+ break;
+ case "trace":
+ method = AT_TRACE_METHOD;
+ break;
+ case "warn":
+ method = AT_WARN_METHOD;
+ break;
+ default:
+ throw new ConversionException("Unknown logging method " + methodName);
+ }
+ mv.invokeInterface(LOGGER_TYPE, method);
+ }
+
+ @Override
+ public void handleMethodInstruction(LocationMethodVisitor mv, String name, String descriptor) {
+ switch (name) {
+ case "debug":
+ case "error":
+ case "fatal":
+ case "info":
+ case "log":
+ case "trace":
+ case "warn":
+ handleLogMethods(mv, name, descriptor);
+ break;
+ case "printf":
+ handlePrintfMethods(mv, descriptor);
+ break;
+ case "always":
+ case "atDebug":
+ case "atError":
+ case "atFatal":
+ case "atInfo":
+ case "atLevel":
+ case "atTrace":
+ case "atWarn":
+ mv.invokeInterface(LOGGER_TYPE, new Method(name, descriptor));
+ mv.storeLocation();
+ break;
+ case "catching":
+ case "throwing":
+ handleCatchingThrowing(mv, descriptor, "throwing".equals(name));
+ break;
+ case "isDebugEnabled":
+ case "isEnabled":
+ case "isErrorEnabled":
+ case "isFatalEnabled":
+ case "isInfoEnabled":
+ case "isTraceEnabled":
+ case "isWarnEnabled":
+ case "logMessage":
+ // These are NOPs
+ mv.invokeInterface(LOGGER_TYPE, new Method(name, descriptor));
+ break;
+ case "traceEntry":
+ handleTraceEntry(mv, descriptor);
+ break;
+ case "traceExit":
+ handleTraceExit(mv, descriptor);
+ break;
+ default:
+ throw new ConversionException("Unsupported method 'org.apache.logging.log4j.Logger#" + name + "'.");
+ }
+ }
+
+ /**
+ * Rewrites the most common methods: {@code log} and its level specializations.
+ */
+ private void handleLogMethods(LocationMethodVisitor mv, String name, String descriptor) {
+ final Type[] types = Type.getArgumentTypes(descriptor);
+ final int[] varIndexes = new int[types.length];
+ int from = types.length > 0 && types[0].equals(LEVEL_TYPE) ? 1 : 0;
+ int to = types.length;
+ // Store arguments to local variables
+ // TODO: most of the time there is a more efficient way
+ for (int i = to - 1; i >= from; i--) {
+ varIndexes[i] = mv.nextLocal();
+ mv.storeLocal(varIndexes[i], types[i]);
+ }
+ // create the LogBuilder
+ createLogBuilder(mv, name);
+ mv.storeLocation();
+ // Marker argument
+ if (from < to && types[from].equals(MARKER_TYPE)) {
+ mv.loadLocal(varIndexes[from], MARKER_TYPE);
+ mv.invokeInterface(LOG_BUILDER_TYPE, WITH_MARKER_METHOD);
+ from++;
+ }
+ // Throwable argument
+ if (from < to && types[to - 1].equals(THROWABLE_TYPE)) {
+ mv.loadLocal(varIndexes[to - 1], THROWABLE_TYPE);
+ mv.invokeInterface(LOG_BUILDER_TYPE, WITH_THROWABLE_METHOD);
+ to--;
+ }
+ // Call log(...)
+ final Type[] arguments;
+ // We need to replace (Supplier<?>) with ("{}", Supplier<?>)
+ if (SUPPLIER_TYPE.equals(types[from])) {
+ mv.push("{}");
+ mv.push(1);
+ mv.newArray(SUPPLIER_TYPE);
+ mv.dup();
+ mv.push(0);
+ mv.loadLocal(varIndexes[from], types[from]);
+ mv.arrayStore(SUPPLIER_TYPE);
+ arguments = LOG_BUILDER_LOG_STRING_SUPPLIER;
+ // We need to convert MessageSupplier to Supplier<Message>
+ } else if (MESSAGE_SUPPLIER_TYPE.equals(types[from])) {
+ mv.loadLocal(varIndexes[from], types[from]);
+ mv.invokeSupplierLambda(SupplierLambdaType.MESSAGE_SUPPLIER);
+ arguments = LOG_BUILDER_LOG_SUPPLIER_MESSAGE;
+ } else {
+ for (int i = from; i < to; i++) {
+ mv.loadLocal(varIndexes[i], types[i]);
+ }
+ arguments = Arrays.copyOfRange(types, from, to);
+ }
+ final Method logMethod = new Method("log", Type.VOID_TYPE, arguments);
+ mv.invokeInterface(LOG_BUILDER_TYPE, logMethod);
+ }
+
+ private void handlePrintfMethods(LocationMethodVisitor mv, String descriptor) {
+ final Type[] types = Type.getArgumentTypes(descriptor);
+ // Transform the last two arguments into a supplier
+ mv.invokeSupplierLambda(SupplierLambdaType.FORMATTED_MESSAGE);
+ int supplierIndex = mv.nextLocal();
+ mv.storeLocal(supplierIndex, SUPPLIER_TYPE);
+ int markerIndex = -1;
+ if (types[1].equals(MARKER_TYPE)) {
+ markerIndex = mv.nextLocal();
+ mv.storeLocal(markerIndex, MARKER_TYPE);
+ }
+ mv.invokeInterface(LOGGER_TYPE, AT_LEVEL_METHOD);
+ mv.storeLocation();
+ if (markerIndex >= 0) {
+ mv.loadLocal(markerIndex, MARKER_TYPE);
+ mv.invokeInterface(LOG_BUILDER_TYPE, WITH_MARKER_METHOD);
+ }
+ mv.loadLocal(supplierIndex, SUPPLIER_TYPE);
+ mv.invokeInterface(LOG_BUILDER_TYPE, LOG_BUILDER_LOG_SUPPLIER_METHOD);
+ }
+
+ private void handleCatchingThrowing(LocationMethodVisitor mv, String descriptor, boolean throwing) {
+ final boolean hasLevel = Type.getArgumentTypes(descriptor).length > 1;
+ final int throwableIndex = mv.nextLocal();
+ mv.storeLocal(throwableIndex, THROWABLE_TYPE);
+ if (hasLevel) {
+ mv.invokeInterface(LOGGER_TYPE, AT_LEVEL_METHOD);
+ } else {
+ mv.invokeInterface(LOGGER_TYPE, AT_ERROR_METHOD);
+ }
+ mv.storeLocation();
+ mv.loadLocal(throwableIndex, THROWABLE_TYPE);
+ mv.invokeInterface(LOG_BUILDER_TYPE, WITH_THROWABLE_METHOD);
+ mv.getStatic(ABSTRACT_LOGGER_TYPE, throwing ? THROWING_MARKER : CATCHING_MARKER, MARKER_TYPE);
+ mv.invokeInterface(LOG_BUILDER_TYPE, WITH_MARKER_METHOD);
+ mv.push(throwing ? THROWING : CATCHING);
+ mv.invokeInterface(LOG_BUILDER_TYPE, LOG_BUILDER_LOG_STRING_METHOD);
+ if (throwing) {
+ mv.loadLocal(throwableIndex, THROWABLE_TYPE);
+ }
+ }
+
+ private void handleTraceEntry(LocationMethodVisitor mv, String descriptor) {
+ final Type[] types = Type.getArgumentTypes(descriptor);
+ final int[] vars = new int[types.length];
+ for (int i = vars.length - 1; i >= 0; i--) {
+ vars[i] = mv.nextLocal();
+ mv.storeLocal(vars[i]);
+ }
+ // only Logger on stack
+ mv.dup();
+ final int loggerIdx = mv.nextLocal();
+ mv.storeLocal(loggerIdx, LOGGER_TYPE);
+ mv.invokeInterface(LOGGER_TYPE, AT_TRACE_METHOD);
+ mv.storeLocation();
+ mv.getStatic(ABSTRACT_LOGGER_TYPE, ENTRY_MARKER, MARKER_TYPE);
+ mv.invokeInterface(LOG_BUILDER_TYPE, WITH_MARKER_METHOD);
+ mv.loadLocal(loggerIdx, LOGGER_TYPE);
+ if (types.length == 0) {
+ mv.push((String) null);
+ mv.push((String) null);
+ mv.invokeSupplierLambda(SupplierLambdaType.ENTRY_MESSAGE_STRING_OBJECTS);
+ } else if (types[0].equals(MESSAGE_TYPE)) {
+ mv.loadLocal(vars[0]);
+ mv.invokeSupplierLambda(SupplierLambdaType.ENTRY_MESSAGE_MESSAGE);
+ } else {
+ if (types.length == 1) {
+ mv.push((String) null);
+ }
+ for (int i = 0; i < vars.length; i++) {
+ mv.loadLocal(vars[i]);
+ }
+ final boolean usesSuppliers = types[types.length - 1].equals(SUPPLIER_ARRAY_TYPE);
+ mv.invokeSupplierLambda(usesSuppliers ? SupplierLambdaType.ENTRY_MESSAGE_STRING_SUPPLIERS
+ : SupplierLambdaType.ENTRY_MESSAGE_STRING_OBJECTS);
+ }
+ mv.invokeInterface(LOG_BUILDER_TYPE, LOG_AND_GET_METHOD);
+ }
+
+ private void handleTraceExit(LocationMethodVisitor mv, String descriptor) {
+ final Type[] types = Type.getArgumentTypes(descriptor);
+ final int[] vars = new int[types.length];
+ for (int i = vars.length - 1; i >= 0; i--) {
+ vars[i] = mv.nextLocal();
+ mv.storeLocal(vars[i]);
+ }
+ // only Logger on stack
+ mv.dup();
+ final int loggerIdx = mv.nextLocal();
+ mv.storeLocal(loggerIdx, LOGGER_TYPE);
+ mv.invokeInterface(LOGGER_TYPE, AT_TRACE_METHOD);
+ mv.storeLocation();
+ mv.getStatic(ABSTRACT_LOGGER_TYPE, EXIT_MARKER, MARKER_TYPE);
+ mv.invokeInterface(LOG_BUILDER_TYPE, WITH_MARKER_METHOD);
+ mv.loadLocal(loggerIdx, LOGGER_TYPE);
+ if (types.length == 0) {
+ mv.push((String) null);
+ mv.push((String) null);
+ mv.invokeSupplierLambda(SupplierLambdaType.EXIT_MESSAGE_STRING_OBJECT);
+ } else if (Arrays.deepEquals(types, MESSAGE_OBJECT_ARRAY)) {
+ // Invert arguments
+ mv.loadLocal(vars[1]);
+ mv.loadLocal(vars[0]);
+ mv.invokeSupplierLambda(SupplierLambdaType.EXIT_MESSAGE_OBJECT_MESSAGE);
+ } else if (ENTRY_MESSAGE_TYPE.equals(types[0])) {
+ final boolean hasResult = types.length == 2;
+ if (hasResult) {
+ mv.loadLocal(vars[1]);
+ }
+ mv.loadLocal(vars[0]);
+ mv.invokeSupplierLambda(hasResult ? SupplierLambdaType.EXIT_MESSAGE_OBJECT_ENTRY_MESSAGE
+ : SupplierLambdaType.EXIT_MESSAGE_ENTRY_MESSAGE);
+ } else {
+ final boolean hasFormat = STRING_TYPE.equals(types[0]);
+ if (hasFormat) {
+ mv.loadLocal(vars[0]);
+ } else {
+ mv.push((String) null);
+ }
+ mv.loadLocal(vars[hasFormat ? 1 : 0], OBJECT_TYPE);
+ mv.invokeSupplierLambda(SupplierLambdaType.EXIT_MESSAGE_STRING_OBJECT);
+ }
+ mv.invokeInterface(LOG_BUILDER_TYPE, LOG_BUILDER_LOG_SUPPLIER_METHOD);
+ // except void methods traceExit() and traceExit(EntryMessage)
+ if (types.length != 0 && (types.length > 1 || !ENTRY_MESSAGE_TYPE.equals(types[0]))) {
+ mv.loadLocal(vars[vars.length - 1]);
+ }
+ }
+}
diff --git a/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/AbstractConversionHandlerTest.java b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/AbstractConversionHandlerTest.java
new file mode 100644
index 0000000..59f4e7b
--- /dev/null
+++ b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/AbstractConversionHandlerTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+public class AbstractConversionHandlerTest {
+
+ protected static Class<?> convertedClass;
+ protected static Object testObject;
+
+ protected static void transformClass(String internalName) throws Exception {
+ final TestClassLoader testCl = new TestClassLoader();
+
+ final ByteArrayOutputStream dest = new ByteArrayOutputStream();
+ final LocationClassConverter converter = new LocationClassConverter();
+ final LocationCacheGenerator locationCache = new LocationCacheGenerator();
+
+ getNestedClasses(internalName).forEach(classFile -> assertDoesNotThrow(() -> {
+ dest.reset();
+ converter.convert(Files.newInputStream(classFile), dest, locationCache);
+ testCl.defineClass(dest.toByteArray());
+ }));
+ locationCache.generateClasses().values().forEach(testCl::defineClass);
+ convertedClass = testCl.loadClass(internalName.replaceAll("/", "."));
+ testObject = assertDoesNotThrow(() -> convertedClass.getConstructor().newInstance());
+ }
+
+ private static Stream<Path> getNestedClasses(String internalName) throws Exception {
+ final Path topClass = Paths
+ .get(AbstractConversionHandlerTest.class.getClassLoader().getResource(internalName + ".class").toURI());
+ final String simpleClassName = Paths.get(internalName).getFileName().toString();
+ return Files.walk(topClass.getParent(), 1).filter(p -> {
+ final String nested = p.getFileName().toString();
+ return nested.startsWith(simpleClassName) && nested.endsWith(".class");
+ }).sorted();
+ }
+
+ private static class TestClassLoader extends ClassLoader {
+
+ public TestClassLoader() {
+ super(AbstractConversionHandlerTest.class.getClassLoader());
+ }
+
+ public Class<?> defineClass(byte[] bytes) {
+ return defineClass(null, bytes, 0, bytes.length);
+ }
+ }
+}
diff --git a/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/NoLoggingExample.java b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/NoLoggingExample.java
new file mode 100644
index 0000000..a1c9994
--- /dev/null
+++ b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/NoLoggingExample.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver;
+
+public class NoLoggingExample {
+
+ public String greet() {
+ return "Hello Log4j2!";
+ }
+}
diff --git a/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/NoLoggingTest.java b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/NoLoggingTest.java
new file mode 100644
index 0000000..0ebc7d9
--- /dev/null
+++ b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/NoLoggingTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver;
+
+import java.lang.reflect.Method;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test a class that needs no conversion at all.
+ */
+public class NoLoggingTest extends AbstractConversionHandlerTest {
+
+ @BeforeAll
+ public static void setup() throws Exception {
+ transformClass("org/apache/logging/log4j/weaver/NoLoggingExample");
+ }
+
+ @Test
+ public void testGreet() throws Exception {
+ final Method greetMethod = convertedClass.getDeclaredMethod("greet");
+ final Object message = greetMethod.invoke(testObject);
+ assertThat(message).isEqualTo("Hello Log4j2!");
+ }
+}
diff --git a/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderConversionHandlerExample.java b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderConversionHandlerExample.java
new file mode 100644
index 0000000..0bf9cbd
--- /dev/null
+++ b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderConversionHandlerExample.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver.log4j2;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.logging.log4j.LogBuilder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LogBuilderConversionHandlerExample {
+
+ public void testWithLocation() {
+ // We can't use verify: if `withLocation` is correctly remove, so is
+ // verify(...).withLocation()
+ final AtomicBoolean called = new AtomicBoolean();
+ final AtomicReference<StackTraceElement> locationRef = new AtomicReference<>();
+ final LogBuilder logBuilder = new LogBuilderMock(locationRef, called);
+ // We remove the call without parameters
+ logBuilder.withLocation().log();
+ assertThat(called).isFalse();
+
+ final StackTraceElement stackTraceElement = new StackTraceElement(
+ LogBuilderConversionHandlerExample.class.getName(), "specialMethod",
+ "LogBuilderConversionHandlerExample.java", 1024);
+ logBuilder.withLocation(stackTraceElement).log();
+ assertThat(locationRef).hasValue(stackTraceElement);
+ }
+
+}
diff --git a/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderConversionHandlerTest.java b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderConversionHandlerTest.java
new file mode 100644
index 0000000..92059bd
--- /dev/null
+++ b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderConversionHandlerTest.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver.log4j2;
+
+import org.apache.logging.log4j.weaver.AbstractConversionHandlerTest;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class LogBuilderConversionHandlerTest extends AbstractConversionHandlerTest {
+
+ @BeforeAll
+ public static void setup() throws Exception {
+ transformClass("org/apache/logging/log4j/weaver/log4j2/LogBuilderConversionHandlerExample");
+ }
+
+ @Test
+ public void testWithLocation() throws Exception {
+ convertedClass.getMethod("testWithLocation").invoke(testObject);
+ }
+
+}
diff --git a/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderMock.java b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderMock.java
new file mode 100644
index 0000000..25e0b46
--- /dev/null
+++ b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LogBuilderMock.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver.log4j2;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.logging.log4j.LogBuilder;
+
+public final class LogBuilderMock implements LogBuilder {
+ private final AtomicReference<StackTraceElement> locationRef;
+ private final AtomicBoolean called;
+
+ public LogBuilderMock(AtomicReference<StackTraceElement> locationRef, AtomicBoolean called) {
+ this.locationRef = locationRef;
+ this.called = called;
+ }
+
+ @Override
+ public LogBuilder withLocation() {
+ called.set(true);
+ return LogBuilder.super.withLocation();
+ }
+
+ @Override
+ public LogBuilder withLocation(StackTraceElement location) {
+ locationRef.set(location);
+ return LogBuilder.super.withLocation(location);
+ }
+}
diff --git a/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LoggerConversionHandlerExample.java b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LoggerConversionHandlerExample.java
new file mode 100644
index 0000000..eef7cc5
--- /dev/null
+++ b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LoggerConversionHandlerExample.java
@@ -0,0 +1,943 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver.log4j2;
+
+import java.util.List;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.MarkerManager;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.message.DefaultFlowMessageFactory;
+import org.apache.logging.log4j.message.EntryMessage;
+import org.apache.logging.log4j.message.ExitMessage;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.AbstractLogger;
+import org.apache.logging.log4j.util.Supplier;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class LoggerConversionHandlerExample {
+
+ private static final CharSequence CHAR_SEQUENCE = "(CharSequence)";
+ private static final Message MESSAGE = new SimpleMessage("(Message)");
+ private static final EntryMessage ENTRY_MESSAGE = new DefaultFlowMessageFactory().newEntryMessage(MESSAGE);
+ private static final String STRING = "(String)";
+ private static final Object OBJECT = "(Object)";
+ private static final Object P0 = "(p0)";
+ private static final Object P1 = "(p1)";
+ private static final Object P2 = "(p2)";
+ private static final Object P3 = "(p3)";
+ private static final Object P4 = "(p4)";
+ private static final Object P5 = "(p5)";
+ private static final Object P6 = "(p6)";
+ private static final Object P7 = "(p7)";
+ private static final Object P8 = "(p8)";
+ private static final Object P9 = "(p9)";
+ private static final Object[] PARRAY = { "(...)" };
+ private static final Supplier<?>[] SUPPLIERS = { () -> OBJECT };
+ private static final Throwable THROWABLE = new RuntimeException();
+ private static final Marker MARKER = MarkerManager.getMarker("MARKER");
+
+ private static final Logger logger = LogManager.getLogger();
+
+ private static final int referenceLine = 63;
+
+ public void testFatal(final ListAppender app) {
+ app.clear();
+ final String methodName = "testFatal";
+ int lineNumber = referenceLine + 5; // current line number
+ logger.fatal(CHAR_SEQUENCE);
+ assertLocationEquals(methodName, ++lineNumber, app);
+ logger.fatal(CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, CHAR_SEQUENCE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(() -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(() -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, () -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, () -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(() -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(() -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, () -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, () -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.fatal(MARKER, STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ }
+
+ public void testError(final ListAppender app) {
+ app.clear();
+ final String methodName = "testError";
+ int lineNumber = referenceLine + 103; // current line number
+ logger.error(CHAR_SEQUENCE);
+ assertLocationEquals(methodName, ++lineNumber, app);
+ logger.error(CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, CHAR_SEQUENCE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(() -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(() -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, () -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, () -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(() -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(() -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, () -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, () -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.error(MARKER, STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ }
+
+ public void testWarn(final ListAppender app) {
+ app.clear();
+ final String methodName = "testWarn";
+ int lineNumber = referenceLine + 201; // current line number
+ logger.warn(CHAR_SEQUENCE);
+ assertLocationEquals(methodName, ++lineNumber, app);
+ logger.warn(CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, CHAR_SEQUENCE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(() -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(() -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, () -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, () -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(() -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(() -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, () -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, () -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.warn(MARKER, STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ }
+
+ public void testInfo(final ListAppender app) {
+ app.clear();
+ final String methodName = "testInfo";
+ int lineNumber = referenceLine + 299; // current line number
+ logger.info(CHAR_SEQUENCE);
+ assertLocationEquals(methodName, ++lineNumber, app);
+ logger.info(CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, CHAR_SEQUENCE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(() -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(() -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, () -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, () -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(() -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(() -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, () -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, () -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.info(MARKER, STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ }
+
+ public void testDebug(final ListAppender app) {
+ app.clear();
+ final String methodName = "testDebug";
+ int lineNumber = referenceLine + 397; // current line number
+ logger.debug(CHAR_SEQUENCE);
+ assertLocationEquals(methodName, ++lineNumber, app);
+ logger.debug(CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, CHAR_SEQUENCE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(() -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(() -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, () -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, () -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(() -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(() -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, () -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, () -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.debug(MARKER, STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ }
+
+ public void testTrace(final ListAppender app) {
+ app.clear();
+ final String methodName = "testTrace";
+ int lineNumber = referenceLine + 495; // current line number
+ logger.trace(CHAR_SEQUENCE);
+ assertLocationEquals(methodName, ++lineNumber, app);
+ logger.trace(CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, CHAR_SEQUENCE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(() -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(() -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, () -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, () -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(() -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(() -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, () -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, () -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.trace(MARKER, STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ }
+
+ public void testLog(final ListAppender app) {
+ app.clear();
+ final String methodName = "testLog";
+ int lineNumber = referenceLine + 593; // current line number
+ logger.log(Level.INFO, CHAR_SEQUENCE);
+ assertLocationEquals(methodName, ++lineNumber, app);
+ logger.log(Level.INFO, CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, CHAR_SEQUENCE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, CHAR_SEQUENCE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, P0);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, P0, P1);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, P0, P1, P2);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, P0, P1, P2, P3);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, P0, P1, P2, P3, P4);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, P0, P1, P2, P3, P4, P5);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, P0, P1, P2, P3, P4, P5, P6);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, PARRAY);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, () -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, () -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, () -> MESSAGE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, () -> MESSAGE, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, () -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, () -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, () -> OBJECT);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, () -> OBJECT, THROWABLE);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.log(Level.INFO, MARKER, STRING, SUPPLIERS);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ }
+
+ public void testFrames(final ListAppender app) {
+ app.clear();
+ final String methodName = "testFrames";
+ final int lineNumber = referenceLine + 691; // Current line
+ int i = 0;
+ while (i < 2) {
+ if (i < 1) {
+ logger.debug(OBJECT);
+ assertLocationEquals(methodName, lineNumber + 4, app);
+ } else {
+ logger.debug(OBJECT);
+ assertLocationEquals(methodName, lineNumber + 7, app);
+ }
+ logger.debug(OBJECT);
+ assertLocationEquals(methodName, lineNumber + 10, app);
+ i++;
+ }
+ switch (i) {
+ case 2:
+ logger.debug(OBJECT);
+ assertLocationEquals(methodName, lineNumber + 16, app);
+ break;
+ }
+ for (; i >= 0; i--) {
+ logger.debug(OBJECT);
+ assertLocationEquals(methodName, lineNumber + 21, app);
+ }
+ }
+
+ public void testPrintf(final ListAppender app) {
+ app.clear();
+ final String methodName = "testPrintf";
+ int lineNumber = referenceLine + 720; // Current line
+ logger.printf(Level.INFO, "Hello %s.%s", "LoggerConversionHandlerExample", methodName);
+ assertLocationEquals(methodName, ++lineNumber, app);
+ logger.printf(Level.INFO, MARKER, "Hello %s.%s", "LoggerConversionHandlerExample", methodName);
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ }
+
+ public void testLogBuilder(final ListAppender app) {
+ app.clear();
+ final String methodName = "testLogBuilder";
+ int lineNumber = referenceLine + 730; // Current line
+ logger.always().log();
+ assertLocationEquals(methodName, ++lineNumber, app);
+ logger.atDebug().log();
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.atError().log();
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.atFatal().log();
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.atInfo().log();
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.atLevel(Level.INFO).log();
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.atTrace().log();
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ logger.atWarn().log();
+ assertLocationEquals(methodName, lineNumber += 2, app);
+ }
+
+ /**
+ * Tests the method calls that should be not modified.
+ */
+ public void testPassthrough(final ListAppender app) {
+ assertTrue(logger.isDebugEnabled());
+ assertTrue(logger.isDebugEnabled(MARKER));
+ assertTrue(logger.isEnabled(Level.INFO));
+ assertTrue(logger.isEnabled(Level.INFO, MARKER));
+ assertTrue(logger.isErrorEnabled());
+ assertTrue(logger.isErrorEnabled(MARKER));
+ assertTrue(logger.isFatalEnabled());
+ assertTrue(logger.isFatalEnabled(MARKER));
+ assertTrue(logger.isInfoEnabled());
+ assertTrue(logger.isInfoEnabled(MARKER));
+ assertTrue(logger.isTraceEnabled());
+ assertTrue(logger.isTraceEnabled(MARKER));
+ assertTrue(logger.isWarnEnabled());
+ assertTrue(logger.isWarnEnabled(MARKER));
+ app.clear();
+ logger.logMessage(Level.INFO, MARKER, LoggerConversionHandlerExample.class.getName(), null, MESSAGE, THROWABLE);
+ assertThat(app.getEvents()).hasSize(1);
+ }
+
+ public void testCatchingThrowing(final ListAppender app) {
+ app.clear();
+ final String methodName = "testCatchingThrowing";
+ int lineNumber = referenceLine + 775; // Current line
+ logger.catching(THROWABLE);
+ assertThat(app.getEvents()).allMatch(event -> AbstractLogger.CATCHING_MARKER.equals(event.getMarker()));
+ assertLocationEquals(methodName, ++lineNumber, app);
+ logger.catching(Level.INFO, THROWABLE);
+ assertThat(app.getEvents()).allMatch(event -> AbstractLogger.CATCHING_MARKER.equals(event.getMarker()));
+ assertLocationEquals(methodName, lineNumber += 3, app);
+ assertThat(logger.throwing(THROWABLE)).isInstanceOf(RuntimeException.class);
+ assertThat(app.getEvents()).allMatch(event -> AbstractLogger.THROWING_MARKER.equals(event.getMarker()));
+ assertLocationEquals(methodName, lineNumber += 3, app);
+ assertThat(logger.throwing(THROWABLE)).isInstanceOf(RuntimeException.class);
+ assertThat(app.getEvents()).allMatch(event -> AbstractLogger.THROWING_MARKER.equals(event.getMarker()));
+ assertLocationEquals(methodName, lineNumber += 3, app);
+ }
+
+ public void testTraceEntry(final ListAppender app) {
+ app.clear();
+ final String methodName = "testTraceEntry";
+ int lineNumber = referenceLine + 793; // Current line
+ EntryMessage entryMessage = logger.traceEntry();
+ LogEvent event = assertLocationEquals(methodName, ++lineNumber, app);
+ assertThat(event.getMarker()).isEqualTo(AbstractLogger.ENTRY_MARKER);
+ assertThat(entryMessage.getMessage()).isNull();
+
+ entryMessage = logger.traceEntry(MESSAGE);
+ event = assertLocationEquals(methodName, lineNumber += 5, app);
+ assertThat(event.getMarker()).isEqualTo(AbstractLogger.ENTRY_MARKER);
+ assertThat(entryMessage.getMessage()).isEqualTo(MESSAGE);
+
+ entryMessage = logger.traceEntry("param1 = {}", PARRAY);
+ event = assertLocationEquals(methodName, lineNumber += 5, app);
+ assertThat(event.getMarker()).isEqualTo(AbstractLogger.ENTRY_MARKER);
+ assertThat(entryMessage.getFormattedMessage()).isEqualTo("Enter param1 = (...)");
+
+ entryMessage = logger.traceEntry(() -> P0, () -> P1);
+ event = assertLocationEquals(methodName, lineNumber += 5, app);
+ assertThat(event.getMarker()).isEqualTo(AbstractLogger.ENTRY_MARKER);
+ assertThat(entryMessage.getFormattedMessage()).isEqualTo("Enter params((p0), (p1))");
+
+ entryMessage = logger.traceEntry("param1 = {}, param2 = {}", () -> P0, () -> P1);
+ event = assertLocationEquals(methodName, lineNumber += 5, app);
+ assertThat(event.getMarker()).isEqualTo(AbstractLogger.ENTRY_MARKER);
+ assertThat(entryMessage.getFormattedMessage()).isEqualTo("Enter param1 = (p0), param2 = (p1)");
+ }
+
+ public void testTraceExit(final ListAppender app) {
+ app.clear();
+ final String methodName = "testTraceExit";
+ int lineNumber = referenceLine + 823; // Current line
+ logger.traceExit();
+ LogEvent event = assertLocationEquals(methodName, ++lineNumber, app);
+ assertThat(event.getMarker()).isEqualTo(AbstractLogger.EXIT_MARKER);
+ assertThat(event.getMessage()).isInstanceOf(ExitMessage.class);
+ assertThat(event.getMessage().getFormattedMessage()).isEqualTo("Exit");
+
+ logger.traceExit(ENTRY_MESSAGE);
+ event = assertLocationEquals(methodName, lineNumber += 6, app);
+ assertThat(event.getMarker()).isEqualTo(AbstractLogger.EXIT_MARKER);
+ assertThat(event.getMessage()).isInstanceOf(ExitMessage.class);
+ assertThat(event.getMessage().getFormattedMessage()).isEqualTo("Exit (Message)");
+
+ Object result = logger.traceExit(OBJECT);
+ assertThat(result).isSameAs(OBJECT);
+ event = assertLocationEquals(methodName, lineNumber += 6, app);
+ assertThat(event.getMarker()).isEqualTo(AbstractLogger.EXIT_MARKER);
+ assertThat(event.getMessage()).isInstanceOf(ExitMessage.class);
+ assertThat(event.getMessage().getFormattedMessage()).isEqualTo("Exit with((Object))");
+
+ result = logger.traceExit("result = {}", OBJECT);
+ assertThat(result).isSameAs(OBJECT);
+ event = assertLocationEquals(methodName, lineNumber += 7, app);
+ assertThat(event.getMarker()).isEqualTo(AbstractLogger.EXIT_MARKER);
+ assertThat(event.getMessage()).isInstanceOf(ExitMessage.class);
+ assertThat(event.getMessage().getFormattedMessage()).isEqualTo("Exit result = (Object)");
+
+ result = logger.traceExit(MESSAGE, OBJECT);
+ assertThat(result).isSameAs(OBJECT);
+ event = assertLocationEquals(methodName, lineNumber += 7, app);
+ assertThat(event.getMarker()).isEqualTo(AbstractLogger.EXIT_MARKER);
+ assertThat(event.getMessage()).isInstanceOf(ExitMessage.class);
+ assertThat(event.getMessage().getFormattedMessage()).isEqualTo("Exit (Message): (Object)");
+
+ result = logger.traceExit(ENTRY_MESSAGE, OBJECT);
+ assertThat(result).isSameAs(OBJECT);
+ event = assertLocationEquals(methodName, lineNumber += 7, app);
+ assertThat(event.getMarker()).isEqualTo(AbstractLogger.EXIT_MARKER);
+ assertThat(event.getMessage()).isInstanceOf(ExitMessage.class);
+ assertThat(event.getMessage().getFormattedMessage()).isEqualTo("Exit (Message): (Object)");
+ }
+
+ private static LogEvent assertLocationEquals(final String methodName, final int lineNumber,
+ final ListAppender app) {
+ final List<LogEvent> events = app.getEvents();
+ assertThat(events).hasSize(1);
+ final LogEvent event = events.get(0);
+ assertThat(event.isIncludeLocation()).isFalse();
+ assertThat(event.getSource()).isNotNull();
+ final StackTraceElement location = event.getSource();
+ assertThat(location.getClassName()).isEqualTo(LoggerConversionHandlerExample.class.getName());
+ assertThat(location.getMethodName()).isEqualTo(methodName);
+ assertThat(location.getFileName()).isEqualTo("LoggerConversionHandlerExample.java");
+ assertThat(location.getLineNumber()).isEqualTo(lineNumber);
+ app.clear();
+ return event;
+ }
+}
diff --git a/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LoggerConversionHandlerTest.java b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LoggerConversionHandlerTest.java
new file mode 100644
index 0000000..3e146bb
--- /dev/null
+++ b/log4j-weaver/src/test/java/org/apache/logging/log4j/weaver/log4j2/LoggerConversionHandlerTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.weaver.log4j2;
+
+import java.util.stream.Stream;
+
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.core.test.junit.Named;
+import org.apache.logging.log4j.weaver.AbstractConversionHandlerTest;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+@LoggerContextSource("log4j2-test.xml")
+public class LoggerConversionHandlerTest extends AbstractConversionHandlerTest {
+
+ private ListAppender appender;
+
+ @BeforeAll
+ public static void setup() throws Exception {
+ transformClass("org/apache/logging/log4j/weaver/log4j2/LoggerConversionHandlerExample");
+ }
+
+ @BeforeEach
+ public void setupAppender(final @Named("List") ListAppender appender) {
+ this.appender = appender;
+ }
+
+ static Stream<String> testLocationConverter() {
+ return Stream.of("testFatal", "testError", "testWarn", "testInfo", "testDebug", "testLog", "testFrames",
+ "testPrintf", "testLogBuilder", "testPassthrough", "testCatchingThrowing", "testTraceEntry",
+ "testTraceExit");
+ }
+
+ @ParameterizedTest
+ @MethodSource
+ public void testLocationConverter(final String methodName) throws Exception {
+ convertedClass.getMethod(methodName, ListAppender.class).invoke(testObject, appender);
+ }
+
+}
diff --git a/log4j-weaver/src/test/resources/log4j2-test.xml b/log4j-weaver/src/test/resources/log4j2-test.xml
new file mode 100644
index 0000000..3d3e84c
--- /dev/null
+++ b/log4j-weaver/src/test/resources/log4j2-test.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache license, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the license for the specific language governing permissions and
+ ~ limitations under the license.
+ -->
+<Configuration status="off">
+ <Appenders>
+ <List name="List" />
+ </Appenders>
+ <Loggers>
+ <Root level="trace" includeLocation="false">
+ <AppenderRef ref="List" />
+ </Root>
+ </Loggers>
+</Configuration>
diff --git a/pom.xml b/pom.xml
index c228083..b1cb6e8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -132,6 +132,8 @@
<modules>
<!-- the parent POM must come first: -->
<module>log4j-transform-parent</module>
+ <module>log4j-transform-maven-plugin</module>
+ <module>log4j-weaver</module>
</modules>
<scm>
<connection>scm:git:git@github.com:apache/logging-log4j-transform.git</connection>
@@ -168,7 +170,20 @@
<sign-maven-plugin.version>1.0.1</sign-maven-plugin.version>
<spotless-maven-plugin.version>2.29.0</spotless-maven-plugin.version>
</properties>
- <dependencyManagement/>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-transform-maven-plugin</artifactId>
+ <version>${revision}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-weaver</artifactId>
+ <version>${revision}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
<build>
<pluginManagement>
<plugins>
@@ -196,7 +211,6 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
- <version>${flatten-maven-plugin.version}</version>
<inherited>false</inherited>
<executions>
<execution>
@@ -244,9 +258,9 @@
<pom>
<sortPom>
<expandEmptyElements>false</expandEmptyElements>
- <keepBlankLines>false</keepBlankLines>
+ <keepBlankLines>true</keepBlankLines>
<indentSchemaLocation>true</indentSchemaLocation>
- <sortDependencies>artifactId,groupId</sortDependencies>
+ <sortDependencies>scope,artifactId,groupId</sortDependencies>
<sortDependencyExclusions>artifactId,groupId</sortDependencyExclusions>
<sortPlugins>artifactId,groupId</sortPlugins>
</sortPom>
diff --git a/src/changelog/.0.1.x/LOG4J2-3638_Provide_Maven_plugin_to_inline_location.xml b/src/changelog/.0.1.x/LOG4J2-3638_Provide_Maven_plugin_to_inline_location.xml
new file mode 100644
index 0000000..d449168
--- /dev/null
+++ b/src/changelog/.0.1.x/LOG4J2-3638_Provide_Maven_plugin_to_inline_location.xml
@@ -0,0 +1,7 @@
+<entry type="added">
+ <issue id="LOG4J2-3638" link="https://issues.apache.org/jira/browse/LOG4J2-3638"/>
+ <author id="pkarwasz"/>
+ <description format="asciidoc">
+ Add bytecode transformation tool to provide location information without reflection.
+ </description>
+</entry>