| // |
| // 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. |
| // |
| = 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 https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/LogBuilder.html[`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 xref:../log4j-transform-perf/src/main/java/org/apache/logging/log4j/transform/perf/LocationBenchmark.java[`LocationBenchmark`] on a Ryzen 7 2700U laptop with Java 17 gives the following results: |
| |
| [cols="1,1,1,1,1,1,1"] |
| |=== |
| |Logging interface|Sync/async logger|No. threads|Precomputed location|Score|Error|Units |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/LogBuilder.html[`LogBuilder`] |
| |sync |
| |1 |
| |yes |
| |202343,624 |
| |±719,875 |
| |ops/s |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/LogBuilder.html[`LogBuilder`] |
| |sync |
| |1 |
| |no |
| |68449,813 |
| |±5086,148 |
| |ops/s |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html[`Logger`] |
| |sync |
| |1 |
| |yes |
| |202579,793 |
| |±547,961 |
| |ops/s |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html[`Logger`] |
| |sync |
| |1 |
| |no |
| |100105,246 |
| |±13748,554 |
| |ops/s |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html[`Logger`] |
| |async |
| |8 |
| |yes |
| |726877,012 |
| |±38214,575 |
| |ops/s |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html[`Logger`] |
| |async |
| |8 |
| |no |
| |440245,135 |
| |±4849,946 |
| |ops/s |
| |
| |=== |
| |
| The figures show a performance bump of around 5 µs per logging statement when |
| https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html[`Logger`] |
| is used and a bump of around 9 µs per logging statement when |
| https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/LogBuilder.html[`LogBuilder`] |
| is used. |
| |
| By comparison, *disabling* location information on the same machine gives: |
| |
| [cols="1,1,1,1,1,1,1"] |
| |=== |
| |Logging interface|Sync/async logger|No. threads|Precomputed location|Score|Error|Units |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/LogBuilder.html[`LogBuilder`] |
| |sync |
| |1 |
| |yes |
| |234666,556 |
| |±19759,779 |
| |ops/s |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/LogBuilder.html[`LogBuilder`] |
| |sync |
| |1 |
| |no |
| |212562,315 |
| |±3631,670 |
| |ops/s |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html[`Logger`] |
| |sync |
| |1 |
| |yes |
| |210751,730 |
| |±1508,148 |
| |ops/s |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html[`Logger`] |
| |sync |
| |1 |
| |no |
| |220837,404 |
| |±13248,184 |
| |ops/s |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html[`Logger`] |
| |async |
| |8 |
| |yes |
| |743467,533 |
| |±38046,044 |
| |ops/s |
| |
| |https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html[`Logger`] |
| |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 |
| [source,java] |
| ---- |
| public void helloLog() { |
| logger.info(MarkerManager.getMarker("NET"), "Sending {} bytes of data.", 1000); |
| } |
| ---- |
| is rewritten at a bytecode level into an equivalent `LogBuilder` call: |
| [source,java] |
| ---- |
| 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: |
| |
| xref:src/doc/process-classes-mojo.adoc[`log4j-transform:process-classes`]:: |
| is bound to the process-classes phase and weaves your classes to include precomputed location information. |
| |
| == Usage |
| |
| To use the plugin you need to declare a dependency on https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api/2.20.0[`log4j-api`] version 2.20.0 or newer. |
| |
| Add the following configuration to your POM file: |
| |
| [source,xml] |
| ---- |
| <project> |
| [...] |
| <build> |
| [...] |
| <plugins> |
| <plugin> |
| <groupId>org.apache.logging.log4j</groupId> |
| <artifactId>log4j-transform-maven-plugin</artifactId> |
| <version>0.1.0-SNAPSHOT</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>process-classes</goal> |
| </goals> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| [...] |
| </build> |
| [...] |
| </project> |
| ---- |