返回介绍

Spring 系列

MyBatis

Netty

Dubbo

Tomcat

Redis

Nacos

Sentinel

RocketMQ

番外篇(JDK 1.8)

学习心得

SpringBoot 日志系统

发布于 2024-05-19 21:34:34 字数 19516 浏览 0 评论 0 收藏 0

SpringBoot 日志系统

日志级别

  • 日志级别: org.springframework.boot.logging.LogLevel

    public enum LogLevel {
        TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
    }
    

Java 日志实现

  • org.springframework.boot.logging.java.JavaLoggingSystem

    image-20200323144523848

    static {
        // KEY :  springBoot 定义的日志级别, value: jdk 定义的日志级别
        LEVELS.map(LogLevel.TRACE, Level.FINEST);
        LEVELS.map(LogLevel.DEBUG, Level.FINE);
        LEVELS.map(LogLevel.INFO, Level.INFO);
        LEVELS.map(LogLevel.WARN, Level.WARNING);
        LEVELS.map(LogLevel.ERROR, Level.SEVERE);
        LEVELS.map(LogLevel.FATAL, Level.SEVERE);
        LEVELS.map(LogLevel.OFF, Level.OFF);
    }
    
  • LEVELS 对象

    protected static class LogLevels<T> {
        /**
         * key : SpringBoot 中定义的日志级别, value: 其他日志框架的日志级别
         */
        private final Map<LogLevel, T> systemToNative;
        /**
         * key : 其他日志框架的日志级别 , value: springBoot 中定义中定义的日志级别
         */
        private final Map<T, LogLevel> nativeToSystem;
    }
    

LoggingSystem

  • 抽象类
  • org.springframework.boot.logging.LoggingSystem

  • 一个 map 对象: SYSTEMS

    /**
     * key: 第三方日志框架的类 value: springBoot 中的处理类
     */
    private static final Map<String, String> SYSTEMS;
    
    static {
        Map<String, String> systems = new LinkedHashMap<>();
        systems.put("ch.qos.logback.core.Appender", "org.springframework.boot.logging.logback.LogbackLoggingSystem");
        systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory",
                "org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");
        systems.put("java.util.logging.LogManager", "org.springframework.boot.logging.java.JavaLoggingSystem");
        SYSTEMS = Collections.unmodifiableMap(systems);
    }
    
  • 各个抽象方法

    | 方法名称 | 作用 | | ----------------------- | ---------------------------------- | | beforeInitialize | 初始化之前调用,目的是减少日志输出 | | initialize | 初始化日志 | | cleanUp | 清除日志 | | getShutdownHandler | | | getSupportedLogLevels | 获取支持的日志级别 | | setLogLevel | 设置日志级别 | | getLoggerConfigurations | 获取日志配置 |

get

public static LoggingSystem get(ClassLoader classLoader) {
    // 获取系统属性
    String loggingSystem = System.getProperty(SYSTEM_PROPERTY);

    if (StringUtils.hasLength(loggingSystem)) {
        // 是不是NONE
        if (NONE.equals(loggingSystem)) {
            // 空的日志系统
            return new NoOpLoggingSystem();
        }
        return get(classLoader, loggingSystem);
    }
    // 循环所有日志,
    return SYSTEMS.entrySet().stream().filter((entry) -> ClassUtils.isPresent(entry.getKey(), classLoader))
            .map((entry) ->
            // 实例化具体日志
                get(classLoader, entry.getValue())).findFirst()
            .orElseThrow(() -> new IllegalStateException("No suitable logging system located"));
}
  • 实例化日志系统
private static LoggingSystem get(ClassLoader classLoader, String loggingSystemClass) {
    try {
        Class<?> systemClass = ClassUtils.forName(loggingSystemClass, classLoader);
        Constructor<?> constructor = systemClass.getDeclaredConstructor(ClassLoader.class);
        constructor.setAccessible(true);
        return (LoggingSystem) constructor.newInstance(classLoader);
    }
    catch (Exception ex) {
        throw new IllegalStateException(ex);
    }
}

image-20200323151409473

  • 默认日志: org.springframework.boot.logging.logback.LogbackLoggingSystem

beforeInitialize

  • 初始化之前

    image-20200323154205484

  • 链路

    1. org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationEvent
    2. org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationStartingEvent
    3. org.springframework.boot.logging.LoggingSystem#beforeInitialize
  • 因为前文中我们已知对象是:org.springframework.boot.logging.logback.LogbackLoggingSystem 直接看这个类的 beforeInitialize 方法

    @Override
    public void beforeInitialize() {
        // 日志上下文
        LoggerContext loggerContext = getLoggerContext();
        // 是否初始化
        if (isAlreadyInitialized(loggerContext)) {
            return;
        }
        // 父类方法
        super.beforeInitialize();
        // 添加过滤器
        loggerContext.getTurboFilterList().add(FILTER);
    }
    
  • 初始化之前的的操作完成了初始化方法开始

initialize

  • org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationEnvironmentPreparedEvent

    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        if (this.loggingSystem == null) {
            this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
        }
        initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
    }
    
  • org.springframework.boot.context.logging.LoggingApplicationListener#initializeSystem

    protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
        new LoggingSystemProperties(environment).apply();
        this.logFile = LogFile.get(environment);
        if (this.logFile != null) {
            this.logFile.applyToSystemProperties();
        }
        this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
        // 早期 的日志级别
        initializeEarlyLoggingLevel(environment);
        // 初始化日志系统
        initializeSystem(environment, this.loggingSystem, this.logFile);
        // 初始化日志级别
        initializeFinalLoggingLevels(environment, this.loggingSystem);
        registerShutdownHookIfNecessary(environment, this.loggingSystem);
    }
    
    private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) {
        LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
        String logConfig = environment.getProperty(CONFIG_PROPERTY);
        if (ignoreLogConfig(logConfig)) {
            // 日志系统初始化
            system.initialize(initializationContext, null, logFile);
        }
        else {
            try {
                ResourceUtils.getURL(logConfig).openStream().close();
                system.initialize(initializationContext, logConfig, logFile);
            }
            catch (Exception ex) {
                // NOTE: We can't use the logger here to report the problem
                System.err.println("Logging system failed to initialize using configuration from '" + logConfig + "'");
                ex.printStackTrace(System.err);
                throw new IllegalStateException(ex);
            }
        }
    }
    
  • org.springframework.boot.logging.logback.LogbackLoggingSystem#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.");
        }
    }
    
  • org.springframework.boot.logging.AbstractLoggingSystem#initializeWithConventions

    private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
        String config = getSelfInitializationConfig();
        if (config != null && logFile == null) {
            // self initialization has occurred, reinitialize in case of property changes
            reinitialize(initializationContext);
            return;
        }
        if (config == null) {
            config = getSpringInitializationConfig();
        }
        if (config != null) {
            loadConfiguration(initializationContext, config, logFile);
            return;
        }
        // 加载默认配置
        loadDefaults(initializationContext, logFile);
    }
    
  • org.springframework.boot.logging.logback.LogbackLoggingSystem#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);
    }
    
    @Override
    public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
        LoggerContext loggerContext = getLoggerContext();
        // 是否加载过
        if (isAlreadyInitialized(loggerContext)) {
            return;
        }
        // 日志初始化
        super.initialize(initializationContext, configLocation, logFile);
        // 删除 FILTER
        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.");
        }
    }
    
  • 标记 markAsInitialized

    private void markAsInitialized(LoggerContext loggerContext) {
        loggerContext.putObject(LoggingSystem.class.getName(), new Object());
    }
    

此时日志初始化完成。

默认配置文件

  • getStandardConfigLocations 这个方法定义了默认配置文件有哪些。

    @Override
    protected String[] getStandardConfigLocations() {
        return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
    }
    
  • 切回 org.springframework.boot.logging.AbstractLoggingSystem#initializeWithConventions 方法

  • 添加依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
        <version>${revision}</version>
    </dependency>
    
  • 添加配置文件

    image-20200323161442058

    image-20200323161522570

  • 此时配置文件地址出现了

    protected String getSelfInitializationConfig() {
        // 寻找配置文件
        return findConfig(getStandardConfigLocations());
    }
    
    @Override
    protected String[] getStandardConfigLocations() {
        return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
    }
    
    private String findConfig(String[] locations) {
        for (String location : locations) {
            ClassPathResource resource = new ClassPathResource(location, this.classLoader);
            if (resource.exists()) {
                return "classpath:" + location;
            }
        }
        return null;
    }
    
  • 此时自定义配置文件如何获取的已经明了。

reinitialize

@Override
protected void reinitialize(LoggingInitializationContext initializationContext) {
    // 日志上下文重新设置
    getLoggerContext().reset();
    getLoggerContext().getStatusManager().clear();
    // 加载配置文件
    loadConfiguration(initializationContext, getSelfInitializationConfig(), null);
}
@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")) {
        // logback 日志操作
        JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
        // 设置上下文
        configurator.setContext(loggerContext);
        // 执行配置
        configurator.doConfigure(url);
    }
    else {
        new ContextInitializer(loggerContext).configureByResource(url);
    }
}

执行配置属于 logback 操作源码不在此进行分析。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文