如何使用 Java 在日志文件中写入调用者位置信息,而不影响性能?

发布于 2024-09-16 07:31:04 字数 179 浏览 0 评论 0 原文

如何使用 Java 和 log4j 在日志文件中写入调用者位置信息(Java 源文件和行),而不影响性能? log4j允许您在日志文件中写入此类信息,但它使用堆栈跟踪来获取该信息,每次发出日志语句时,这会导致性能下降。 我正在寻找一种性能友好的替代方案,例如在编译时而不是在运行时获取位置信息。可以使用注释来实现这一点吗?或者也许还有其他技术?

How can I write caller location information (Java source file and line), in a log file using Java and log4j, but without hurting performance?
log4j allow you to write such information in the log file, but it uses the stack trace to get that it information, every time a log statement is issued, what causes performance degradation.
I'm looking for a performance friendly alternative, like getting the location information at compile time instead at runtime. It is possible to use annotations to accomplish that? Or maybe some other technique?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

空心↖ 2024-09-23 07:31:04

如何将其作为构建过程的一部分来替换某些占位符,例如此代码段中的 $filename$$linenumber$

logger.info("The original message... $filename$ $linenumber$");

要替换文件名,用您的版本控制系统进行关键字替换可能就足够了。免责声明:这只是我的想法,我自己从未尝试过。

How about making it part of the build process to replace certain placeholders, like $filename$ and $linenumber$ in this snippet.

logger.info("The original message... $filename$ $linenumber$");

To replace the filename, it may suffice to have keyword substitution with your revision control system. Disclaimer: this is just from the top of my head, I never tried it myself.

め可乐爱微笑 2024-09-23 07:31:04

对于任何寻求更新解决方案(如果您将 log4j/logback 与 slf4j 结合使用)的人,我编写了一个 ma​​ven-plugin 来解决此问题,同时也不会产生任何性能问题:
https://github.com/PhilKes/slf4j-caller-info-maven-插件

该插件在编译时将类名、方法名称和源代码行号注入到所有 SLF4J 日志语句中(请参阅配置)。
您需要做的就是将 callerInformation MDC 参数添加到您的日志模式中,并将插件添加到您的 pom.xml 中:

<plugin>
 <groupId>io.github.philkes</groupId>
 <artifactId>slf4j-caller-info-maven-plugin</artifactId>
 <version>1.1.0</version>
 <executions>
   <execution>
     <goals>
        <goal>inject</goal>
     </goals>
   </execution>
 </executions>
</plugin>

For anyone looking for a more current solution if you use log4j/logback with slf4j, I wrote a maven-plugin addressing this issue, while not generating any performance issues either:
https://github.com/PhilKes/slf4j-caller-info-maven-plugin

This plugin injects the class name, optionally also the method name and source code line number into all SLF4J log statements at compile time (see configuration).
All you need to do is to add the callerInformation MDC parameter to your log-pattern and add the plugin in your pom.xml:

<plugin>
 <groupId>io.github.philkes</groupId>
 <artifactId>slf4j-caller-info-maven-plugin</artifactId>
 <version>1.1.0</version>
 <executions>
   <execution>
     <goals>
        <goal>inject</goal>
     </goals>
   </execution>
 </executions>
</plugin>
妄司 2024-09-23 07:31:04

我同意罗布的观点,一般来说这是没有必要的。通常,日志消息中会有一些不同的字符串,搜索它即可找到源头。有了一个好的 IDE,这确实很快。

现在,鉴于问题所在,这是一个可能的解决方案:

Class Foo
    void bar()
        new Logger(){} . warn("blah");

对于运行时的每个日志操作,都会创建一个新对象 - 这不是问题。

对于包含此类日志语句的每一行源代码,都会创建一个新类。那可能太多了。

魔法是这样运作的:

abstract public class Logger
    static Map<Class, String> sourceInfo = new ...
    public Logger()
        Class thisClass = this.getClass();
        String info = sourceInfo.get(thisClass);
        if(info==null)
             info = ... // init from stack trace
             sourceInfo.put(thisClass,info)
        this.info = info

     public void warn(msg)
        log(WARN, this.info,msg)

I agree with Rob that it is generally unnecessary. Usually there's some distinct string in a log message, searching for it will get to the source. With a good IDE this is really fast.

Now, given the question as is, this is a possible solution:

Class Foo
    void bar()
        new Logger(){} . warn("blah");

for each log action at runtime, a new object is created - that's not a problem.

for each line of source containing such log statement, a new class is created. that can be too many.

here's how the magic works:

abstract public class Logger
    static Map<Class, String> sourceInfo = new ...
    public Logger()
        Class thisClass = this.getClass();
        String info = sourceInfo.get(thisClass);
        if(info==null)
             info = ... // init from stack trace
             sourceInfo.put(thisClass,info)
        this.info = info

     public void warn(msg)
        log(WARN, this.info,msg)
风蛊 2024-09-23 07:31:04

如果您正在寻找 Phil 的 SLF4J 插件 的 Log4j API 对应项,从 Log4j API 2.10 开始,就有一个 LogBuilder#withLocation 方法,允许工具将位置信息注入字节码。

最近我们发布了 log4j-maven-转换插件,后处理类文件并替换传统的日志记录调用,例如:

public void helloLog() {
    logger.info(MarkerManager.getMarker("NET"), "Sending {} bytes of data.", 1000);
}

到带有注入位置的LogBuilder调用中:

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);
}

使用起来非常简单,您只需要添加:

      <plugin>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-transform-maven-plugin</artifactId>
        <version>0.1.0</version>
        <executions>
          <execution>
            <goals>
              <goal>process-classes</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

到你的 Maven 插件。

If you are looking for a Log4j API counterpart to Phil's SLF4J plugin, since Log4j API 2.10 there is a LogBuilder#withLocation method that allows tools to inject location information into byte-code.

Recently we published log4j-maven-transform-plugin that postprocesses class file and replaces classical logging calls like:

public void helloLog() {
    logger.info(MarkerManager.getMarker("NET"), "Sending {} bytes of data.", 1000);
}

into a LogBuilder call with injected location:

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);
}

It is pretty straightforward to use, you just need to add:

      <plugin>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-transform-maven-plugin</artifactId>
        <version>0.1.0</version>
        <executions>
          <execution>
            <goals>
              <goal>process-classes</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

to your Maven plugins.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文