1. 程式人生 > >讓你的spring-boot應用日誌隨心所欲--spring boot日誌深入分析

讓你的spring-boot應用日誌隨心所欲--spring boot日誌深入分析

1.spring boot日誌概述

spring boot使用Commons Logging作為內部的日誌系統,並且給Java Util Logging,Log4J2以及Logback都提供了預設的配置。
如果使用了spring boot的Starters,那麼預設會使用Logback用於記錄日誌。

2.spring boot日誌預設配置

我們啟動一個空的spring-boot專案看一下控制檯的日誌

控制檯的預設配置

logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}

其中%clr為配置不同的顏色輸出,支援的顏色有以下幾種:

  • blue
  • cyan
  • faint
  • green
  • magenta
  • red
  • yellow

輸出順序分析:

1、日期和時間--精確到毫秒,並按照時間進行簡單的排序,格式為:

%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint}


2、日誌級別--ERROR,WARN,INFO,DEBUG,TRACE

%clr(${LOG_LEVEL_PATTERN:-%5p})


3、程序ID號

%clr(${PID:- })


4、日誌內容,用"---"分隔符分開

%clr(---){faint}


5、執行緒名字--括在方括號中

  

%clr([%15.15t]){faint}


6、日誌的名字--通常對應的是類名

  

%clr(%-40.40logger{39}){cyan}

注意:Logback沒有FATAL級別(對映到ERROR)

不同日誌級別對應的顏色如下

3.spring boot日誌配置

可以通過application.properties或者application.yml檢視所有配置

每個配置後面都有說明,就不一一贅述了。

4.spring boot日誌實現原理

 點選配置屬性,可以進入LoggingApplicationListener這個類,

/**
 * An {@link ApplicationListener} that configures the {@link LoggingSystem}. If the
 * environment contains a {@code logging.config} property it will be used to bootstrap the
 * logging system, otherwise a default configuration is used. Regardless, logging levels
 * will be customized if the environment contains {@code logging.level.*} entries and
 * logging groups can be defined with {@code logging.group}.
 * <p>
 * Debug and trace logging for Spring, Tomcat, Jetty and Hibernate will be enabled when
 * the environment contains {@code debug} or {@code trace} properties that aren't set to
 * {@code "false"} (i.e. if you start your application using
 * {@literal java -jar myapp.jar [--debug | --trace]}). If you prefer to ignore these
 * properties you can set {@link #setParseArgs(boolean) parseArgs} to {@code false}.
 * <p>
 * By default, log output is only written to the console. If a log file is required the
 * {@code logging.path} and {@code logging.file} properties can be used.
 * <p>
 * Some system properties may be set as side effects, and these can be useful if the
 * logging configuration supports placeholders (i.e. log4j or logback):
 * <ul>
 * <li>{@code LOG_FILE} is set to the value of path of the log file that should be written
 * (if any).</li>
 * <li>{@code PID} is set to the value of the current process ID if it can be determined.
 * </li>
 * </ul>
 *
 * @author Dave Syer
 * @author Phillip Webb
 * @author Andy Wilkinson
 * @author Madhura Bhave
 * @since 2.0.0
 * @see LoggingSystem#get(ClassLoader)
 */

它實現了GenericApplicationListener介面,它預設定義了日誌組DEFAULT_GROUP_LOGGERS和日誌級別LOG_LEVEL_LOGGERS

private static final Map<String, List<String>> DEFAULT_GROUP_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");
        DEFAULT_GROUP_LOGGERS = Collections.unmodifiableMap(loggers);
    }

    private static final Map<LogLevel, List<String>> LOG_LEVEL_LOGGERS;
    static {
        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");
        LOG_LEVEL_LOGGERS = Collections.unmodifiableMap(loggers);
    }

你也可以自定義logging.level和logging.group,它們都是map結構。LoggingApplicationListener重寫了onApplicationEvent方法,實現日誌的列印

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationStartingEvent) {
            onApplicationStartingEvent((ApplicationStartingEvent) event); //1
        }
        else if (event instanceof ApplicationEnvironmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent(
                    (ApplicationEnvironmentPreparedEvent) event); //2 
        }
        else if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent((ApplicationPreparedEvent) event); //3
        }
        else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
                .getApplicationContext().getParent() == null) {
            onContextClosedEvent();  //4
        }
        else if (event instanceof ApplicationFailedEvent) {
            onApplicationFailedEvent();  //5
        }
    }

第一步:根據classloader里加載的依賴決定使用哪個日誌系統?

主要實現有JavaLoggingSystem,Log4J2LoggingSystem,LogbackLoggingSystem

    private void onApplicationStartingEvent(ApplicationStartingEvent event) {
        this.loggingSystem = LoggingSystem
                .get(event.getSpringApplication().getClassLoader());
        this.loggingSystem.beforeInitialize();
    }

第二步:通過classpath,enviroment等獲取引數初始化日誌系統

    /**
     * Initialize the logging system according to preferences expressed through the
     * {@link Environment} and the classpath.
     * @param environment the environment
     * @param classLoader the classloader
     */
    protected void initialize(ConfigurableEnvironment environment,
            ClassLoader classLoader) {
        new LoggingSystemProperties(environment).apply();
        LogFile logFile = LogFile.get(environment);
        if (logFile != null) {
            logFile.applyToSystemProperties();
        }
        initializeEarlyLoggingLevel(environment);
        initializeSystem(environment, this.loggingSystem, logFile);
        initializeFinalLoggingLevels(environment, this.loggingSystem);
        registerShutdownHookIfNecessary(environment, this.loggingSystem);
    }

第三步:註冊springBootLoggingSystem

    private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
        ConfigurableListableBeanFactory beanFactory = event.getApplicationContext()
                .getBeanFactory();
        if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
            beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
        }
    }

第四步和第五步:日誌系統清洗

    private void onContextClosedEvent() {
        if (this.loggingSystem != null) {
            this.loggingSystem.cleanUp();
        }
    }

    private void onApplicationFailedEvent() {
        if (this.loggingSystem != null) {
            this.loggingSystem.cleanUp();
        }
    }

 

5.自定義配置檔案

日誌系統自定義配置檔案

Logback

logback-spring.xmllogback-spring.groovylogback.xml, or logback.groovy

Log4j2

log4j2-spring.xml or log4j2.xml

JDK (Java Util Logging)

logging.properties

 

 

6.總結

  spring boot日誌系統封裝了logback,log4j2和java log,預設情況下使用java log,一旦使用各種starts,則預設使用Log4J2,也可以通過classpath來改變,pom.xml指定

<dependency> 
 <groupId>org.springframework.boot</groupId> 
 <artifactId>spring-boot-starter</artifactId> 
 <exclusions> 
  <exclusion> 
   <groupId>org.springframework.boot</groupId> 
   <artifactId>spring-boot-starter-logging</artifactId> 
  </exclusion> 
 </exclusions> 
</dependency> 
<dependency> 
 <groupId>org.springframework.boot</groupId> 
 <artifactId>spring-boot-starter-log4j</artifactId> 
</dependency> 

 

 

參考資料

【1】https://docs.spring.io/spring-boot/docs/2.1.2.RELEASE/reference/htmlsingle/#boot-features-logging-format

【2】https://www.jb51.net/article/133795.htm

&n