Logback配置解析
logback優點
比較吸引的幾個優點如下:
- 核心重寫,初始化記憶體載入更小
- 文件比較齊全
- 支援自動重新載入配置檔案,掃描過程快且安全,它並不需要另外建立一個掃描執行緒
- 支援自動去除舊的日誌檔案,可以控制已經產生日誌檔案的最大數量
logback載入
在專案中引入logback依賴:
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency>
啟動專案時,logback會按照如下順序掃描配置檔案:
- 在系統配置檔案System Properties中尋找是否有logback.configurationFile對應的value
- 在classpath下尋找是否有logback.groovy(即logback支援groovy與xml兩種配置方式)
- 在classpath下尋找是否有logback-test.xml
- 在classpath下尋找是否有logback.xml
以上任何一項找到了,就不進行後續掃描,按照對應的配置進行logback的初始化,可從控制檯輸出資訊中檢視載入的配置檔案。
當所有以上四項都找不到的情況下,logback會呼叫ch.qos.logback.classic.BasicConfigurator
的configure方法,構造一個ConsoleAppender用於向控制檯輸出日誌,預設日誌輸出格式為%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
。
ContextInitializer類中相關方法摘錄如下:
final public static String GROOVY_AUTOCONFIG_FILE = "logback.groovy"; final public static String AUTOCONFIG_FILE = "logback.xml"; final public static String TEST_AUTOCONFIG_FILE = "logback-test.xml"; final public static String CONFIG_FILE_PROPERTY = "logback.configurationFile"; ... public URL findURLOfDefaultConfigurationFile(boolean updateStatus) { ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this); URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus); if (url != null) { return url; } url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus); if (url != null) { return url; } url = getResource(GROOVY_AUTOCONFIG_FILE, myClassLoader, updateStatus); if (url != null) { return url; } return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus); } public void autoConfig() throws JoranException { StatusListenerConfigHelper.installIfAsked(loggerContext); URL url = findURLOfDefaultConfigurationFile(true); if (url != null) { configureByResource(url); } else { Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class); if (c != null) { try { c.setContext(loggerContext); c.configure(loggerContext); } catch (Exception e) { throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass().getCanonicalName() : "null"), e); } } else { BasicConfigurator basicConfigurator = new BasicConfigurator(); basicConfigurator.setContext(loggerContext); basicConfigurator.configure(loggerContext); } } }
logback配置檔案解析
-
根節點<configuration>
<!-- 輸出logback內部日誌資訊,每隔30s判斷一下配置檔案有沒有更新,若更新,則重新載入 --> <configuration scan="true" scanPeriod="30 seconds" debug="true"> <!-- scan: 當此屬性設定為true時,配置檔案如果發生改變,將會被重新載入,預設值為true。 scanPeriod: 設定監測配置檔案是否有修改的時間間隔,如果沒有給出時間單位,預設單位是毫秒。當scan為true時,此屬性生效。預設的時間間隔為1分鐘。 debug: 當此屬性設定為true時,將打印出logback內部日誌資訊,實時檢視logback執行狀態。預設值為false。 -->
-
設定變數<property>
<configuration> <!-- 定義日誌檔案的儲存地址 --> <property name="LOG_HOME" value="log"/> <!-- 格式化輸出 --> <property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}][%-5level][%t][%file:%line][%X{traceId}] -| %m%n"/> </configuration>
-
<logger>與<root>
<logger>用來設定某一個包或者具體某一個類的日誌列印級別、以及指定appender。<logger>可以包含零個或者多個<appender-ref>元素,標識這個appender將會新增到這個logger。
<root>也是<logger>元素,但它是根logger,只有一個level屬性,因為它的name就是ROOT
<!-- name:必填,指定受此logger約束的某一個包或者具體的某一個類。這個name表示的是LoggerFactory.getLogger(XXX.class),XXX的包路徑,包路徑越少越是父級 level:用來設定列印級別,五個常用列印級別從低至高依次為TRACE、DEBUG、INFO、WARN、ERROR,如果未設定此級別,那麼當前logger會繼承上級的級別 additivity:是否向上級logger傳遞列印資訊,預設為true --> <logger name="com.example.iot" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT"/> <appender-ref ref="INFO-APPENDER"/> <appender-ref ref="ERROR-APPENDER"/> </logger> <!-- 沒有配置<appender-ref>,表示此<logger>不會打印出任何資訊 --> <logger name="com.example.iot.demo1" level="DEBUG" additivity="true" /> <!-- 沒有配置level,即繼承父級的level,<logger>的父級為<root>,那麼level=info --> <logger name="com.example.iot.demo2" additivity="false" /> <!-- 控制檯輸出日誌級別 --> <root level="INFO"> <appender-ref ref="STDOUT"/> </root>
-
<appender>
負責寫日誌的元件,有兩個必要屬性name和class
- 常用的控制檯輸出:ConsoleAppender
<!-- name指定<appender>的名稱 class指定<appender>的全限定名 --> <!-- 控制檯輸出 appender --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!-- <encoder>表示輸出格式 --> <pattern>${LOG_PATTERN}</pattern> <!-- 控制檯也要使用UTF-8,不要使用GBK,否則會中文亂碼 --> <charset>utf8</charset> </encoder> </appender>
- 常用的滾動記錄檔案:RollingFileAppender
<!-- INFO日誌 appender: 按照每天生成日誌檔案 --> <appender name="INFO-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 過濾器,只記錄 info 級別以上的日誌 --> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <!-- 寫入的日誌檔名,可以使相對目錄也可以是絕對目錄,如果上級目錄不存在則自動建立 --> <file>${LOG_HOME}/iot-sdk-info.log</file> <!-- 如果為true表示日誌被追加到檔案結尾,如果是false表示清空檔案 --> <append>true</append> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!-- 日誌檔案輸出的檔名: %d:可以包含一個Java.text.SimpleDateFormat指定的時間格式 %i:自增數字 --> <fileNamePattern>${LOG_HOME}/iot-sdk-info.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日誌檔案儲存歷史數量:控制保留的歸檔檔案的最大數量,如果超出數量就刪除舊檔案 --> <maxHistory>30</maxHistory> <!-- 檔案大小超過100MB歸檔 --> <maxFileSize>100MB</maxFileSize> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${LOG_PATTERN}</pattern> <charset>utf8</charset> </encoder> </appender>
-
<encoder>
encoder節點負責兩件事情:
- 把日誌資訊轉換為位元組陣列
- 把位元組陣列寫到輸出流
以下是一個常用配置:
<!-- 控制檯輸出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- 用來設定日誌的輸入格式,使用“%+轉換符”的方式,如果要輸出”%”則必須使用”\”進行轉義。--> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!-- 格式化輸出:https://logback.qos.ch/manual/layouts.html %d 表示日期, %thread 表示執行緒名, %level 日誌級別從左顯示5個字元寬度, %t 執行緒名 %file:%line 檔名+行號, %m 日誌訊息,%n是換行符 %X{traceId}:自定義設定的引數,後面會說。 --> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %t %file:%line %X{traceId} -| %m%n</pattern> <charset>utf8</charset> </encoder> </appender>
-
<filter>
配合appender使用,<filter>是<appender>的一個子節點,表示在當前給到的日誌級別下再進行一次過濾
- Level 級別過濾器
根據日誌級別進行過濾。如果日誌級別等於配置級別,過濾器會根據onMath和onMismatch接收(ACCEPT)或拒絕(DENY)日誌。
<!-- 控制檯輸出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- 只記錄error級別的日誌 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${LOG_PATTERN}</pattern> <charset>utf8</charset> </encoder> </appender>
- ThresholdFilter: 臨界值過濾器
將日誌級別低於<level>的全部進行過濾。當日志級別等於或高於臨界值時,過濾器返回NEUTRAL;當日志級別低於臨界值時,日誌會被拒絕。
<!-- 控制檯輸出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- 記錄info級別以上的日誌 --> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${LOG_PATTERN}</pattern> <charset>utf8</charset> </encoder> </appender>
自定義appender
logback提供的appender基本夠用,有時候也免不了有自定義的需求。以下是一個簡單的自定義appender:
package com.example.iot.demo; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.AppenderBase; public class DemoAppender extends AppenderBase<ILoggingEvent> { @Override protected void append(ILoggingEvent eventObject) { // 列印格式化後的日誌資訊 System.out.println(eventObject.getFormattedMessage()); } }
在配置檔案中引入DemoAppender
<appender name="DEMO-APPENDER" class="com.example.iot.demo.DemoAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${LOG_PATTERN}</pattern> <charset>utf8</charset> </encoder> </appender> <logger name="com.example.iot.demo" level="DEBUG" additivity="true"> <appender-ref ref="DEMO-APPENDER"/> </logger>
常用配置
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="30 seconds" debug="true"> <!-- logback的根節點 <configuration>的屬性scan、scanPeriod、debug scan:當此屬性設定為true時,配置檔案如果發生改變,將會被重新載入,預設值為true。 scanPeriod:設定監測配置檔案是否有修改的時間間隔,如果沒有給出時間單位,預設單位是毫秒。當scan為true時,此屬性生效。預設的時間間隔為1分鐘。 debug:當此屬性設定為true時,將打印出logback內部日誌資訊,實時檢視logback執行狀態。預設值為false。 --> <!-- 定義日誌檔案的儲存地址 --> <property name="LOG_HOME" value="log"/> <!-- 格式化輸出: %d表示日期, %thread表示執行緒名, %level日誌級別從左顯示5個字元寬度, %thread執行緒名 %file:%line 檔名:行號, %m日誌訊息, %n換行符, %X{traceId} 自定義設定的引數 %mdc自定義引數 --> <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%5level) -- [%15.15t] %cyan(%-23.23logger{23}) : %m%n"/> <!-- appender是configuration的子節點,是負責寫日誌的元件 --> <!-- 控制檯輸出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${LOG_PATTERN}</pattern> <!-- 控制檯也要使用UTF-8,不要使用GBK,否則會中文亂碼 --> <charset>utf8</charset> </encoder> </appender> <!-- INFO日誌 appender: 按照每天生成日誌檔案 --> <appender name="INFO-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 過濾器,記錄info級別以上的日誌 --> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <!-- 寫入的日誌檔名,可以使相對目錄也可以是絕對目錄,如果上級目錄不存在則自動建立 --> <file>${LOG_HOME}/iot-sdk-info.log</file> <!-- 如果為true表示日誌被追加到檔案結尾,如果是false表示清空檔案 --> <append>true</append> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!-- 日誌檔案輸出的檔名: %d可以包含一個Java.text.SimpleDateFormat指定的時間格式 --> <fileNamePattern>${LOG_HOME}/iot-sdk-info.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日誌檔案儲存歷史數量:控制保留的歸檔檔案的最大數量,如果超出數量就刪除舊檔案 --> <maxHistory>30</maxHistory> <!-- 檔案大小超過100MB歸檔 --> <maxFileSize>100MB</maxFileSize> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${LOG_PATTERN}</pattern> <charset>utf8</charset> </encoder> </appender> <!-- 錯誤日誌 appender: 按照每天生成日誌檔案 --> <appender name="ERROR-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 過濾器,只記錄error級別的日誌 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <!-- 日誌名稱 --> <file>${LOG_HOME}/iot-sdk-error.log</file> <append>true</append> <!-- 每天生成一個日誌檔案,儲存30天的日誌檔案 --> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!-- 日誌檔案輸出的檔名:按天回滾 daily --> <fileNamePattern>${LOG_HOME}/iot-sdk-error.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日誌檔案保留天數 --> <maxHistory>30</maxHistory> <!-- 檔案大小超過100MB歸檔 --> <maxFileSize>100MB</maxFileSize> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${LOG_PATTERN}</pattern> <charset>utf8</charset> </encoder> </appender> <!-- 指定專案中某個包,當有日誌操作行為時的日誌記錄級別 級別依次為【從高到低】:FATAL > ERROR > WARN > INFO > DEBUG > TRACE additivity=false 表示匹配之後,不再繼續傳遞給其他的logger --> <logger name="com.example.iot" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT"/> <appender-ref ref="INFO-APPENDER"/> <appender-ref ref="ERROR-APPENDER"/> </logger> <!-- 控制檯輸出日誌級別 --> <root level="INFO"> <appender-ref ref="STDOUT"/> </root> </configuration>
參考: