这里有一个很长的问题要问 Log4j2 专家。
我有一项服务:
- 有非常严格的性能要求
- 使用 log4j2 进行大量日志记录调用。
典型的调用是门控的,例如:
if ( LOG.isInfoEnabled() ) {
LOG.info("everything's fine");
}
由于日志消息的数量和性能需求,该服务通常会在日志记录设置为 WARN 的情况下运行(即消息不多)。
但是,我被要求在服务调用中构建一个参数,如果给出该参数,将导致它:
- 暂时将日志记录级别提高到参数中请求的级别(例如 INFO 或 TRACE)
- 添加
WriterAppender
以捕获PrintWriter
中的日志记录。 - 将
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();
但我有一个直接的问题,因为它导致日志记录也进入服务的日志文件。这可能不是世界末日,但如果可能的话,我想避免这种情况。
我通过让默认日志记录通过按级别过滤的附加程序来做到这一点,这样即使打开日志记录,它也不会写入比默认日志文件中所需的更详细的任何消息。 (像这样,来 self 的 .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) 个服务调用,我...
- ...希望每个添加的
Appender
只写入由创建该Appender
的线程生成的日志消息。 - ...希望每个添加的
Appender
在添加它的请求完成后被删除。 - ...希望日志记录级别重置回原来的状态,只要没有其他打开此日志记录参数的请求仍在处理中。
理想情况下,我认为这听起来像是我希望每个线程都有一个完全独立的日志记录上下文。那可能吗?关于如何做到这一切有什么想法吗?
最佳答案
您可以使用自定义 Context Selector每个线程有不同的上下文,但是当多个线程想要写入同一个日志文件时,这可能会导致问题,因此可能不是一个可行的选择。
另一种方法是编写一个自定义Appender
,它使用ThreadLocal
来存储StringWriter
。如果尚未为线程建立StringWriter
,则追加的内容将跳过日志记录。此自定义 Appender
应添加到 Log4J 配置文件中,以便它始终存在并接收日志条目。
这样,您可以通过创建 StringWriter
并将其分配给 ThreadLocal
来启用特定线程的日志记录,运行代码,然后清除 ThreadLocal
> 并从StringWriter
获取记录的信息。由于任何线程最初都没有 StringWriter
,因此追加器不会执行任何操作,因此不会以任何明显的方式影响性能。
您仍然需要执行已经在执行的级别升级,并在其他附加程序上使用过滤器。
关于java - 暂时提高多线程服务中的log4j2记录器级别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57695811/