简体   繁体   English

如何自定义 log4j2 RollingFileAppender?

[英]How to customize log4j2 RollingFileAppender?

We use log4j 1.2.x for logging in our product and are looking to migrate to log4j 2.x in near future.我们使用 log4j 1.2.x 来登录我们的产品,并希望在不久的将来迁移到 log4j 2.x。 One of the functionality we have implemented is to log the system information and other important parameters on every new roll-over logfile that gets generated.我们已实现的功能之一是在生成的每个新滚动日志文件上记录系统信息和其他重要参数。 The way we implemented in log4j 1.2.x is that we have extended RollingFileAppender class of log4j and have overridden the rollOver() method, below is the part snippet of the implementation我们在 log4j 1.2.x 中实现的方式是我们扩展了 log4j 的RollingFileAppender类并覆盖了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.
    //

}

Now as we want to migrate to log4j2 we are looking at a new solution to achieve same functionality.现在,当我们想要迁移到 log4j2 时,我们正在寻找一种新的解决方案来实现相同的功能。 But as I see the source code for log4j2 it is very different from older source code.但是当我看到 log4j2 的源代码时,它与旧的源代码非常不同。 The RollingFileAppender class does not contain rollover() method as it has been moved to RollingManagerhelper and it has been set to private as-well. RollingFileAppender类不包含rollover()方法,因为它已移至RollingManagerhelper并且也已设置为private

Developing a complete new package and extending/implementing some abstract/helper classes from log4j2 is one of the possible solution for us but that would require a lot of coding/copying as we do not modify what RollingFileAppender does rather we only need small extension to it.开发一个完整的新包并从 log4j2 扩展/实现一些抽象/帮助类是我们可能的解决方案之一,但这将需要大量编码/复制,因为我们不修改RollingFileAppender所做的事情,而只需要对其进行小的扩展. Is there a simple solution to it?有没有简单的解决方案?

UPDATE更新

I created a custom lookup according to the suggestion in answers and below is how I created it;我根据答案中的建议创建了一个自定义查找,下面是我创建它的方式;

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

}}

But this did not work as mentioned;但这并没有像提到的那样工作; this always prints this was first call in the header.这总是在标题中打印this was first call I also tried putting breakoint on the first if condition and what I noticed was that it only gets called once.我还尝试将 breakpoint 放在第一个if条件上,我注意到它只被调用一次。 So what I fear is that the customLookup class only gets initialized on the startup when log4j2 is initialising its properties from xml config.所以我担心的是 customLookup 类只会在 log4j2 从 xml 配置初始化其属性时在启动时初始化。 I don't know how else I could implemented this custom lookup class.我不知道我还能如何实现这个自定义查找类。

UPDATE 2更新 2

After the above implementation I tried it in bit different way which is as below;在上述实现之后,我以不同的方式尝试了它,如下所示;

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

But this does the same thing as-well.但这也做同样的事情。 log4j2 creates the headers at while initialising from its xml config file and then uses the headers from memory. log4j2 在从其 xml 配置文件初始化时创建标头,然后使用内存中的标头。 The return value of overridden lookup() method can not be changed dynamically as it only gets called during initialisation.重写的lookup()方法的return值不能动态更改,因为它只在初始化期间被调用。 Any more help would be highly appreciated.任何更多的帮助将不胜感激。

An alternative to using the built-in lookups is to create a custom lookup.使用内置查找的替代方法是创建自定义查找。 This can be accomplished in a few lines of code with a log4j2 plugin.这可以使用 log4j2 插件在几行代码中完成。 Your custom lookup then provides the exact value you want to show in the file header at each roll over.然后,您的自定义查找会提供您希望在每次翻转时在文件标题中显示的确切值。

The plugin code would look something like this:插件代码如下所示:

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);
    }
}

Then, in your configuration you can use the header of the pattern layout to output this at every rollover:然后,在您的配置中,您可以使用模式布局的标题在每次翻转时输出它:

<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>

The log4j2 manual has details on building/deploying custom plugins . log4j2 手册有关于构建/部署自定义插件的详细信息。 Brief summary:简要总结:

The easiest way is to build your jar with Maven;最简单的方法是用 Maven 构建你的 jar; this will cause the log4j2 annotation processor to produce a binary index file in the jar so your plugin can be found quickly by log4j2.这将导致 log4j2 注释处理器在 jar 中生成一个二进制索引文件,以便 log4j2 可以快速找到您的插件。

The alternative is to specify the package name of your plugin class in your log4j2.xml configuration's packages attribute:另一种方法是在 log4j2.xml 配置的packages属性中指定插件类的包名称:

<Configuration status="warn" packages="com.mycompany">
  ...

UPDATE: Note that in your lookup implementation you can get as creative as necessary.更新:请注意,在您的查找实现中,您可以根据需要获得尽可能多的创意。 For example:例如:

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);
    }
    ....
}

This can be accomplished with configuration.这可以通过配置来实现。 You can use the header of the pattern layout to output information.您可以使用模式布局的标题来输出信息。 This will be included at every rollover.这将包含在每次翻转中。

<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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM