临时增加多线程服务中的log4j2 logger级别

Temporarily increase log4j2 logger level in multi-threaded service

这是一个很长的问题,请问 Log4j2 大师。

我有一项服务:

  1. 对性能要求非常严格
  2. 使用 log4j2 进行大量日志记录调用。

典型的呼叫被门控,例如:

if ( LOG.isInfoEnabled() ) {
  LOG.info("everything's fine"); 
}

由于日志消息的数量和性能需求,服务通常会 运行 将日志记录设置为 WARN(即消息不多)。

但是,我被要求为服务调用构建一个参数,如果给定,将导致它:

  1. 暂时将日志记录级别提高到参数中要求的级别(例如,INFO 或 TRACE)
  2. 添加 WriterAppender 以捕获 PrintWriter 中的日志记录。
  3. PrintWriter 日志数据附加到请求响应。

很明显,由于我在每个日志记录调用周围设置了门控,我需要临时提高日志记录级别,如下所示:

LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Configuration cfg = ctx.getConfiguration();
LoggerConfig loggerCfg = cfg.getLoggerConfig("com.mycompany.scm");
loggerCfg.setLevel(logLevel);
.. other code to add `WriterAppender` ...
ctx.updateLoggers();

但是我有一个直接的问题,因为它导致日志记录也进入服务的日志文件。那可能不是世界末日,但我想尽可能避免这种情况。

我通过让默认日志记录通过按级别过滤的附加程序来做到这一点,这样即使打开日志记录,它也不会写入任何比默认日志文件中需要的更详细的消息。 (像这样,来自我的 .properties 文件):

appenders=scm_warn, scm_info 

appender.scm_warn.type = Console
appender.scm_warn.name = SCM_WARN
appender.scm_warn.layout.type = PatternLayout
appender.scm_warn.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
appender.scm_warn.filter.threshold.type = ThresholdFilter
appender.scm_warn.filter.threshold.level = warn

appender.scm_info.type = Console
appender.scm_info.name = SCM_INFO
appender.scm_info.layout.type = PatternLayout
appender.scm_info.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
appender.scm_info.filter.threshold.type = ThresholdFilter
appender.scm_info.filter.threshold.level = info

loggers = coreConfigurator

logger.coreConfigurator.name = com.mycompany.scm
logger.coreConfigurator.level = warn
logger.coreConfigurator.additivity = false   # do not let configurator log messages get processed by client application's parent or root logger.
logger.coreConfigurator.appenderRefs = core
logger.coreConfigurator.appenderRef.core.ref = SCM_WARN

... 这样,即使日志记录级别提高,额外的消息也不会进入主日志文件(我只希望它们进入我的 PrintWriter)。

现在,问题来了!

我怎样才能临时增加当前线程的日志级别(就像我在上面的代码中尝试做的那样)?

如果有三 (3) 个同时调用该服务,我...

  1. ... 希望每个添加的 Appender 仅写入由创建 Appender 的线程生成的日志消息。
  2. ...希望每个添加的 Appender 在添加它的请求完成后被删除。
  3. ...希望将日志记录级别重置回原来的水平,只要没有其他请求仍在处理中打开此日志记录参数即可。

理想情况下,我认为这听起来像是我希望每个线程都有一个完全独立的日志记录上下文。那可能吗?对如何做这一切有什么想法吗?

您可能会使用自定义 Context Selector 让每个线程拥有不同的上下文,但是当多个线程想要写入同一个日志文件时,这可能会导致问题,因此可能不是一个可行的选择。

另一种方法是编写自定义 Appender,它使用 ThreadLocal 来存储 StringWriter。如果尚未为线程建立 StringWriter,则追加将跳过日志记录。此自定义 Appender 应添加到 Log4J 配置文件中,因此它始终存在并接收日志条目。

这样,您可以通过创建 StringWriter 并将其分配给 ThreadLocal、运行 代码来启用特定线程的日志记录,然后清除 ThreadLocal 并获得来自 StringWriter 的记录信息。由于最初没有任何线程的 StringWriter,appender 将不执行任何操作,因此不应以任何明显的方式影响性能。

你仍然需要做你已经在做的级别升级,在其他附加程序上使用过滤器。

你可以:

  • 访问 class 记录器并更改日志级别(按照您的建议): LogManager.getLogger(Class.forName("your.class.package")).setLevel(Level.FATAL);

  • 使用不同的记录器;只需配置两个不同的记录器。