【问题标题】:Log4j2: How can I get Class Name and Line Number without using Throwable?Log4j2:如何在不使用 Throwable 的情况下获取类名和行号?
【发布时间】:2015-03-19 04:42:09
【问题描述】:

我在 Log4j2 上开发了一个包装类。使用 OSGi 的声明性服务,我使用我自己的记录器接口发布了一个自定义记录器服务,其中包装器类是实现。包装类仅用于以编程方式配置记录器,消息格式化和添加更多方法,最后它调用 Log4j2 的记录方法。

我想打印日志文件中请求的每个日志的源类/文件名和行号。选项 %C/%F 和 %L 仅打印有关包装类中我实际调用 log 方法的位置的信息。

因此,作为一种锻炼,我每次都将 new Throwable 作为参数传递,以便我可以使用布局 %throwable{short.lineNumber}。但这对于嵌入式应用程序来说是一个昂贵的过程。

我的主要问题是获取行号,因为对于文件名,我至少可以从 Log4j2 请求一个新的记录器,其中包含每个请求记录器服务的服务的名称,并将其保存在地图中。

有没有办法追溯调用者?我希望对于您不想在记录器服务的每个使用者上都有 LOG4j2 jar 的应用程序有类似的解决方案。仅供参考,我不想使用任何 XML 文件,所有配置都是以编程方式进行的。

【问题讨论】:

  • 有趣的问题。也许看看 log4j 的源代码,看看他们是如何做到的。
  • Log4j 还使用堆栈跟踪来识别这些信息。

标签: java logging log4j log4j2


【解决方案1】:

你可以使用

StackTraceElement[] stes = Thread.currentThread().getStackTrace();

但是我不确定这是否便宜。

我所做的是使每条消息都是唯一的(对于班级而言)并避免包含行号。您可以在 IDE 中搜索唯一消息以查找行号。该类应以记录器的名称命名。

【讨论】:

  • 嗨彼得,谢谢你的回答。我还没有看到其他替代方案,所以我认为从每个日志上的包装类调用堆栈跟踪可能是当前的解决方案(如果我正确理解你的答案)。由于项目要求,我需要行号。但是Log4j2只接受一个消息参数,除了连接要记录的消息中的字符串之外,您对如何传递提取的信息有什么建议吗?
【解决方案2】:

Log4j2 在内部遍历堆栈跟踪以提取位置信息。它通过为org.apache.logging.log4j.spi.ExtendedLogger#logMessage 方法指定正确的 FQCN(完全限定的类名)来知道在堆栈跟踪中停止的位置。

Log4j2 包含一个为记录器包装器生成代码的工具。文档在这里(在自定义日志级别手册页中): http://logging.apache.org/log4j/2.x/manual/customloglevels.html#CustomLoggers

尝试使用此工具生成记录器包装器,并将您的自定义包装器基于生成的代码。此生成的代码将使用正确的 FQCN,并且能够生成正确的位置信息。

【讨论】:

    【解决方案3】:

    正如@Remko Popma 所说,这是一个简单的实现:
    首先创建您的 ExtendedLogger 类:

    import org.apache.logging.log4j.Level;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.apache.logging.log4j.spi.AbstractLogger;
    import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;
    
    public class ExtLogWithLine extends ExtendedLoggerWrapper {
        private static final long serialVersionUID = 8239280349129059055L;
    
        // define your wrapper class here
        private static final String FQCN = WrapperLog.class.getName();
    
        private final ExtendedLoggerWrapper logger;
    
        private ExtLogWithLine(final Logger logger) {
            super((AbstractLogger) logger, logger.getName(), logger.getMessageFactory());
            this.logger = this;
        }
    
        public static ExtLogWithLine create(final String name) {
            final Logger wrapped = LogManager.getLogger(name);
            return new ExtLogWithLine(wrapped);
        }
    
        @Override
        public void debug(String info) {
            if (isDebugEnabled()) {
                logger.logIfEnabled(FQCN, Level.DEBUG, null, info, (Throwable) null);
            }
        }
    
        @Override
        public void info(String info) {
            if (isInfoEnabled()) {
                logger.logIfEnabled(FQCN, Level.INFO, null, info, (Throwable) null);
            }
        }
    
        @Override
        public void warn(String info) {
            if (isWarnEnabled()) {
                logger.logIfEnabled(FQCN, Level.WARN, null, info, (Throwable) null);
            }
        }
    
        @Override
        public void error(String info) {
            if (isErrorEnabled()) {
                logger.logIfEnabled(FQCN, Level.ERROR, null, info, (Throwable) null);
            }
        }
    }
    

    然后使用扩展的记录器打印日志:
    public class WrapperLog {
    
        public void testLog() {
           ExtLogWithLine logger = ExtLogWithLine.create("xxx");
           
           // log will print out with correct line number
           logger.info("see the line number");
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-19
      • 2018-08-27
      • 2011-02-11
      • 1970-01-01
      • 2023-04-05
      • 2018-05-31
      • 1970-01-01
      相关资源
      最近更新 更多