如何自定义log4j2 RollingFileAppender?

时间:2014-12-03 14:41:42

标签: java logging log4j log4j2

我们使用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,并且已将其设置为private

开发一个完整的新包并从log4j2扩展/实现一些抽象/辅助类是我们可能的解决方案之一,但这需要大量的编码/复制,因为我们不修改RollingFileAppender所做的事情。我们只需要小扩展。有一个简单的解决方案吗?

更新

我根据答案中的建议创建了自定义查找,下面是我创建它的方式;

@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";

}}

但这没有提到的那样;这总是在标题中打印this was first call。我也尝试在第一个if条件下使用breakoint,我注意到它只被调用一次。所以我担心的是,当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配置文件初始化时创建标头,然后使用内存中的标头。被覆盖的return方法的lookup()值无法动态更改,因为它仅在初始化期间被调用。任何更多的帮助将受到高度赞赏。

2 个答案:

答案 0 :(得分:6)

使用内置查找的另一种方法是创建自定义查找。这可以使用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);
    }
    ....
}

答案 1 :(得分:2)

这可以通过配置来完成。您可以使用模式布局的标题输出信息。这将包含在每次展期中。

<RollingFile name="RollingFile" fileName="logs/app.log"
             filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}.log.gz">

  <!-- use built-in lookups for system info or system properties -->
  <PatternLayout header="${java:runtime} - ${java:vm} - ${java:os}">
    <Pattern>%d %m%n</Pattern>
  </PatternLayout>
  <Policies>
    <TimeBasedTriggeringPolicy />
  </Policies>
</RollingFile>