Java日志记录:Log4j Version2.x:显示最终客户端调用方法(不是中间日志记录助手方法)

以下3篇文章提供了如何使用中间日志记录助手的答案,并且仍然让底层记录器从客户端的方法报告给该日志记录助手(而不是将日志记录助手方法报告为源):

  • Java日志记录:显示调用者的源行号(而不是日志记录助手方法)

  • 间接调用log4j的日志方法(来自辅助方法)

  • 使用log4j包装器在日志语句中打印“source”类

但似乎只提供Log4j 1.2的答案,它提供了现在已经不存在的:

Category.log(String callerFQCN, Priority level, Object message, Throwable t). 

log4J 2.5 API中的Logger似乎没有明显的等价物。

任何人都可以提供与直接使用Log4J 2.x兼容的答案吗?

对于Log4j2,答案完全通过使用logger包装器提供,如Log4j2手册中生成的Logger Wrapper的示例用法中所述 。 可以简单地生成(使用其中所示的org.apache.logging.log4j.core.tools.Generate $ ExtendedLogger工具)具有单个STUB级别的记录器包装器,然后调整它以创建模仿logIfEnabled使用的自定义日志记录方法(FQCN,LEVEL,Marker,message,Throwable) – 可能忽略STUB级别并使用常规级别 – 然后根据需要删除或注释掉STUB级别及其方法)。 为此,FormattedMessage可能会有所帮助。

例:

 java -cp log4j-core-2.5.jar org.apache.logging.log4j.core.tools.Generate\$ExtendedLogger com.mycomp.ExtLogger STUB=350 > com/mycomp/ExtLogger.java 

然后调整生成的类(省略大多数支持方法):

 public final class ExtLogger extends ExtendedLoggerWrapper { ... private final ExtendedLoggerWrapper logger; private static final String FQCN = ExtLogger.class.getName(); private static final Level STUB = Level.forName("STUB", 350); //Delete this afterwards if level not used. private ExtLogger(final Logger logger) { super((AbstractLogger) logger, logger.getName(), logger.getMessageFactory()); this.logger = this; } /** * Returns a custom Logger with the name of the calling class. * * @return The custom Logger for the calling class. */ public static ExtLogger create() { final Logger wrapped = LogManager.getLogger(); return new ExtLogger(wrapped); } /** * Returns a custom Logger using the fully qualified name of the Class as * the Logger name. * * @param loggerName The Class whose name should be used as the Logger name. * If null it will default to the calling class. * @return The custom Logger. */ public static ExtLogger create(final Class loggerName) { final Logger wrapped = LogManager.getLogger(loggerName); return new ExtLogger(wrapped); } ... /** * Logs a message object with the {@code STUB} level. * * @param message the message object to log. */ public void stub(final String message) { logger.logIfEnabled(FQCN, STUB, null, message, (Throwable) null); } /** * Example: Adapt with custom formatting. * Here DEBUG level is used just as an example. * * @param name * @param value */ public void echo(final String name, Object value) { Message m = new FormattedMessage("echo: %s(%s)",name,value); logger.logIfEnabled(FQCN, Level.DEBUG, null, m, (Throwable) null); } ... } 

然后在客户端类中,它现在将通过记录器的帮助器方法正确地“代表”该客户端,在这种情况下,格式化示例echo(name,value):

 public class TestLog4j { private static final ExtLogger extLogger = ExtLogger.create(TestLog4j.class); public static void elseWhere() { extLogger.echo("aVariableName", 4); } public static void main(String[] args) { extLogger.echo("aStringVariableName","from main"); elseWhere(); } } 

Simple PatternLayout:

   

输出:

  DEBUG [TestLog4j::main(63)] testlogging.TestLog4j - echo: aStringVariableName(from main) DEBUG [TestLog4j::elseWhere(42)] testlogging.TestLog4j - echo: aVariableName(4) 

一旦你对使用fQCN(log4j在堆栈跟踪中搜索)使用logger.logIfEnabled(FQCN,…),你可能希望删除或注释掉stub(..)方法和STUB级别如果你不使用额外的级别。

如果您正在构建一个新系统,请坚持使用Webel的答案。

如果你有一个现有的系统,你正在迁移到log4j2,你应该仍然可以运行generate方法(但我在下面包含一个最小的工作类),你可以添加这个提供旧的1.2 callerFQCN服务方式的函数( ):

 public void log(Class ignoreClassFQCN, Level level, Marker marker, String msg, Throwable throwable){ logger.logIfEnabled(ignoreClassFQCN.getName(), level, marker, msg, throwable); } 

然后,从现有的日志包装类中,您可以执行以下操作:

 // inside ThisClass.java, denoting an old logger such as one that used log4j 1.2 with the callerFQCN parameter, or just an old logger that's naive and custom built. private static final MyLog4j2WrapperClass newLogger = MyLog4j2WrapperClass.create(); public static void debug(String message) { // basic example newLogger.log(ThisClass.class, Level.DEBUG, null, message, null); } public static void logFailure(String message) { // example of using a custom log level newLogger.log(ThisClass.class, MyLog4j2WrapperClass.FAILURE, null, message, null); } 

我碰巧删除了一些我没有使用的其他生成的函数,因为我打算为一个设计糟糕的(自定义)日志系统做一个简单的垫片。 我删除了一堆我不打算使用的create()方法。 所以这是一个工人阶级(值得分享的部分):

 public final class MyLog4j2WrapperClass extends ExtendedLoggerWrapper { private static final long serialVersionUID = 1L; private final ExtendedLoggerWrapper logger; private static final String FQCN = MyLog4j2WrapperClass.class.getName(); public static final Level FAILURE = Level.forName("FAILURE", 150); public void log(Class ignoreClass, Level level, Marker marker, String msg, Throwable throwable){ logger.logIfEnabled(ignoreClass.getName(), level, marker, msg, throwable); } private MyLog4j2WrapperClass(final Logger logger) { super((AbstractLogger) logger, logger.getName(), logger.getMessageFactory()); this.logger = this; } /** * Returns a custom Logger with the name of the calling class. * * @return The custom Logger for the calling class. */ public static MyLog4j2WrapperClass create() { final Logger wrapped = LogManager.getLogger(); return new MyLog4j2WrapperClass(wrapped); } 

我应该注意到,我认为这是一个垫片 – 它可以帮助你,但是我强烈建议你在垫片到位后继续远离旧的测井系统。 这将使您可以进行更多增量代码更改,而无需立即完成完整迁移。