log4j -拦截在类/方法中创建的日志记录

ac1kyiln  于 2022-11-06  发布在  其他
关注(0)|答案(3)|浏览(182)

我有一个应用程序,其中有许多ejb计时器方法,这些方法在不同的时间间隔触发,为了记录这些计划任务的性能,我编写了两个方法,记录每个任务的开始和结束,并记录一些信息,如名称、运行持续时间和每次触发计时器时的日志信息
使用日志信息的目的是截获从这些方法中引发的所有日志记录语句,输出到字符串并保存......我通过创建一个新的WriterAppender来实现这一点,为所有日志记录级别将其附加到Logger,然后在计时器完成时捕获字符串输出并删除appender
这样做效果很好,但似乎有两个不想要的效果:
1)它停止了出现在我们的普通文件附加器日志中的来自定时器类的任何日志记录,例如,日志消息被捕获,但它们现在不出现在我们的普通日志中(配置为显示所有内容〉INFO
2)来自不同计时器的日志语句重叠出现在彼此的日志中,我猜这是Logger在单个线程上工作的结果,不确定是否有解决这个问题的方法?
我不是log4j方面的Maven,所以我希望有人能指出一个简单的修复1、2或两者的方法
下面的代码显示了在每个作业开始和结束时调用的两个方法

private Writer w = new StringWriter();
private WriterAppender wa;
private Logger logOutput = Logger.getLogger(ScheduledActions.class);

private TimerEvent logStartTimerEvent(ScheduledTask task){

    RedisActions redisActions = (RedisActions)Component.getInstance("redisActions");
    TimerEvent event = new TimerEvent(task);

    //Initialise a new log appender to intercept all log4j entries
    w = new StringWriter();
    Layout l = new PatternLayout("%m%n");
    wa = new WriterAppender(l, w);
    wa.setEncoding("UTF-8");
    wa.setThreshold(Level.ALL);
    wa.activateOptions();

    // Add it to logger
    logOutput = Logger.getLogger(ScheduledActions.class);
    logOutput.addAppender(wa);

    //Push this timer event to redis
    redisActions.addToFixedLengthList(RedisItem.TIMER_HISTORY.getKey()+"_"+task.getName(), gson.toJson(event), 100L);

    return event;
}

private void logEndTimerEvent(TimerEvent event){

    try{

        RedisActions redisActions = (RedisActions)Component.getInstance("redisActions");
        event.setLogInfo(w.toString());
        event.setEndTime(new Date());
        redisActions.editLastMemberInList(RedisItem.TIMER_HISTORY.getKey()+"_"+event.getTask().getName(), gson.toJson(event));
        w.flush();

    } catch (IOException e) {
        e.printStackTrace();
    }finally{
        //clean up the temp log appender
        logOutput.removeAppender(wa);
        wa.close();

    }

}
jjjwad0x

jjjwad0x1#

我在尝试做类似的事情时发现了这个老问题。现在,我的解决方案是创建一个独立的记录器/追加器,带有一个唯一的loggerId,如下所示:

var loggerId = operation.getId(); // This is a unique ID
var logger = (org.apache.logging.log4j.core.Logger) LogManager.getLogger(loggerId);
var appender = createAppender();
logger.addAppender(appender);

logger.info("using logger");

appender.stop();
logger.removeAppender(appender);

我遇到的问题是我需要传递这个logger变量,我不能使用经典的“静态记录器”。

amrnrhlw

amrnrhlw2#

我正在考虑的另一个解决方案是向rootLogger添加一个AbstractAppender,然后从那里对日志进行任何操作。
我会更新这个答案,当我开发它多一点。

var layout = PatternLayout.newBuilder()
        .withPattern("%d{ISO8601_OFFSET_DATE_TIME_HHCMM} [%t] %-5level  %c{1.} %X %msg%n%throwable")
        .build();

var rootLogger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger();

var appender = new AbstractAppender("intercept-appender", null, layout, false, null) {
    @Override
    public void append(LogEvent event) {
        System.out.println("INTERCEPTED: " + getLayout().toSerializable(event));
    }
};

appender.start();
rootLogger.addAppender(appender);

log.info("Message after adding intercept appender");
5t7ly7z5

5t7ly7z53#

我的最终解决方案是将AppenderThreadContextMapFilter相加。

// With %X you will see the thread context values
    var layout = PatternLayout.newBuilder()
            .withPattern("%d{ISO8601_OFFSET_DATE_TIME_HHCMM} [%t] %-5level %c{1.} %X %msg%n%throwable")
            .build();

    var appender = FileAppender.newBuilder()
            .setName(fileId)
            .withFileName("/tmp/my-file.log")
            .setLayout(layout)
            // --- IMPORTANT PART ---
            .setFilter(new ThreadContextMapFilter(
                    Map.of("my-key", List.of("my-value")), true, null, null))
            .build();

    // Add the appender
    // https://logging.apache.org/log4j/2.x/manual/customconfig.html
    appender.start();
    final LoggerContext context = LoggerContext.getContext(false);
    final Configuration config = context.getConfiguration();
    config.addAppender(appender);
    config.getRootLogger().addAppender(appender, null, null);
    context.updateLoggers();

    // --- IMPORTANT PART ---
    try (var ignore = CloseableThreadContext.put("my-key", "my-value")) {
        // the logs here will be captured by the appender
        log.info("This log WILL be captured by the appender");

        // Note: In case you start threads from here,
        // you should pass the ThreadContext to them.
    }

    log.info("This log will NOT be captured by the appender");

    // Wait for the logs to be flushed. Any improvement ideas?
    // https://stackoverflow.com/a/71081005/1121497
    try {
        Thread.sleep(500);
    } catch (InterruptedException ignore) {
    }

    // Remove the appender
    final LoggerContext context = LoggerContext.getContext(false);
    final Configuration config = context.getConfiguration();
    config.getRootLogger().removeAppender(appender.getName());
    context.updateLoggers();
    appender.stop();

相关问题