Log4j Transform Maven Plugin
The Transform Plugin is used to postprocess the compiled classes of your project and replace all Log4j 2.x API calls with LogBuilder
calls with a statically precomputed location.
This allows you to use location information in your logs without incurring in the expensive runtime calls usually used to acquire it.
Why do we need it
Finding the location of a logging call is a very expensive operation (a couple of microseconds).
Running LocationBenchmark
on a Ryzen 7 2700U laptop with Java 17 gives the following results:
Logging interface | Sync/async logger | No. threads | Precomputed location | Score | Error | Units |
---|---|---|---|---|---|---|
sync |
1 |
yes |
202343,624 |
±719,875 |
ops/s |
|
sync |
1 |
no |
68449,813 |
±5086,148 |
ops/s |
|
sync |
1 |
yes |
202579,793 |
±547,961 |
ops/s |
|
sync |
1 |
no |
100105,246 |
±13748,554 |
ops/s |
|
async |
8 |
yes |
726877,012 |
±38214,575 |
ops/s |
|
async |
8 |
no |
440245,135 |
±4849,946 |
ops/s |
The figures show a performance bump of around 5 µs per logging statement when
Logger
is used and a bump of around 9 µs per logging statement when
LogBuilder
is used.
By comparison, disabling location information on the same machine gives:
Logging interface | Sync/async logger | No. threads | Precomputed location | Score | Error | Units |
---|---|---|---|---|---|---|
sync |
1 |
yes |
234666,556 |
±19759,779 |
ops/s |
|
sync |
1 |
no |
212562,315 |
±3631,670 |
ops/s |
|
sync |
1 |
yes |
210751,730 |
±1508,148 |
ops/s |
|
sync |
1 |
no |
220837,404 |
±13248,184 |
ops/s |
|
async |
8 |
yes |
743467,533 |
±38046,044 |
ops/s |
|
async |
8 |
no |
776778,635 |
±38878,794 |
ops/s |
How it works
The working principle is very simple: every call to the Log4j 2.x API like
public void helloLog() {
logger.info(MarkerManager.getMarker("NET"), "Sending {} bytes of data.", 1000);
}
is rewritten at a bytecode level into an equivalent LogBuilder
call:
private static final StackTraceElement[] locations = {
new StackTraceElement("org.apache.logging.log4j.HelloWorld", "HelloWorld.java", "helloLog", 1234)
};
public void helloLog() {
logger.atInfo()
.withLocation(locations[0])
.withMarker(MarkerManager.getMarker("NET"))
.log("Sending {} bytes of data.", 1000);
}
In the current implementation locations are stored in classes whose name ends in $$Log4j2$$Cache
, so they can not accidentally be used by XML/JSON serializers.
Goals
This plugin consists of a single goal:
log4j-transform:process-classes
-
is bound to the process-classes phase and weaves your classes to include precomputed location information.
log4j-transform:process-classes
- Full name
-
org.apache.logging.log4j:log4j-transform-maven-plugin:0.2.0:process-classes
- Description
-
Generates static location information of Log4j 2.x API calls in the project classes. The resulting bytecode will not rely on runtime resolution of the location information.
- Attributes
-
-
Requires a Maven project to be executed
-
Requires dependency resolution of artifacts in scope:
<code>compile</code>
-
The goal is thread-safe and supports parallel builds
-
Binds by default to the lifecycle phase:
process-classes
.
-
Required Parameters
Name | Type | Description |
---|---|---|
|
|
The directory containing classes to be processed.
It defaults to |
|
|
The directory where woven classes will be written.
It defaults to |
Optional Parameters
Name | Type | Description |
---|---|---|
|
|
Files to include. If empty all class files will be processed. |
|
|
Files to exclude. |
|
|
Sets the granularity in milliseconds of the last modification date for testing if a class file needs weaving.
It defaults to |
Usage
To use the plugin you need to declare a dependency on log4j-api
version 2.20.0 or newer.
Add the following configuration to your POM file:
<plugin>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-transform-maven-plugin</artifactId>
<version>0.2.0</version>
<executions>
<execution>
<goals>
<goal>process-classes</goal>
</goals>
</execution>
</executions>
</plugin>