美文网首页
聊聊springboot的LogbackLoggingSyste

聊聊springboot的LogbackLoggingSyste

作者: go4it | 来源:发表于2023-11-01 09:18 被阅读0次

    本文主要研究一下springboot的LogbackLoggingSystem

    LoggingSystem

    org/springframework/boot/logging/LoggingSystem.java

    public abstract class LoggingSystem {
    
        public abstract void beforeInitialize();
    
        public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
        }
    
        public void cleanUp() {
        }
    
        public Runnable getShutdownHandler() {
            return null;
        }
    
        public Set<LogLevel> getSupportedLogLevels() {
            return EnumSet.allOf(LogLevel.class);
        }
    
        public void setLogLevel(String loggerName, LogLevel level) {
            throw new UnsupportedOperationException("Unable to set log level");
        }
    
        public List<LoggerConfiguration> getLoggerConfigurations() {
            throw new UnsupportedOperationException("Unable to get logger configurations");
        }
    
        public LoggerConfiguration getLoggerConfiguration(String loggerName) {
            throw new UnsupportedOperationException("Unable to get logger configuration");
        }               
    }
    

    LoggingSystem定义了beforeInitialize抽象方法,需要子类实现,,同时还提供了setLogLevel、getLoggerConfigurations、getLoggerConfiguration,默认是抛出UnsupportedOperationException

    NoOpLoggingSystem

        static class NoOpLoggingSystem extends LoggingSystem {
    
            @Override
            public void beforeInitialize() {
    
            }
    
            @Override
            public void setLogLevel(String loggerName, LogLevel level) {
    
            }
    
            @Override
            public List<LoggerConfiguration> getLoggerConfigurations() {
                return Collections.emptyList();
            }
    
            @Override
            public LoggerConfiguration getLoggerConfiguration(String loggerName) {
                return null;
            }
    
        }
    

    NoOpLoggingSystem继承了LoggingSystem,其方法都是空操作

    AbstractLoggingSystem

    org/springframework/boot/logging/AbstractLoggingSystem.java

    public abstract class AbstractLoggingSystem extends LoggingSystem {
    
        protected static final Comparator<LoggerConfiguration> CONFIGURATION_COMPARATOR = new LoggerConfigurationComparator(
                ROOT_LOGGER_NAME);
    
        private final ClassLoader classLoader;
    
        public AbstractLoggingSystem(ClassLoader classLoader) {
            this.classLoader = classLoader;
        }
    
        @Override
        public void beforeInitialize() {
        }
    
        @Override
        public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
            if (StringUtils.hasLength(configLocation)) {
                initializeWithSpecificConfig(initializationContext, configLocation, logFile);
                return;
            }
            initializeWithConventions(initializationContext, logFile);
        }
    
        /**
         * Load sensible defaults for the logging system.
         * @param initializationContext the logging initialization context
         * @param logFile the file to load or {@code null} if no log file is to be written
         */
        protected abstract void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile);
    
        /**
         * Load a specific configuration.
         * @param initializationContext the logging initialization context
         * @param location the location of the configuration to load (never {@code null})
         * @param logFile the file to load or {@code null} if no log file is to be written
         */
        protected abstract void loadConfiguration(LoggingInitializationContext initializationContext, String location,
                LogFile logFile);
    
        /**
         * Reinitialize the logging system if required. Called when
         * {@link #getSelfInitializationConfig()} is used and the log file hasn't changed. May
         * be used to reload configuration (for example to pick up additional System
         * properties).
         * @param initializationContext the logging initialization context
         */
        protected void reinitialize(LoggingInitializationContext initializationContext) {
        }
    
        //......
    }   
    

    AbstractLoggingSystem继承了LoggingSystem,它主要是重写了initialize方法,若存在configLocation配置则执行initializeWithSpecificConfig,否则执行initializeWithConventions;它同时还定义了loadDefaults、loadConfiguration抽象方法需要子类实现

    Slf4JLoggingSystem

    org/springframework/boot/logging/Slf4JLoggingSystem.java

    public abstract class Slf4JLoggingSystem extends AbstractLoggingSystem {
    
        private static final String BRIDGE_HANDLER = "org.slf4j.bridge.SLF4JBridgeHandler";
    
        public Slf4JLoggingSystem(ClassLoader classLoader) {
            super(classLoader);
        }
    
        @Override
        public void beforeInitialize() {
            super.beforeInitialize();
            configureJdkLoggingBridgeHandler();
        }
    
        @Override
        public void cleanUp() {
            if (isBridgeHandlerAvailable()) {
                removeJdkLoggingBridgeHandler();
            }
        }
    
        @Override
        protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
                LogFile logFile) {
            Assert.notNull(location, "Location must not be null");
            if (initializationContext != null) {
                applySystemProperties(initializationContext.getEnvironment(), logFile);
            }
        }
    
        //......
    }   
    

    Slf4JLoggingSystem继承了AbstractLoggingSystem,它覆盖了beforeInitialize,新增configureJdkLoggingBridgeHandler;其cleanUp方法在isBridgeHandlerAvailable的时候执行removeJdkLoggingBridgeHandler;其loadConfiguration在initializationContext不为null的时候执行applySystemProperties

    configureJdkLoggingBridgeHandler

        private void configureJdkLoggingBridgeHandler() {
            try {
                if (isBridgeJulIntoSlf4j()) {
                    removeJdkLoggingBridgeHandler();
                    SLF4JBridgeHandler.install();
                }
            }
            catch (Throwable ex) {
                // Ignore. No java.util.logging bridge is installed.
            }
        }
    

    configureJdkLoggingBridgeHandler主要是判断是否将JUL绑定到SLF4J,是的话则removeJdkLoggingBridgeHandler,然后执行SLF4JBridgeHandler.install()

    removeJdkLoggingBridgeHandler

        private void removeJdkLoggingBridgeHandler() {
            try {
                removeDefaultRootHandler();
                SLF4JBridgeHandler.uninstall();
            }
            catch (Throwable ex) {
                // Ignore and continue
            }
        }
    
        private void removeDefaultRootHandler() {
            try {
                Logger rootLogger = LogManager.getLogManager().getLogger("");
                Handler[] handlers = rootLogger.getHandlers();
                if (handlers.length == 1 && handlers[0] instanceof ConsoleHandler) {
                    rootLogger.removeHandler(handlers[0]);
                }
            }
            catch (Throwable ex) {
                // Ignore and continue
            }
        }
    

    removeJdkLoggingBridgeHandler主要是执行removeDefaultRootHandler,以及SLF4JBridgeHandler.uninstall()

    applySystemProperties

        protected final void applySystemProperties(Environment environment, LogFile logFile) {
            new LoggingSystemProperties(environment).apply(logFile);
        }
    
        public void apply(LogFile logFile) {
            PropertyResolver resolver = getPropertyResolver();
            setSystemProperty(resolver, EXCEPTION_CONVERSION_WORD, "exception-conversion-word");
            setSystemProperty(PID_KEY, new ApplicationPid().toString());
            setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "pattern.console");
            setSystemProperty(resolver, FILE_LOG_PATTERN, "pattern.file");
            setSystemProperty(resolver, FILE_CLEAN_HISTORY_ON_START, "file.clean-history-on-start");
            setSystemProperty(resolver, FILE_MAX_HISTORY, "file.max-history");
            setSystemProperty(resolver, FILE_MAX_SIZE, "file.max-size");
            setSystemProperty(resolver, FILE_TOTAL_SIZE_CAP, "file.total-size-cap");
            setSystemProperty(resolver, LOG_LEVEL_PATTERN, "pattern.level");
            setSystemProperty(resolver, LOG_DATEFORMAT_PATTERN, "pattern.dateformat");
            setSystemProperty(resolver, ROLLING_FILE_NAME_PATTERN, "pattern.rolling-file-name");
            if (logFile != null) {
                logFile.applyToSystemProperties();
            }
        }   
    

    applySystemProperties通过LoggingSystemProperties设置了系统属性方便后续log配置文件使用

    LogbackLoggingSystem

    org/springframework/boot/logging/logback/LogbackLoggingSystem.java

    public class LogbackLoggingSystem extends Slf4JLoggingSystem {
    
        private static final String CONFIGURATION_FILE_PROPERTY = "logback.configurationFile";
    
        private static final LogLevels<Level> LEVELS = new LogLevels<>();
    
        static {
            LEVELS.map(LogLevel.TRACE, Level.TRACE);
            LEVELS.map(LogLevel.TRACE, Level.ALL);
            LEVELS.map(LogLevel.DEBUG, Level.DEBUG);
            LEVELS.map(LogLevel.INFO, Level.INFO);
            LEVELS.map(LogLevel.WARN, Level.WARN);
            LEVELS.map(LogLevel.ERROR, Level.ERROR);
            LEVELS.map(LogLevel.FATAL, Level.ERROR);
            LEVELS.map(LogLevel.OFF, Level.OFF);
        }
    
        private static final TurboFilter FILTER = new TurboFilter() {
    
            @Override
            public FilterReply decide(Marker marker, ch.qos.logback.classic.Logger logger, Level level, String format,
                    Object[] params, Throwable t) {
                return FilterReply.DENY;
            }
    
        };
    
        public LogbackLoggingSystem(ClassLoader classLoader) {
            super(classLoader);
        }
    
        @Override
        protected String[] getStandardConfigLocations() {
            return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
        }
    
        //......
    }   
    

    LogbackLoggingSystem继承了Slf4JLoggingSystem,其getStandardConfigLocations返回logback-test.groovy, logback-test.xml, logback.groovy, logback.xml

    beforeInitialize

        @Override
        public void beforeInitialize() {
            LoggerContext loggerContext = getLoggerContext();
            if (isAlreadyInitialized(loggerContext)) {
                return;
            }
            super.beforeInitialize();
            loggerContext.getTurboFilterList().add(FILTER);
        }
    

    beforeInitialize方法主要是添加了TurboFilter

    initialize

        @Override
        public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
            LoggerContext loggerContext = getLoggerContext();
            if (isAlreadyInitialized(loggerContext)) {
                return;
            }
            super.initialize(initializationContext, configLocation, logFile);
            loggerContext.getTurboFilterList().remove(FILTER);
            markAsInitialized(loggerContext);
            if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
                getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY
                        + "' system property. Please use 'logging.config' instead.");
            }
        }
    

    initialize方法执行super.initialize(initializationContext, configLocation, logFile),然后markAsInitialized(loggerContext)

    loadDefaults

        @Override
        protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) {
            LoggerContext context = getLoggerContext();
            stopAndReset(context);
            boolean debug = Boolean.getBoolean("logback.debug");
            if (debug) {
                StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener());
            }
            LogbackConfigurator configurator = debug ? new DebugLogbackConfigurator(context)
                    : new LogbackConfigurator(context);
            Environment environment = initializationContext.getEnvironment();
            context.putProperty(LoggingSystemProperties.LOG_LEVEL_PATTERN,
                    environment.resolvePlaceholders("${logging.pattern.level:${LOG_LEVEL_PATTERN:%5p}}"));
            context.putProperty(LoggingSystemProperties.LOG_DATEFORMAT_PATTERN, environment.resolvePlaceholders(
                    "${logging.pattern.dateformat:${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}"));
            context.putProperty(LoggingSystemProperties.ROLLING_FILE_NAME_PATTERN, environment
                    .resolvePlaceholders("${logging.pattern.rolling-file-name:${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}"));
            new DefaultLogbackConfiguration(initializationContext, logFile).apply(configurator);
            context.setPackagingDataEnabled(true);
        }
    

    loadDefaults方法通过LOG_LEVEL_PATTERN、LOG_DATEFORMAT_PATTERN、ROLLING_FILE_NAME_PATTERN以及LogbackConfigurator来初始化DefaultLogbackConfiguration

    loadConfiguration

        @Override
        protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
                LogFile logFile) {
            super.loadConfiguration(initializationContext, location, logFile);
            LoggerContext loggerContext = getLoggerContext();
            stopAndReset(loggerContext);
            try {
                configureByResourceUrl(initializationContext, loggerContext, ResourceUtils.getURL(location));
            }
            catch (Exception ex) {
                throw new IllegalStateException("Could not initialize Logback logging from " + location, ex);
            }
            List<Status> statuses = loggerContext.getStatusManager().getCopyOfStatusList();
            StringBuilder errors = new StringBuilder();
            for (Status status : statuses) {
                if (status.getLevel() == Status.ERROR) {
                    errors.append((errors.length() > 0) ? String.format("%n") : "");
                    errors.append(status.toString());
                }
            }
            if (errors.length() > 0) {
                throw new IllegalStateException(String.format("Logback configuration error detected: %n%s", errors));
            }
        }
    
        private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext,
                URL url) throws JoranException {
            if (url.toString().endsWith("xml")) {
                JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
                configurator.setContext(loggerContext);
                configurator.doConfigure(url);
            }
            else {
                new ContextInitializer(loggerContext).configureByResource(url);
            }
        }   
    

    loadConfiguration方法主要是执行configureByResourceUrl,该方法通过SpringBootJoranConfigurator或者ContextInitializer的configureByResource进行配置

    小结

    springboot定义了LoggingSystem、AbstractLoggingSystem、Slf4JLoggingSystem,依次继承,而LogbackLoggingSystem则继承Slf4JLoggingSystem,它主要是定义了要加载的默认的配置文件logback-test.groovy, logback-test.xml, logback.groovy, logback.xml,以及loadDefaults方法,通过LOG_LEVEL_PATTERN、LOG_DATEFORMAT_PATTERN、ROLLING_FILE_NAME_PATTERN以及LogbackConfigurator来初始化DefaultLogbackConfiguration。

    相关文章

      网友评论

          本文标题:聊聊springboot的LogbackLoggingSyste

          本文链接:https://www.haomeiwen.com/subject/srgwidtx.html