Search code examples
javalogginglogback

Find all logback appenders configured in logback.xml even if not attached to any logger


I am building an admin UI for managing logback loggers level and changing appenders. I know I can find all appenders that are added to some logger with the following code:

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;
}

The problem is if I have this logback.xml as example:

<?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>

the NOPAppender is not a attached to any logger and therefor the method getAppendersMap() doesn't find it.


Solution

  • After 4 plus years, I found something that can be used with Logback XML configuration:

    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<>();
      }
    
    }