SpringBoot实现日志收集原理

// 用法
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
        // 获取生成Logger的工厂,通过该工厂生成Logger对象
        // 该ILoggerFactory对象实际上就是Logback的LoggerContext上下文对象
        // 这个系统都只有唯一的LoggerContext上下文,即只有一个LoggerFactory
        // 因此,LoggerFactory.getLogger虽然调用的是静态方法,但最终都是通过系统的单例ILoggerFactory(LoggerContext)对象来创建Logger对象
        // 即getLogger最终都会调用ch.qos.logback.classic.LoggerContext.getLogger(java.lang.String)
        // 因此,只需要根据指定的日志框架配置文件初始化唯一的一份LoggerContext,从而整个项目获取的Logger对象,都是统一的配置的,也就是同一个LoggerContext生成的
        // 并且生成的Logger对象,相同名称的Logger会被缓存起来
        ILoggerFactory logFactory = LoggerFactory.getILoggerFactory();
        // 如果Logger的名称为ROOT,是一个特殊的Logger,全局只有一份ROOT的Logger对象,并且默认的级别为Debug
        Logger luck = logFactory.getLogger("ROOT");
        Logger logger = LoggerFactory.getLogger(App.class);
        Logger logger1 = LoggerFactory.getLogger(App.class);
        logger.error("hello error");
    }
}

// SpringBoot的日志是由这个监听器来处理的它是一个ApplicationListener
// 为什么日志相关的配置都需要保存到系统变量中?
// 因为只有放在系统变量中,其他的Java日志框架才能获取到对应的变量值,从而按照一定规则,配置或者格式打印日志
public class LoggingApplicationListener implements GenericApplicationListener {
    // 日志级别
    private static final ConfigurationPropertyName LOGGING_LEVEL = ConfigurationPropertyName.of("logging.level");
    // 日志分组
    private static final ConfigurationPropertyName LOGGING_GROUP = ConfigurationPropertyName.of("logging.group");
    // 日志级别的map, 日志名称 -> 日志级别的映射,例如: luck.spring: debug
    private static final Bindable<Map<String, LogLevel>> STRING_LOGLEVEL_MAP = Bindable.mapOf(String.class, LogLevel.class);
    // 日志分组的map,分组名称->所有日志名称的集合映射, web -> ["org.a","org.b"]
    private static final Bindable<Map<String, List<String>>> STRING_STRINGS_MAP = Bindable.of(ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class).asMap());
    // LoggingApplicationListener的默认顺序
    public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 20;
    // logback.xml的配置文件路径
    public static final String CONFIG_PROPERTY = "logging.config";
    // 是否允许注册钩子函数的变量配置
    public static final String REGISTER_SHUTDOWN_HOOK_PROPERTY = "logging.register-shutdown-hook";
    // SpringBoog日志系统的Bean名称
    public static final String LOGGING_SYSTEM_BEAN_NAME = "springBootLoggingSystem";
    // SpringBoog日志文件配置的Bean名称
    public static final String LOG_FILE_BEAN_NAME = "springBootLogFile";
    // SpringBoog日志分组的Bean名称
    public static final String LOGGER_GROUPS_BEAN_NAME = "springBootLoggerGroups";
    // 当前日志监听器支持的事件类型
    private static final Class<?>[] EVENT_TYPES = {ApplicationStartingEvent.class, ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class, ContextClosedEvent.class, ApplicationFailedEvent.class};
    // 是否允许注册关闭的钩子
    private static final AtomicBoolean shutdownHookRegistered = new AtomicBoolean();
    // 打印日志的对象,通过门面获取
    private final Log logger = LogFactory.getLog(getClass());
    // 使用具体的打印系统,也就是具体的日志实现,LoggingSystem为抽象类,默认实现为LogbackLoggingSystem
    // private LoggingSystem loggingSystem;
    private LogbackLoggingSystem loggingSystem;
    // 日志文件配置信息
    private LogFile logFile;
    // 日志分组信息
    private LoggerGroups loggerGroups;
    // SpringBoot的日志级别
    private LogLevel springBootLogging = null;
    // 默认的日志分组,以及对应的日志名称
    private static final Map<String, List<String>> DEFAULT_GROUP_LOGGERS;
    // SpringBoot的日志分组,这是在SpringBoot内置打印日志的配置分组和日志级别信息
    private static final Map<LogLevel, List<String>> SPRING_BOOT_LOGGING_LOGGERS;

    static {
        // 默认的日志分组,以及对应的日志名称
        MultiValueMap<String, String> loggers = new LinkedMultiValueMap<>();
        loggers.add("web", "org.springframework.core.codec");
        loggers.add("web", "org.springframework.http");
        loggers.add("web", "org.springframework.web");
        loggers.add("web", "org.springframework.boot.actuate.endpoint.web");
        loggers.add("web", "org.springframework.boot.web.servlet.ServletContextInitializerBeans");
        loggers.add("sql", "org.springframework.jdbc.core");
        loggers.add("sql", "org.hibernate.SQL");
        loggers.add("sql", "org.jooq.tools.LoggerListener");
        DEFAULT_GROUP_LOGGERS = Collections.unmodifiableMap(loggers);

        // SpringBoot的日志分组,这是在SpringBoot内置打印日志的配置分组和日志级别信息
        MultiValueMap<LogLevel, String> loggers = new LinkedMultiValueMap<>();
        loggers.add(LogLevel.DEBUG, "sql");
        loggers.add(LogLevel.DEBUG, "web");
        loggers.add(LogLevel.DEBUG, "org.springframework.boot");
        loggers.add(LogLevel.TRACE, "org.springframework");
        loggers.add(LogLevel.TRACE, "org.apache.tomcat");
        loggers.add(LogLevel.TRACE, "org.apache.catalina");
        loggers.add(LogLevel.TRACE, "org.eclipse.jetty");
        loggers.add(LogLevel.TRACE, "org.hibernate.tool.hbm2ddl");
        SPRING_BOOT_LOGGING_LOGGERS = Collections.unmodifiableMap(loggers);
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        // SpringApplication启动事件
        if (event instanceof ApplicationStartingEvent) {
            this.onApplicationStartingEvent((ApplicationStartingEvent) event);
        }
        // 环境预处理完成事件
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
        }
        // 上下文已经处理好了的回调
        if (event instanceof ApplicationPreparedEvent) {
            this.onApplicationPreparedEvent((ApplicationPreparedEvent) event);
        }
        // 容器关闭事件
        if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
            this.onContextClosedEvent();
        }
        // 容器启动失败
        if (event instanceof ApplicationFailedEvent) {
            this.onApplicationFailedEvent();
        }
    }

    // SpringApplication启动事件
    private void onApplicationStartingEvent(ApplicationStartingEvent event) {
        // 初始化日志系统,就是具体打印日志的实现
        this.loggingSystem = LogbackLoggingSystem.get(event.getSpringApplication().getClassLoader());
        // 初始化日志系统
        this.loggingSystem.beforeInitialize();
    }

    // 环境预处理完成事件
    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        SpringApplication springApplication = event.getSpringApplication();
        // 如果日志系统没有实例化
        if (this.loggingSystem == null) {
            // 获取一个日志系统
            this.loggingSystem = LogbackLoggingSystem.get(springApplication.getClassLoader());
        }
        // 初始化日志配置信息
        this.initialize(event.getEnvironment(), springApplication.getClassLoader());
    }

    // 上下文已经处理好了的回调
    private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
        ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
        // 将日志系统设置为Bean
        if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
            beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
        }
        // 将日志文件配置信息注册为Bean
        if (this.logFile != null && !beanFactory.containsBean(LOG_FILE_BEAN_NAME)) {
            beanFactory.registerSingleton(LOG_FILE_BEAN_NAME, this.logFile);
        }
        // 将日志分组信息注册为Bean
        if (this.loggerGroups != null && !beanFactory.containsBean(LOGGER_GROUPS_BEAN_NAME)) {
            beanFactory.registerSingleton(LOGGER_GROUPS_BEAN_NAME, this.loggerGroups);
        }
    }

    // 清理日志系统信息
    private void onContextClosedEvent() {
        if (this.loggingSystem != null) {
            this.loggingSystem.cleanUp();
        }
    }

    // 清理日志系统信息
    private void onApplicationFailedEvent() {
        if (this.loggingSystem != null) {
            this.loggingSystem.cleanUp();
        }
    }

    // 初始化日志配置信息
    protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
        // 获取日志系统的配置属性,最终都是返回一个new LoggingSystemProperties(environment)对象,从环境对象中获取配置属性
        LoggingSystemProperties systemProperties = this.getLoggingSystemProperties(environment);
        // 初始化日志配置信息,保存到系统变量中
        systemProperties.apply();
        // 初始化日志文件配置
        this.logFile = LogFile.get(environment);
        // 如果存在日志文件相关配置
        if (this.logFile != null) {
            // 将当前对象中路径和文件名保存到系统变量中
            this.logFile.applyToSystemProperties();
        }
        // 日志分组,DEFAULT_GROUP_LOGGERS在静态代码块了设置了很多分组
        this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
        // 初始化SpirngBoot的日志级别,取决于配置变量的debug和trace配置,这里是提前设置,最后会被覆盖
        this.initializeEarlyLoggingLevel(environment);
        // 初始化日志系统
        this.initializeSystem(environment, this.loggingSystem, this.logFile);
        // 初始化最终的日志级别
        this.initializeFinalLoggingLevels(environment, this.loggingSystem);
        // 是否有必要注册解析的钩子函数
        this.registerShutdownHookIfNecessary(environment, this.loggingSystem);
    }

    // 获取日志系统的配置属性,最终都是返回一个new LoggingSystemProperties(environment)对象,从环境对象中获取配置属性
    private LoggingSystemProperties getLoggingSystemProperties(ConfigurableEnvironment environment) {
        return (this.loggingSystem != null) ? this.loggingSystem.getSystemProperties(environment) : new LoggingSystemProperties(environment);

    }

    // 初始化SpirngBoot的日志级别
    private void initializeEarlyLoggingLevel(ConfigurableEnvironment environment) {
        // 如果需要解析参数,并且SpringBoot的日志级别还未初始化
        if (this.parseArgs && this.springBootLogging == null) {
            // 如果设置了debug这个key,则springboot的日志级别设为DEBUG
            if (this.isSet(environment, "debug")) {
                this.springBootLogging = LogLevel.DEBUG;
            }
            // 如果设置了trace这个key,则springboot的日志级别设为TRACE
            if (this.isSet(environment, "trace")) {
                this.springBootLogging = LogLevel.TRACE;
            }
        }
    }

    // 配置属性是否设置了debug,或者trace的key
    private boolean isSet(ConfigurableEnvironment environment, String property) {
        // 从环境对象中获取指定属性的属性值
        String value = environment.getProperty(property);
        // 返回对应的值是否为true
        return (value != null && !value.equals("false"));
    }

    // 初始化日志系统(日志实现)
    private void initializeSystem(ConfigurableEnvironment environment, LogbackLoggingSystem system, LogFile logFile) {
        // 获取logging.config配置的值
        String logConfig = StringUtils.trimWhitespace(environment.getProperty(CONFIG_PROPERTY));
        LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
        // 是否忽略该logging.config配置的logback配置文件路径
        if (this.ignoreLogConfig(logConfig)) {
            // 如果忽略,那么,就使用默认路径logback配置文件
            system.initialize(initializationContext, null, logFile);
        } else {
            // 指定了logback配置文件路径,加载指定路径的配置文件
            system.initialize(initializationContext, logConfig, logFile);
        }
    }

    // 是否忽略该logging.config配置的logback配置文件路径
    private boolean ignoreLogConfig(String logConfig) {
        // 如果没有配置logging.config,或者值为-D开头,则忽略该配置文件
        return !StringUtils.hasLength(logConfig) || logConfig.startsWith("-D");
    }

    // 初始化最终的日志级别
    private void initializeFinalLoggingLevels(ConfigurableEnvironment environment, LogbackLoggingSystem system) {
        // 绑定日志分组
        this.bindLoggerGroups(environment);
        // 如果之前设置了SpringBoot的日志级别
        if (this.springBootLogging != null) {
            // 重新初始化SpringBoot的日志级别
            // 这里设置的是SpringBoot自身对不同包的日志级别配置
            this.initializeSpringBootLogging(system, this.springBootLogging);
        }
        // 绑定logging.level的值,设置日志的级别,包括分组中的所有日志名称
        // 这里是用户自己在配置文件配置的日志名称->日志级别
        this.setLogLevels(system, environment);
    }

    // 绑定日志分组
    private void bindLoggerGroups(ConfigurableEnvironment environment) {
        if (this.loggerGroups != null) {
            // 将logging.group的配置绑定到MultiValueMap对象中,设置到loggerGroups变量中
            Binder binder = Binder.get(environment);
            binder.bind(LOGGING_GROUP, STRING_STRINGS_MAP).ifBound(this.loggerGroups::putAll);
        }
    }


    // 初始化SpringBoot的日志级别
    // 这里设置的是SpringBoot自身对不同包的日志级别配置,和我们自身没多大关系,我们自身的配置在logging.level中
    protected void initializeSpringBootLogging(LogbackLoggingSystem system, LogLevel springBootLogging) {
        // 获取日志级别的配置器
        BiConsumer<String, LogLevel> configurer = this.getLogLevelConfigurer(system);
        // 获取SpringBoot日志级别对应的所有日志名称,设置这些日志名称的日志分组和日志级别
        // 这里设置的是SpringBoot自身对不同包的日志级别配置,和我们自身没多大关系,我们自身的配置在logging.level中
        SPRING_BOOT_LOGGING_LOGGERS.getOrDefault(springBootLogging, Collections.emptyList()).forEach((name) -> this.configureLogLevel(name, springBootLogging, configurer));
    }

    // 绑定logging.level的值,设置日志的级别,包括分组中的所有日志名称
    // 这里是用户自己在配置文件配置的日志名称->日志级别
    protected void setLogLevels(LogbackLoggingSystem system, ConfigurableEnvironment environment) {
        // 获取日志级别的配置器
        BiConsumer<String, LogLevel> customizer = this.getLogLevelConfigurer(system);
        Binder binder = Binder.get(environment);
        // 将环境配置中的logging.level变量值对应的级别绑定到Map<String, LogLevel>中,name->level
        // 这里是用户自己在配置文件配置的日志名称->日志级别
        Map<String, LogLevel> levels = binder.bind(LOGGING_LEVEL, STRING_LOGLEVEL_MAP).orElseGet(Collections::emptyMap);
        // 遍历所有配置的name->日志级别的映射,一一设置这些日志名称的日志分组和日志级别
        levels.forEach((name, level) -> this.configureLogLevel(name, level, customizer));
    }

    // 设置这些日志名称的日志分组和日志级别
    private void configureLogLevel(String name, LogLevel level, BiConsumer<String, LogLevel> configurer) {
        // 如果存在日志分组
        if (this.loggerGroups != null) {
            // 获取到当前日志名称的分组
            LoggerGroup group = this.loggerGroups.get(name);
            // 如果该日志名称存在分组信息,并且其他的日志名称
            if (group != null && group.hasMembers()) {
                // 配置该分组的日志级别,将该分组的所有日志名称都设置为统一日志级别
                group.configureLogLevel(level, configurer);
                return;
            }
        }
        // 配置单个日志名称的日志级别
        configurer.accept(name, level);
    }

    // 获取日志级别的配置器
    private BiConsumer<String, LogLevel> getLogLevelConfigurer(LogbackLoggingSystem system) {
        return new BiConsumer<String, LogLevel>() {
            @Override
            public void accept(String name, LogLevel logLevel) {
                try {
                    // 获取日志系统的日志名称,默认为ROOT
                    name = name.equalsIgnoreCase(LogbackLoggingSystem.ROOT_LOGGER_NAME) ? null : name;
                    // 设置对应名称的日志级别
                    system.setLogLevel(name, level);
                } catch (RuntimeException ex) {
                    this.logger.error(LogMessage.format("Cannot set level '%s' for '%s'", level, name));
                }
            }
        };
    }

    // 是否有必要注册解析的钩子函数
    private void registerShutdownHookIfNecessary(Environment environment, LogbackLoggingSystem loggingSystem) {
        // 如果系统变量值中存在logging.register-shutdown-hook值,默认为true
        if (environment.getProperty(REGISTER_SHUTDOWN_HOOK_PROPERTY, Boolean.class, true)) {
            // 获取对应的构造函数
            Runnable shutdownHandler = loggingSystem.getShutdownHandler();
            // 如果允许注册钩子函数
            if (shutdownHandler != null && shutdownHookRegistered.compareAndSet(false, true)) {
                // 注册钩子函数
                SpringApplication.getShutdownHandlers().add(shutdownHandler);
            }
        }
    }

    /**
     * 设置用于Spring Boot和相关库的自定义日志级别
     */
    public void setSpringBootLogging(LogLevel springBootLogging) {
        this.springBootLogging = springBootLogging;
    }

}

// 不同日志实现对应不同的配置属性,这是一个抽象类,我们讲logback
public class LoggingSystemProperties {
    // 日志文件配置的路径保存在系统变量中的KEY
    public static final String LOG_PATH = "LOG_PATH";
    public static final String LOG_FILE = "LOG_FILE";


    // 将key和value设置到系统变量中
    protected final void setSystemProperty(String name, String value) {
        // 解析Spel表达式
        String value = resolver.getProperty(propertyName);
        value = (value != null) ? value : defaultValue;
        // 如果系统变量中不存在,则添加到系统变量中
        if (System.getProperty(name) == null && value != null) {
            System.setProperty(name, value);
        }
    }

    // 执行属性配置
    protected void apply(LogFile logFile, PropertyResolver resolver) {
        // LoggingSystemProperties:公共逻辑
        super.apply(logFile, resolver) {
            // 如果系统变量中不存在该配置,从环境对象中获取logging.exception-conversion-word,并设置都系统变量中
            this.setSystemProperty(resolver, EXCEPTION_CONVERSION_WORD, "logging.exception-conversion-word")
            // 下面的属性都是一样,从环境对象中获取,也就是从配置文件或者命令行中获取
            this.setSystemProperty(PID_KEY, new ApplicationPid().toString());
            this.setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "logging.pattern.console");
            this.setSystemProperty(resolver, CONSOLE_LOG_CHARSET, "logging.charset.console", getDefaultCharset().name());
            this.setSystemProperty(resolver, LOG_DATEFORMAT_PATTERN, "logging.pattern.dateformat");
            this.setSystemProperty(resolver, FILE_LOG_PATTERN, "logging.pattern.file");
            this.setSystemProperty(resolver, FILE_LOG_CHARSET, "logging.charset.file", getDefaultCharset().name());
            this.setSystemProperty(resolver, LOG_LEVEL_PATTERN, "logging.pattern.level");
            // 如果存在日志文件,应用日志文件的属性配置
            if (logFile != null) {
                logFile.applyToSystemProperties();
            }
        }
        // logback逻辑
        // 配置logback专有的属性
        // 如果系统变量中不存在该配置,从环境对象中获取logging.logback.rollingpolicy.file-name-pattern,并设置都系统变量中
        // 第一个参数: 占位符字符串解析器
        // 第二个参数: 系统环境变量中存入的Key
        // 第三个参数: 最新配置文件key的名称
        // 第四个参数: 兼容之前配置文件key的名称,这些名称已经过期不推荐使用
        applyRollingPolicy(resolver, ROLLINGPOLICY_FILE_NAME_PATTERN, "logging.logback.rollingpolicy.file-name-pattern", "logging.pattern.rolling-file-name")
        {
            // 通过最新的配置名称从环境对象中的值,如果存在,解析系统变变量中spel表达式返回
            T value = resolver.getProperty(propertyName, type);
            // 如果环境对象中不存在该Key的值
            if (value == null) {
                // 使用以前过期的配置名称获取值并解析spel
                value = resolver.getProperty(deprecatedPropertyName, type);
            }
            // 如果获取到配置的值
            if (value != null) {
                String stringValue = String.valueOf((value instanceof DataSize) ? ((DataSize) value).toBytes() : value);
                // 将key和value设置到系统变量中
                this.setSystemProperty(systemPropertyName, stringValue);
            }
        }
        applyRollingPolicy(resolver, ROLLINGPOLICY_CLEAN_HISTORY_ON_START, "logging.logback.rollingpolicy.clean-history-on-start", "logging.file.clean-history-on-start");
        applyRollingPolicy(resolver, ROLLINGPOLICY_MAX_FILE_SIZE, "logging.logback.rollingpolicy.max-file-size", "logging.file.max-size", DataSize.class);
        applyRollingPolicy(resolver, ROLLINGPOLICY_TOTAL_SIZE_CAP, "logging.logback.rollingpolicy.total-size-cap", "logging.file.total-size-cap", DataSize.class);
        applyRollingPolicy(resolver, ROLLINGPOLICY_MAX_HISTORY, "logging.logback.rollingpolicy.max-history", "logging.file.max-history");

    }
}

// 日志文件配置对象,内部的变量与系统变量LOG_FILE,LOG_PATH同步
public class LogFile {
    // 文件名配置的变量Key
    public static final String FILE_NAME_PROPERTY = "logging.file.name";
    // 文件路径配置的变量Key
    public static final String FILE_PATH_PROPERTY = "logging.file.path";
    // 文件名
    private final String file;
    // 文件路径
    private final String path;

    // 获取日志文件配置
    public static LogFile get(PropertyResolver propertyResolver) {
        // 获取环境对象中logging.file.name的值
        String file = propertyResolver.getProperty(FILE_NAME_PROPERTY);
        // 获取环境对象中logging.file.path的值
        String path = propertyResolver.getProperty(FILE_PATH_PROPERTY);
        // 存在路径或者文件名,则创建日志文件配置
        if (StringUtils.hasLength(file) || StringUtils.hasLength(path)) {
            return new LogFile(file, path);
        }
        // 如果都没配置,返回null
        return null;
    }

    // 将当前对象中路径和文件名保存到系统变量中
    public void applyToSystemProperties() {
        // 将当前对象中路径和文件名保存到系统变量中
        this.applyTo(System.getProperties());
    }

    // 将当前对象中路径和文件名保存到系统变量中
    public void applyTo(Properties properties) {
        // 将属性key和value(value不为空)保存到properties中,Key为: LOG_PATH
        this.put(properties, LoggingSystemProperties.LOG_PATH, this.path);
        // 将属性key和value(value不为空)保存到properties中,Key为: LOG_FILE
        this.put(properties, LoggingSystemProperties.LOG_FILE, toString());
    }

    // 将当前对象中路径和文件名保存到系统变量中
    private void put(Properties properties, String key, String value) {
        // 如果存在值才会保存到系统变量中
        if (StringUtils.hasLength(value)) {
            properties.put(key, value);
        }
    }
}

// logback对日志系统的实现
public class LogbackLoggingSystem extends Slf4JLoggingSystem {
    // 根日志使用的名称
    public static final String ROOT_LOGGER_NAME = "ROOT";
    // 过期的属性配置,该配置指定logback的配置文件路径,现在改为最新的"logging.config"配置
    private static final String CONFIGURATION_FILE_PROPERTY = "logback.configurationFile";

    // 从spring.factories文件中获取LoggingSystemFactory类型的日志工厂
    private static final LoggingSystemFactory SYSTEM_FACTORY = LoggingSystemFactory.fromSpringFactories();


    // 设置日志实现的日志级别
    public void setLogLevel(String loggerName, LogLevel level) {
        ch.qos.logback.classic.Logger logger = getLogger(loggerName);
        if (logger != null) {
            logger.setLevel(LEVELS.convertSystemToNative(level));
        }
    }

    // 获取日志系统
    public static LoggingSystem get(ClassLoader classLoader) {
        // 获取LoggingSystem.class.getName()的系统属性名,配置日志系统实现的全类名
        String loggingSystemClassName = System.getProperty(SYSTEM_PROPERTY);
        // 如果存在LoggingSystem.class.getName()的系统属性配置
        if (StringUtils.hasLength(loggingSystemClassName)) {
            // 但是配置的为none,直接返回一个没有任何操作的日志系统
            if (NONE.equals(loggingSystemClassName)) {
                return new NoOpLoggingSystem();
            }
            // 使名称返回创建日志系统对象
            Class<?> systemClass = ClassUtils.forName(loggingSystemClassName, classLoader);
            Constructor<?> constructor = systemClass.getDeclaredConstructor(ClassLoader.class);
            constructor.setAccessible(true);
            return (LoggingSystem) constructor.newInstance(classLoader);
        }
        // 如果没有配置LoggingSystem.class.getName()的系统属性值
        // 则从spring.factories文件中获取LoggingSystemFactory类型的日志工厂,直到可以生成日志系统,否则抛出异常
        LoggingSystem loggingSystem = SYSTEM_FACTORY.getLoggingSystem(classLoader);
        Assert.state(loggingSystem != null, "No suitable logging system located");
        return loggingSystem;
    }

    // 创建Logger对象
    private ch.qos.logback.classic.Logger getLogger(String name) {
        LoggerContext factory = getLoggerContext();
        return factory.getLogger(getLoggerName(name));
    }

    // 获取日志名称
    private String getLoggerName(String name) {
        // 如果日志名称为空或者为ROOT,返回根日志名称
        if (!StringUtils.hasLength(name) || Logger.ROOT_LOGGER_NAME.equals(name)) {
            return ROOT_LOGGER_NAME;
        }
        // 否则返回指定的日志名称
        return name;
    }

    // 日志系统关闭的钩子函数
    public Runnable getShutdownHandler() {
        return () -> getLoggerContext().stop();
    }

    // logback的配置属性
    @Override
    public LoggingSystemProperties getSystemProperties(ConfigurableEnvironment environment) {
        return new LoggingSystemProperties(environment);
    }

    // logback支持的配置文件
    @Override
    protected String[] getStandardConfigLocations() {
        return new String[]{"logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"};
    }

    //

    /**
     * 初始化日志系统
     *
     * @param initializationContext 日志初始化上下文,默认为new LoggingInitializationContext(environment);
     * @param configLocation        logback配置文件路径,默认为"logging.config"配置的路径,如果没有配置则为null
     * @param logFile               日志文件的配置
     */
    @Override
    public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
        // 获取日志上下文(创建日志的工厂)
        LoggerContext loggerContext = this.getLoggerContext();
        // 是否已经初始化了,已经初始化了就不处理
        if (this.isAlreadyInitialized(loggerContext)) {
            return;
        }
        // 初始化系统
        if (StringUtils.hasLength(configLocation)) {
            // 初始化特殊的配置,也就是指定了配置文件的路径名称(存在该"logging.config"配置值)
            // 没有指定那就是默认的logback.xml
            this.initializeWithSpecificConfig(initializationContext, configLocation, logFile);
            return;
        }
        // 如果没有指定配置文件路径,则初始化约定(也就是默认的)的配置文件信息
        this.initializeWithConventions(initializationContext, logFile);
        // 标记日志系统已初始化
        this.markAsInitialized(loggerContext);
        if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
            this.getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY + "' system property. Please use 'logging.config' instead.");
        }
    }

    // 初始化特殊的配置,也就是指定了配置文件的路径名称(存在该"logging.config"配置值)
    // 没有指定那就是默认的logback.xml
    private void initializeWithSpecificConfig(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
        // 解析路径中的占位符
        configLocation = SystemPropertyUtils.resolvePlaceholders(configLocation);
        // 加载日志配置文件,并给日志系统进行设置
        this.loadConfiguration(initializationContext, configLocation, logFile);
    }

    // 加载日志配置文件,并给日志系统进行设置
    protected void loadConfiguration(LoggingInitializationContext initializationContext, String location, LogFile logFile) {
        super.loadConfiguration(initializationContext, location, logFile) {
            if (initializationContext != null) {
                // 根据给定的日志文件配置信息重新加载一份配置属性,因为LogFile可以被修改,同时日志文件配置信息需要保存到系统变量中,而且需要同步更新
                this.applySystemProperties(initializationContext.getEnvironment(), logFile) {
                    new LoggingSystemProperties(environment).apply(logFile);
                }
            }
        }
        // 获取日志上下文
        LoggerContext loggerContext = this.getLoggerContext();
        // 停止日志系统并重置配置信息
        this.stopAndReset(loggerContext);
        // 根据logback配置文件路径的,默认为"logging.config"配置的路径,如果没有配置则为null,获取到文件资源
        URL url = ResourceUtils.getURL(location);
        // 加载logback.xml配置文件内容,对日志系统进行配置
        this.configureByResourceUrl(initializationContext, loggerContext, url);
    }

    // 根据当前日志实现(Logback)获取到该实现的配置文件
    protected String getSelfInitializationConfig() {
        // logback支持的配置文件
        // return new String[]{"logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"};
        String[] standardConfigLocations = this.getStandardConfigLocations();
        // 通过支持的配置找符合条件的配置文件
        return this.findConfig(standardConfigLocations);
    }

    // 通过该日志实现(Logback)支持的配置找符合条件的配置文件
    private String findConfig(String[] locations) {
        // 遍历所有的文件路径
        for (String location : locations) {
            // 默认为classpath下的路径
            ClassPathResource resource = new ClassPathResource(location, this.classLoader);
            if (resource.exists()) {
                // 如果classpath下支持的文件名存在,返回该资源信息
                return "classpath:" + location;
            }
        }
        return null;
    }

    // 初始化约定(也就是默认的)的配置文件信息
    private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
        // 根据当前日志实现(Logback)获取到该实现的配置文件
        String config = this.getSelfInitializationConfig();
        // 因为initialize方法是public,这使得我们可以动态的修改日志配置信息
        // 当classpath下,存在默认的配置文件的时候{"logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"}
        // 我们就要根据传入进来的日志文件配置信息做判断,因为LogFile中存入的系统变量的参数可以在logback.xml中应用,例如:"${LOG_PATH}" "${LOG_FILE}";
        // logFile最终的作用是将logFile配置的路径保存到系统变量中通过这两个"${LOG_PATH}" "${LOG_FILE}"Key保存
        // 如果没有给定,那么就不需要对logFile的变量进行操作,直接重新初始化一次,加载默认的配置即可,不用对"${LOG_PATH}" "${LOG_FILE}"进行任何更改
        // 如果给定了,那么就要将logFile变量中的值保存到系统变量中
        if (config != null && logFile == null) {
            // 对日志系统进行重新初始化,重新加载日志系统配置文件,不修改日志文件的系统变量
            this.reinitialize(initializationContext);
            return;
        }
        // 如果不存在约定的的日志配置文件 {"logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"}
        if (config == null) {
            // 再找找与spring相关的logback配置文件
            // 找类路径下的xxx-spring.xxx  (logback-spring.xml)配置
            config = this.getSpringInitializationConfig();
        }
        // 如果找到了logback默认的或者拼接上-spring的配置文件
        if (config != null) {
            // 加载该配置文件信息,并修改日志文件的系统变量
            this.loadConfiguration(initializationContext, config, logFile);
            return;
        }
        // 如果不存在logback.xml配置文件,那么就需要加载lombok的默认配置
        this.loadDefaults(initializationContext, logFile);
    }

    // 加载默认配置
    protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) {
        // 获取日志上下文
        LoggerContext context = this.getLoggerContext();
        // 停止日志系统并重置配置信息
        this.stopAndReset(context);
        // 获取系统变量中,logback.debug的值
        boolean debug = Boolean.getBoolean("logback.debug");
        // 如果开启了debug
        if (debug) {
            // 添加在控制台打印的状态监听器
            StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener());
        }
        // 获取环境对象
        Environment environment = initializationContext.getEnvironment();
        // 在同一个JVM运行多个应用程序的情况下,直接应用系统属性,将系统属性保存到LoggingSystemProperties属性配置中
        new LoggingSystemProperties(environment, context::putProperty).apply(logFile);
        // 创建logback的配置器,可以通过api进行配置,比xml解析要快
        LogbackConfigurator configurator = debug ? new DebugLogbackConfigurator(context) : new LogbackConfigurator(context);
        // 应用日志文件信息,配置默认的logback配置,例如控制台格式等等
        new DefaultLogbackConfiguration(logFile).apply(configurator);
        context.setPackagingDataEnabled(true);
    }

    // 找类路径下的xxx-spring.xxx  (logback-spring.xml)配置
    protected String getSpringInitializationConfig() {
        // 获取spring支持的配置文件名称
        // xxx-spring.xxx  (logback-spring.xml)
        String[] springConfigLocations = this.getSpringConfigLocations();
        // 找类路径下的对应的配置文件
        return this.findConfig(springConfigLocations);
    }

    // 获取spring支持的配置文件名称
    //  xxx-spring.xxx  (logback-spring.xml)
    protected String[] getSpringConfigLocations() {
        // 获取logback约定支持的配置文件
        String[] locations = this.getStandardConfigLocations();
        for (int i = 0; i < locations.length; i++) {
            // 获取扩展名
            String extension = StringUtils.getFilenameExtension(locations[i]);
            // 将路径拼接为 xxx-spring.xxx  (logback-spring.xml)
            locations[i] = locations[i].substring(0, locations[i].length() - extension.length() - 1) + "-spring." + extension;
        }
        return locations;
    }

    // 对日志系统进行重新初始化,重新加载日志系统配置文件,不修改日志文件的系统变量
    protected void reinitialize(LoggingInitializationContext initializationContext) {
        // 重置在日志上下文中保存的原有的信息
        getLoggerContext().reset();
        // 重置状态
        getLoggerContext().getStatusManager().clear();
        // 根据当前日志实现(Logback)获取到该实现的配置文件
        String selfInitializationConfig = this.getSelfInitializationConfig();
        // 重新加载配置文件
        this.loadConfiguration(initializationContext, selfInitializationConfig, null);
    }

    // 获取日志上下文(创建日志的工厂)
    private LoggerContext getLoggerContext() {
        // 获取创建日志对象的工厂
        ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
        // 断言ILoggerFactory必须为LoggerContext的实现
        Assert.isInstanceOf(LoggerContext.class, factory,
                () -> String.format(
                        "LoggerFactory is not a Logback LoggerContext but Logback is on "
                                + "the classpath. Either remove Logback or the competing "
                                + "implementation (%s loaded from %s). If you are using "
                                + "WebLogic you will need to add 'org.slf4j' to "
                                + "prefer-application-packages in WEB-INF/weblogic.xml",
                        factory.getClass(), getLocation(factory)));
        // 返回该日志上下文(创建日志的工厂)
        return (LoggerContext) factory;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值