Log4j2 RollingFile Appender – 在每个日志文件的开头添加自定义信息

在java 1.7中使用log4j2(beta9)。

我的完整log4j2.xml:

   Tts %d %-5p [%t] %C{2} (%F:%L) - %m%n %d %highlight{%-5p}{FATAL=bright red, ERROR=red, WARN=yellow, INFO=cyan, DEBUG=green, TRACE=bright blue} %style{[%t] %C{2} (%F:%L) -}{bright,black} %m%n Log/${projectPrefix}.log Log/${projectPrefix}-%i.log                     

我想在每个日志文件的顶部添加一些自定义信息,例如我的应用程序的版本字符串,应用程序正常运行时间和系统正常运行时间。 甚至在刚刚关闭的日志文件的底部写一些“bye,bye / eof”也没关系。

当RollingFileAppander创建了一个新文件时,是否有像钩子或回调这样的东西得到通知,以便我可以将我的东西放在这些新的日志文件(或任何其他建议)中?

好的,通过像这里描述的那样扩展DefaultRolloverStrategy,可以解决这个问题。 但

  • 它需要大约150行代码(包括包装RolloverDescription和appender.rolling.helper.Action)和
  • 闻起来有点因为需要完全复制DefaultRolloverStrategy的工厂方法(使得这个解决方案维护不友好,例如,如果DefaultRolloverStrategy在将来的版本中获得更多的配置参数)

要让log4j2调用我们的工厂方法,log4j2.xml的root标记必须与我们类的包一起归属,例如:

  ...  

在我们自己的RolloverStrategy中,我们必须处理@Plugin@PluginFactory ,如此处所述。

最后在这里我完整的log4j2.xml(你不需要所有的属性 – 这就是我喜欢配置我的日志记录的方式):

    Tts %d %-5p [%t] %C{2} (%F:%L) - %m%n %d %highlight{%-5p}{FATAL=bright red, ERROR=red, WARN=yellow, INFO=cyan, DEBUG=green, TRACE=bright blue} %style{[%t] %C{2} (%F:%L) -}{bright,black} %m%n %d %highlight{%-5p}{FATAL=bright red, ERROR=red, WARN=yellow, INFO=cyan, DEBUG=green, TRACE=bright blue} %style{[%t] -}{bright,black} %m%n Log/${projectPrefix}.log Log/${projectPrefix}-%i.log                     

在这里MyRolloverStrategy.java:

 package de.jme.toolbox.logging; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.zip.Deflater; import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy; import org.apache.logging.log4j.core.appender.rolling.RollingFileManager; import org.apache.logging.log4j.core.appender.rolling.RolloverDescription; import org.apache.logging.log4j.core.appender.rolling.helper.Action; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.plugins.PluginAttribute; import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.helpers.Integers; import org.apache.logging.log4j.core.lookup.StrSubstitutor; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.status.StatusLogger; /** * Own RolloverStrategy to hook the DefaultRolloverStrategy's rollover events * * Siehe auch: * - https://issues.apache.org/jira/browse/LOG4J2-486 * - http://apache-logging.6191.n7.nabble.com/log4j2-getting-started-amp-rolling-files-tt8406.html#a42402 * - http://stackoverflow.com/questions/20819376/log4j2-rollingfile-appender-add-custom-info-at-the-start-of-each-logfile * * @author Joe Merten */ @org.apache.logging.log4j.core.config.plugins.Plugin(name="MyRolloverStrategy", category="Core", printObject=true) public class MyRolloverStrategy extends DefaultRolloverStrategy { protected static final Logger logger = StatusLogger.getLogger(); // ============================== // ↓↓↓ Some stuff copied from ↓↓↓ // https://svn.apache.org/repos/asf/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java r1556050 // Just changed »DefaultRolloverStrategy« to »MyRolloverStrategy« private static final int MIN_WINDOW_SIZE = 1; private static final int DEFAULT_WINDOW_SIZE = 7; @PluginFactory public static MyRolloverStrategy createStrategy( @PluginAttribute("max") final String max, @PluginAttribute("min") final String min, @PluginAttribute("fileIndex") final String fileIndex, @PluginAttribute("compressionLevel") final String compressionLevelStr, @PluginConfiguration final Configuration config) { final boolean useMax = fileIndex == null ? true : fileIndex.equalsIgnoreCase("max"); int minIndex; if (min != null) { minIndex = Integer.parseInt(min); if (minIndex < 1) { LOGGER.error("Minimum window size too small. Limited to " + MIN_WINDOW_SIZE); minIndex = MIN_WINDOW_SIZE; } } else { minIndex = MIN_WINDOW_SIZE; } int maxIndex; if (max != null) { maxIndex = Integer.parseInt(max); if (maxIndex < minIndex) { maxIndex = minIndex < DEFAULT_WINDOW_SIZE ? DEFAULT_WINDOW_SIZE : minIndex; LOGGER.error("Maximum window size must be greater than the minimum windows size. Set to " + maxIndex); } } else { maxIndex = DEFAULT_WINDOW_SIZE; } final int compressionLevel = Integers.parseInt(compressionLevelStr, Deflater.DEFAULT_COMPRESSION); return new MyRolloverStrategy(minIndex, maxIndex, useMax, compressionLevel, config.getStrSubstitutor()); } // ↑↑↑ Some stuff copied from ↑↑↑ // ============================== protected MyRolloverStrategy(int minIndex, int maxIndex, boolean useMax, int compressionLevel, StrSubstitutor subst) { super(minIndex, maxIndex, useMax, compressionLevel, subst); } // Wrapper class only for setting a hook to execute() static class MyAction implements Action { final Action delegate; final String fileName; public MyAction(final Action delegate, final String fileName) { this.delegate = delegate; this.fileName = fileName; } @Override public void run() { delegate.run(); } @Override public boolean execute() throws IOException { try { BufferedWriter writer = null; try { writer = new BufferedWriter(new FileWriter(new File(fileName), true)); writer.write("****************************\n"); writer.write("*** Bye, bye old logfile ***\n"); writer.write("****************************\n"); } finally { if (writer != null) writer.close(); } } catch (Throwable e) { logger.error("Writing to bottom of old logfile \"" + fileName + "\" with", e); } boolean ret = delegate.execute(); try { BufferedWriter writer = null; try { writer = new BufferedWriter(new FileWriter(new File(fileName), true)); writer.write("*************************\n"); writer.write("*** Hello new logfile ***\n"); writer.write("*************************\n"); } finally { if (writer != null) writer.close(); } } catch (Throwable e) { logger.error("Writing to top of new logfile \"" + fileName + "\" with", e); } return ret; } @Override public void close() { delegate.close(); } @Override public boolean isComplete() { return delegate.isComplete(); } } // Wrapper class only for setting a hook to getSynchronous().execute() static class MyRolloverDescription implements RolloverDescription { final RolloverDescription delegate; public MyRolloverDescription(final RolloverDescription delegate) { this.delegate = delegate; } @Override public String getActiveFileName() { return delegate.getActiveFileName(); } @Override public boolean getAppend() { //return delegate.getAppend(); // As long as we already put some data to the top of the new logfile, subsequent writes should be performed with "append". return true; } // The synchronous action is for renaming, here we want to hook @Override public Action getSynchronous() { Action delegateAction = delegate.getSynchronous(); if (delegateAction == null) return null; return new MyAction(delegateAction, delegate.getActiveFileName()); } // The asynchronous action is for compressing, we don't need to hook here @Override public Action getAsynchronous() { return delegate.getAsynchronous(); } } public RolloverDescription rollover(final RollingFileManager manager) { RolloverDescription ret = super.rollover(manager); return new MyRolloverDescription(ret); } } 

如果我的已发布function请求已实现,则在未来版本的log4j2中解决此要求可能会更容易。

目前没有用于翻转的回调挂钩。 我可以建议在log4j2问题跟踪器中将其作为function请求提出吗?

提供新解决方案

自从提出这个问题以来已经过了一段时间了,现在,当我想要做同样的事情时,我发现可以在不搞乱工厂的情况下解决问题,但这并不容易找到。 log4j2团队通过配置实现了这一目标。 我希望我的post有用,为别人节省时间。

他们在PatternLayout元素中隐藏了这个function。 不是我第一次看的地方,但为什么抱怨什么时候有效?

这是我的配置 (注意页眉和页脚,以及他们使用的属性):

    ${sys:catalina.home}/logs/my-app ${log-path}/archive [%d] [%-5p] [%t] %C{5} - %m%n 450 [%d] Start of log \n========================================================================\n Will be archived in ${archive}\n\n \n========================================================================\n[%d] End of log                                                                    

如您所见,我已经包含了一个包含系统属性的时间戳和属性。 Log4j2可以显示许多不同类型的属性,您可以根据需要执行很多操作。

日志文件如下所示:

 [2016-08-09 17:00:43,924] Start of log ======================================================================== Will be archived in /home/emanciperingsivraren/program/apache-tomcat-8.0.32/logs/my-app/archive [2016-08-09 17:00:44,000] [INFO ] [RMI TCP Connection(2)-127.0.0.1] [snip] ======================================================================== [2016-08-09 17:02:17,871] End of log 

您需要更多自定义信息吗? – 尝试将该信息放在属性,系统属性或log4j2可以读取的其他内容中。

有关您可以拥有的属性类型的详细信息,请参阅log4j2中的属性替换 。

关于配置的评论

  • 我使用属性而不是重复常用设置
  • 如果在根记录器中写入了某些东西,那么它是未处理的,应该编写一个记录器来定义我们想要用它做什么
  • 由于错误/exception可以在许多其他日志消息中“隐藏”,因此它们也可以在那里打印,并且可以很容易地找到
  • 在Console上打印的消息具有不同的颜色