我们使用log4j 1.2.x登录我们的产品,并希望在不久的将来迁移到log4j 2.x.我们实现的功能之一是在生成的每个新的翻转日志文件上记录系统信息和其他重要参数.我们在log4j 1.2.x中实现的方式是我们扩展了RollingFileAppender类的log4j并覆盖了rollOver()方法,下面是实现的部分片段
@Override
public void rollOver() {
super.rollOver(); //We are not modifying it's default functionality but as soon as rollOver happens we apply our logic
//
// Logic to log required system properties and important parameters.
//
}
现在我们想要迁移到log4j2,我们正在寻找一种新的解决方案来实现相同的功能.但是当我看到log4j2的源代码时,它与旧的源代码非常不同. RollingFileAppender类不包含rollover()方法,因为它已被移动到RollingManagerhelper并且它已被设置为私有.
开发一个完整的新包并从log4j2扩展/实现一些抽象/辅助类是我们可能的解决方案之一,但这需要大量的编码/复制,因为我们不修改RollingFileAppender所做的事情,而只需要对它进行小的扩展.有一个简单的解决方案吗?
UPDATE
我根据答案中的建议创建了自定义查找,下面是我创建它的方式;
@Plugin(name = "property", category = StrLookup.CATEGORY)
public class CustomLookup extends AbstractLookup {
private static AtomicLong aLong = new AtomicLong(0);
@Override
public String lookup(LogEvent event, String key) {
if (aLong.getAndIncrement() == 0) {
return "this was first call";
}
if (key.equalsIgnoreCase("customKey")) {
return getCustomHeader();
} else {
return "non existing key";
}
}
private static String getCustomHeader() {
// Implementation of custom header
return "custom header string";
}}
但这没有提到的那样;这总是打印这是标题中的第一个调用.我也尝试将breakoint放在第一个if条件上,我注意到它只被调用一次.所以我担心的是,当log4j2从xml config初始化其属性时,customLookup类仅在启动时初始化.我不知道我怎么能实现这个自定义查找类.
更新2
完成上述实现后,我尝试了不同的方法,如下所示;
private static AtomicLong aLong = new AtomicLong(0);
@Override
public String lookup(LogEvent event, String key) {
return getCustomHeader(key);
}
private static String getCustomHeader(final String key) {
if (aLong.getAndIncrement() == 0) {
return "this was first call";
}
if (key.equalsIgnoreCase("customKey")) {
// Implementation for customKey
return "This is custom header";
} else {
return "non existing key";
}
}
但这也是一样的. log4j2在从其xml配置文件初始化时创建标头,然后使用内存中的标头.重写的lookup()方法的返回值不能动态更改,因为它只在初始化期间被调用.任何更多的帮助将受到高度赞赏.
解决方法:
使用内置查找的替代方法是创建自定义查找.这可以使用log4j2插件在几行代码中完成.然后,您的自定义查找会提供您希望在每次翻转时在文件标题中显示的确切值.
插件代码看起来像这样:
package com.mycompany;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.lookup.AbstractLookup;
import org.apache.logging.log4j.core.lookup.StrLookup;
/**
* Looks up keys from a class SomeClass which has access to all
* information you want to provide in the log file header at rollover.
*/
@Plugin(name = "setu", category = StrLookup.CATEGORY)
public class SetuLookup extends AbstractLookup {
/**
* Looks up the value of the specified key by invoking a
* static method on SomeClass.
*
* @param event The current LogEvent (ignored by this StrLookup).
* @param key the key to be looked up, may be null
* @return The value of the specified key.
*/
@Override
public String lookup(final LogEvent event, final String key) {
return com.mycompany.SomeClass.getValue(key);
}
}
然后,在您的配置中,您可以使用模式布局的标题在每次翻转时输出:
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}.log.gz">
<!-- use custom lookups to access arbitrary internal system info -->
<PatternLayout header="${setu:key1} ${setu:key2}">
<Pattern>%d %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
</Policies>
</RollingFile>
log4j2手册包含有关构建/部署custom plugins的详细信息.简要摘要:
最简单的方法是使用Maven构建jar;这将导致log4j2注释处理器在jar中生成二进制索引文件,因此log4j2可以快速找到您的插件.
另一种方法是在log4j2.xml配置的packages属性中指定插件类的包名称:
<Configuration status="warn" packages="com.mycompany">
...
更新:请注意,在您的查找实现中,您可以根据需要进行创作.例如:
package com.mycompany;
public class SomeClass {
private static AtomicLong count = new AtomicLong(0);
public static String getValue(final String key) {
if (count.getAndIncrement() == 0) { // is this the first call?
return ""; // don't output a value at system startup
}
if ("FULL".equals(key)) {
// returns info to shown on rollover, nicely formatted
return fullyFormattedHeader();
}
return singleValue(key);
}
....
}