https://github.com/PhilKes/slf4j-caller-info-plugins
Maven plugin to inject caller location information into all SLF4J log statements without performance loss
https://github.com/PhilKes/slf4j-caller-info-plugins
asm caller-location log4j logback logger maven-plugin performance slf4j
Last synced: 8 months ago
JSON representation
Maven plugin to inject caller location information into all SLF4J log statements without performance loss
- Host: GitHub
- URL: https://github.com/PhilKes/slf4j-caller-info-plugins
- Owner: PhilKes
- License: apache-2.0
- Created: 2023-01-16T10:09:55.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2025-09-18T05:24:08.000Z (9 months ago)
- Last Synced: 2025-10-10T14:42:35.130Z (8 months ago)
- Topics: asm, caller-location, log4j, logback, logger, maven-plugin, performance, slf4j
- Language: Java
- Homepage:
- Size: 1.29 MB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 13
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# slf4j-caller-info-maven-plugin
[](https://maven-badges.herokuapp.com/maven-central/io.github.philkes/slf4j-caller-info-maven-plugin)
[](https://snyk.io/test/github/PhilKes/slf4j-caller-info-maven-plugin)
[](./LICENSE)
Maven plugin to **inject caller-location-information** to all [SLF4J Logger](https://www.slf4j.org/api/org/slf4j/Logger.html) log statement invocations (`info()`, etc.) in your compiled code, as a better alternative to SLF4J caller location evaluation during runtime. Also allows to inject caller-information when using wrapper classes/methods (see [Configuration/injectedMethods](#configuration)).
## Description
By default SLF4J implementations such as `logback` or `log4j` offer to log the caller-location (e.g. [Logback/Layouts#method](https://logback.qos.ch/manual/layouts.html#method)), but this comes at a huge performance loss (see [Apache/Log4j2-Performance-Caller-Location](https://logging.apache.org/log4j/2.x/performance.html#asynchronous-logging-with-caller-location-information)).
Instead of evaluating the caller-location (method, source code line number) during runtime, this plugin injects the caller-location info to all log statements when building the project.
The injection is done with a [MDC.put(...)](https://www.slf4j.org/api/org/slf4j/MDC.html#put-java.lang.String-java.lang.String-) call before every SLF4J log invocation, putting the class name, line number (optionally also method name) into the MDC in the compiled `.class` files. This allows to conveniently **print out where exactly in the code the log statement originates from** for every single log statement, without any overhead or performance loss, by simply adding the **Mapped Diagnostic Context** ([MDC](https://logback.qos.ch/manual/mdc.html)) parameter `callerInformation` to your logging-pattern configuration. It can therefore be used with any SLF4J implementation, such as [logback](https://logback.qos.ch/), [log4j2](https://logging.apache.org/log4j/2.x/), etc.
Since this plugin adds the necessary code during the build stage, there is **nearly no performance loss** by injecting the caller-location-information in comparison to using e.g. the `%class` or `%line` pattern parameters (see [Log4j2 manual](https://logging.apache.org/log4j/2.x/manual/layouts.html#Patterns) or [Logback manual](https://logback.qos.ch/manual/layouts.html#class) in your logging pattern, which look for the caller-information on the stacktrace during runtime which is very costly.
## Usage
Add the plugin to your `pom.xml`:
```xml
io.github.philkes
slf4j-caller-info-maven-plugin
1.1.0
inject
```
*Note: JDK 8 or higher required*
The plugin is executed in the `process-classes` phase or can be explicitly run with:
```shell
mvn slf4j-caller-info:inject
```
_Note: The `inject` goal is idempotent_
## Code Example
See [logback.xml](./src/it/projects/logback/src/test/resources/logback.xml):
```xml
...
%d{HH:mm:ss.SSS} %-5level %logger{36} \(%X{callerInformation}\) - %msg%n
...
```
See [LoggingTest.java](./src/it/projects/logback/src/main/java/io/github/philkes/slf4j/callerinfo/LoggingTest.java):
```java
1 package io.github.philkes.slf4j.callerinfo;
2
3 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory;
5
6 /**
7 * Example Class using an SLF4J Logger on different Levels
8 */
9 public class LoggingTest {
10 private final Logger log = LoggerFactory.getLogger(LoggingTest.class);
11
12 public void log(String msg) {
13 log.info(msg);
14 log.warn(msg);
15 log.error(msg);
16 log.debug(msg);
17 log.trace(msg);
18 }
19 }
```
Log Output of `LoggingTest.log("This is a test message")`:

*(Screenshot from IntelliJ, automatically detects class + linenumber as links to jump directly into the source code)*
## Performance at runtime

As you can see using the `slf4j-caller-info-maven-plugin` (orange bars) printing the caller location is about **4x faster** than using Log4j2's or Logback's caller-location built-in pattern (red bars). In total there is a performance loss of only ~9% for Log4j2 and ~8% for Logback in comparison to not logging the caller-location at all (blue bars).
System specs for the benchmarks:
JDK 17 on Linux Mint 20.3 with 8 cores CPU AMD Ryzen 2700X@3.7Ghz
The benchmarking was done with [JMH](https://github.com/openjdk/jmh) based on log4j's [log4j-perf](https://github.com/apache/logging-log4j2) module.
For more details about the benchmarks see the [benchmark](./benchmark/) module.
### Performance at compiletime
As for the time it takes the `inject` goal to execute, the compilation time of the plugin is evaluated by generating Java projects with n classes, that all contain 5 SLF4J log-statements and running the plugin on these projects and averaging the plugin's execution time.

We can see that the plugin's execution time obviously goes up the more classes and `SLF4J` log statements there are in the source code,
but even for a project with 10,000 classes the compilation time is ~2 seconds, which is only about twice as long as with only 1 class.
### Configuration
There are several parameters you can overwrite:
```xml
%class:%line
callerInformation
.*
org/slf4j/Logger#info
org/slf4j/Logger#warn
org/slf4j/Logger#error
org/slf4j/Logger#debug
org/slf4j/Logger#trace
false
${project.build.outputDirectory}
```
### Compiled .class File of Code Example
```java
// import ...
public class LoggingTest {
//...
public void log(String msg) {
Logger var10000 = this.log;
MDC.put("callerInformation", "LoggingTest.class:13");
var10000.info(msg);
MDC.remove("callerInformation");
var10000 = this.log;
MDC.put("callerInformation", "LoggingTest.class:14");
var10000.warn(msg);
...
}
}
```
## Dependencies
- [ASM](https://asm.ow2.io/) for Java bytecode manipulation
- [Apache Commons IO](https://commons.apache.org/proper/commons-io/) for FileUtils
- Built with Java 17
- [JMH](https://github.com/openjdk/jmh) for benchmarks
This project is licensed under the terms of the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.txt).