当前位置: 首页 > 面试题库 >

如何自定义log4j2 RollingFileAppender?

孙渝
2023-03-14
问题内容

我们使用log4j
1.2.x登录我们的产品,并希望在不久的将来迁移到log4j2.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并且已设置为privateas。

开发一个完整的新程序包并从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在标题中。我还尝试将breakoint置于第一个if条件,我注意到它只被调用一次。因此,我担心的是,仅当log4j2从xml配置中初始化其属性时,才会在启动时初始化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()方法的值不能动态更改,因为它仅在初始化期间被调用。任何更多的帮助将不胜感激。


问题答案:

使用内置查找的一种替代方法是创建自定义查找。使用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手册详细介绍了如何构建/部署自定义插件。简要摘要:

最简单的方法是使用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.自定义异常,3.自定义异常的原理前记 在前面的文章中, 表示了视图解析的原理和异常解析器的解析原理。 这篇通过如何自定义视图和自定义异常处理和自定义异常处理的原理进行说明。 这里说明一下, 自定义的视图和自定义的异常都是会代替容器默认的组件的, 异常还好说, 就是不符合就抛, 视图的话需要注意一下优先级, 可以在自定义的视图解析器上加上注解。 1.自定义视图 这里原理就是添加一个视图和视图解析器, 然后放入容器中, 最后访问相应

  • 问题内容: 我想使用ModelMapper将实体转换为DTO并返回。通常,它可以工作,但是如何自定义它。它有太多选择,因此很难弄清楚从哪里开始。最佳做法是什么? 我会在下面亲自回答,但如果另一个答案更好,我会接受。 问题答案: 首先这是一些链接 modelmapper入门 API文档 博客文章 随机代码示例 我对mm的印象是它的设计很好。该代码很扎实,阅读起来很愉快。但是,该文档非常简洁,仅包含很

  • 本文向大家介绍如何自定义filter?相关面试题,主要包含被问及如何自定义filter?时的应答技巧和注意事项,需要的朋友参考一下 在模块下挂在一个filter()方法,第一个参数传入过滤器的名字,第二个参数是回调函数,处理过滤方法的详细内容,最后返回结果,这样外部就可以根据过滤器的名字调用了

  • 我想创建一个自定义的矩形进度条与白色背景色。进度条中有一个文本居中,用于定义进度条的高度。还有另一个黑色背景色的视图,其宽度从左侧开始增长,具体取决于进度。我有这个,但不起作用: 编辑:好的,我有这个: 唯一的问题是:如何告诉LinearLayout与TextView一样高?

  • 我有多个web api项目(微服务),我想只使用一个swagger-ui链接来公开它们。为了撰写本文,我将每个web api项目称为EndpointA和EndpointB。 我创建了一个swagger-ui项目,并将每个endpoint添加到该项目中。 我的swagger-ui项目 我还尝试使用直接在每个endpoint中设置前缀,但我不知道如何设置。

  • TensorFlow GraphDef based models (typically created via the Python API) may be saved in one of following formats: TensorFlow SavedModel Frozen Model Session Bundle Tensorflow Hub module All of above f