查找在 logback 中配置的所有登录追加程序.xml即使未附加到任何记录器



我正在构建一个用于管理登录记录器级别和更改追加器的管理员 UI。我知道我可以使用以下代码找到添加到某个记录器的所有追加器:

private Map<String, Appender<ILoggingEvent>> getAppendersMap() {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
Map<String, Appender<ILoggingEvent>> appendersMap = new HashMap<>();
for (Logger logger : loggerContext.getLoggerList()) {
Iterator<Appender<ILoggingEvent>> appenderIterator = logger.iteratorForAppenders();
while (appenderIterator.hasNext()) {
Appender<ILoggingEvent> appender = appenderIterator.next();
if (!appendersMap.containsKey(appender.getName())) {
appendersMap.put(appender.getName(), appender);
}
}
}
return appendersMap;
}

问题是如果我有这个logback.xml作为示例:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="10 minutes">
<appender name="writeToConsole" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%date{yyyy-MM-dd;HH:mm:ss.SSS} %-11p: %40.40c: %X{tablist}%m %n
</pattern>
</encoder>
</appender>
<appender name="NOPAppender" class="ch.qos.logback.core.helpers.NOPAppender" />
<root>
<level value="INFO"/>
<appender-ref ref="writeToConsole"/>
</root>
</configuration>

NOPAppender没有附加到任何记录器,因此该方法getAppendersMap()找不到它

当从外部文件(logback.xml,logback-test.xml,logback.groovy 等)配置自身时,Logback 会像这样遍历配置(这是它所做的事情的删节版本):

  • 查找任何追加程序
  • 将每个追加器添加到InterpretationContext.objectMap中的瞬态APPENDER_BAG
  • 对于每个appender-ref(存在于记录器定义中),在此APPENDER_BAG中查找引用的追加器(按名称),如果存在,则将该追加器添加到当前记录器

您可以使用追加器关联定义显式记录器:<logger name="com.x.y" level="INFO"><appender-ref ref="STDOUT"/></logger>或使用<root/>元素将追加器与所有记录器实例相关联:<root level="INFO"><appender-ref ref="STDOUT"/></root>

因此,不与任何记录器关联的追加器仅在配置时知道,一旦创建了LoggerContext,瞬态(APPENDER_BAG)就会被丢弃。这就解释了为什么您的getAppendersMap()找不到您的NOOPAppender

我认为您在这里的要求是"了解"附加器,即使在配置时不需要它。据推测,追加器最初可能是不需要的,但有人可以选择稍后启用。这就像您尝试使用 logback.xml作为所有追加器的完整语句,有人可能希望在应用程序运行时的某个时刻使用这些追加器。为此,您最初必须在LoggerContext中包含可选的追加器,但要采用无操作实现。这是您在问题中已经在做的事情,但您只需要将NOPAppender添加到根上下文中。

<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="NOPAppender"/>
</root>

这对正在运行的系统没有影响,因为追加器是无操作的,但由于它在LoggerContext中被引用,因此您可以管理它,当然,如果您想启用此功能,则必须以编程方式将追加器类的选择更改为无操作以外的其他内容。

另一种替代方法可能是以编程方式创建追加器,并在需要时将其添加到LoggerContext中。

4 年多后,我发现了一些可以与 Logback XML 配置一起使用的东西:

import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.util.ContextInitializer;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.joran.action.ActionConst;
import ch.qos.logback.core.joran.spi.InterpretationContext;
import ch.qos.logback.core.joran.spi.JoranException;
/**
* Utility class for finding all configured logback appenders.
*/
public class LogbackAppenderFinder {
private static final Logger LOGGER = LoggerFactory.getLogger(LogbackAppenderFinder.class);
private LogbackAppenderFinder() {
}
/**
* Tries to find all appenders from an xml config. The method works by trying to find the default logback config file 
* names, can be further enhanced to include others, e.g. files supported by Spring Boot or custom files.
*
* @return Map of all the appenders configured with the XML file where the key is the appender name configured in
* the xml file and the value is the appender itself.
*/
public static Map<String, Appender<ILoggingEvent>> findAppendersFromXmlConfig() {
try {
LoggerContext dummyLoggerContext = new LoggerContext();
ContextInitializer contextInitializer = new ContextInitializer(dummyLoggerContext);
URL configurationFileUrl = contextInitializer.findURLOfDefaultConfigurationFile(true);
if (configurationFileUrl != null && configurationFileUrl.toString().endsWith(".xml")) {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(dummyLoggerContext);
configurator.doConfigure(configurationFileUrl);
InterpretationContext interpretationContext = configurator.getInterpretationContext();
Map<String, Object> objectMap = interpretationContext.getObjectMap();
return (Map<String, Appender<ILoggingEvent>>) objectMap.get(ActionConst.APPENDER_BAG);
} else {
LOGGER.warn("Unable to find xml configuration file.");
}
} catch (JoranException e) {
LOGGER.warn("Failed to parse logback configuration, check config file.");
}
return new HashMap<>();
}
}

相关内容