1. Java 日誌體系
1.1 市面上常用的日誌框架
日誌門面技術有 JCL(Jakarta Commons Logging)、SLF4j(Simple Logging Facade for Java)、jboss-logging,它們都不提供具體的日誌實現
日誌實現技術有 Log4j、JUL(java.util.logging)、Log4j2、Logback ,它們都提供了不同的 API 使用
1.2 SLF4J 技術
1.2.1 SLF4J 綁定器
每一個日誌的實現框架都有自己的配置文件。使用 SLF4J 以後,配置文件還是做成日誌實現框架自己本身的配置文件
用戶除了需要引入 SLF4J API 的 jar 包外,還需要引入 SLF4J 對日誌實現的綁定包
1.2.2 SLF4J 橋接器
SLF4J 橋接器是爲了解決 Jar 包衝突的問題,即有些第三方 Jar 包可能直接使用 Log4j 打印,然後系統中使用的是 SLF4J + Logback 打印,那麼就會出現兩種日誌
使用 SLF4J 橋接器的步驟是:先把其他日誌實現包排除掉,然後使用橋接包來替換,最後導入 SLF4J 綁定器的實現包
2. SpringBoot 日誌
2.1 SpringBoot 日誌關係
SpringBoot 底層是使用 SLF4J + Logback 的方式進行日誌記錄,同時引入了 jul-to-slf4j
和 log4j-to-slf4j
兩個橋接包,從而將 JUL 和 Log4j 的日誌轉到 SLF4J 上,然後再統一使用 Logback 進行日誌輸出
2.2 SpringBoot 日誌使用
代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見
tutorial-spring-boot-core/tutorial-spring-boot-log
工程
2.2.1 配置文件內容
1. application.properties
# 全局日誌配置
logging.level.root=info
# 精確到包名
logging.level.pers.masteryourself.tutorial.spring.boot.log=debug
# 可以指定完整的路徑, 如 D:/springboot.log, 也可以使用 ./ 表示當前路徑
logging.file=./spring.log
# 在當前磁盤的根路徑下創建 spring 文件夾和裏面的 log 文件夾, 使用 spring.log 作爲默認文件, 這兩個配置無法配合使用
logging.path=/spring/log
# 在控制檯輸出的日誌的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
# 指定文件中日誌輸出的格式
logging.pattern.file=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
2.2.2 配置文件位置
Logback:logback-spring.xml
, logback-spring.groovy
, logback.xml
, logback.groovy
Log4j2: log4j2-spring.xml
, log4j2.xml
JUL: logging.properties
如果是原生的 log 配置文件,那麼會直接被日誌框架識別
如果加上了 spring, 例如 logback-spring.xml
或者 log4j2-spring.xml
, 那麼日誌框架不會直接加載日誌的配置項, 而是由 SpringBoot 解析,可以使用 SpringBoot 的高級 Profile 功能
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<!-- 在 dev 環境下生效 -->
<springProfile name="dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
</springProfile>
<!-- 在非 dev 環境下生效 -->
<springProfile name="!dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
</springProfile>
</layout>
</appender>
2.3 切換日誌框架爲 log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
2.4 日誌模板
1. logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:當此屬性設置爲 true 時,配置文件如果發生改變,將會被重新加載,默認值爲 true
scanPeriod:設置監測配置文件是否有修改的時間間隔,如果沒有給出時間單位,默認單位是毫秒當 scan 爲 true 時, 此屬性生效。默認的時間間隔爲 1 分鐘
debug:當此屬性設置爲 true 時,將打印出 logback 內部日誌信息,實時查看 logback 運行狀態。默認值爲 false
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<!-- 定義日誌的根目錄 -->
<property name="LOG_HOME" value="/app/log"/>
<!-- 定義日誌文件名稱 -->
<property name="appName" value="tutorial-spring"/>
<!-- ch.qos.logback.core.ConsoleAppender 表示控制檯輸出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--
日誌輸出格式:
%d 表示日期時間
%thread 表示線程名
%-5level:級別從左顯示 5 個字符寬度
%logger{50} 表示 logger 名字最長 50 個字符,否則按照句點分割
%msg:日誌消息
%n 是換行符
-->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</layout>
</appender>
<!-- 滾動記錄文件,先將日誌記錄到指定文件,當符合某個條件時,將日誌記錄到其他文件 -->
<appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 指定日誌文件的名稱 -->
<file>${LOG_HOME}/${appName}.log</file>
<!--
當發生滾動時,決定 RollingFileAppender 的行爲,涉及文件移動和重命名
TimeBasedRollingPolicy: 最常用的滾動策略,它根據時間來制定滾動策略,既負責滾動也負責出發滾動。
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
滾動時產生的文件的存放位置及文件名稱 %d{yyyy-MM-dd}:按天進行日誌滾動
%i:當文件大小超過 maxFileSize 時,按照 i 進行文件滾動
-->
<fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!--
可選節點,控制保留的歸檔文件的最大數量,超出數量就刪除舊文件。假設設置每天滾動
且 maxHistory 是 365,則只保存最近 365 天的文件,刪除之前的舊文件
-->
<MaxHistory>365</MaxHistory>
<!--
當日志文件超過 maxFileSize 指定的大小是,根據上面提到的 %i 進行日誌文件滾動
注意此處配置 SizeBasedTriggeringPolicy 是無法實現按文件大小進行滾動的,必須配置 timeBasedFileNamingAndTriggeringPolicy
-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日誌輸出格式: -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
</layout>
</appender>
<!--
logger 主要用於存放日誌對象,也可以定義日誌類型、級別
name:表示匹配的 logger 類型前綴,也就是包的前半部分
level:要記錄的日誌級別,包括 TRACE < DEBUG < INFO < WARN < ERROR
additivity:作用在於 children-logger 是否使用 rootLogger 配置的 appender 進行輸出
false:表示只用當前 logger 的 appender-ref
true:表示當前 logger 的 appender-ref 和 rootLogger 的 appender-ref 都有效
-->
<!-- tutorial-spring logger -->
<logger name="pers.masteryourself.tutorial.spring" level="info"/>
<!-- Spring framework logger -->
<logger name="org.springframework" level="info" ref="stdout" additivity="false">
<appender-ref ref="stdout"/>
</logger>
<!--
root 與 logger 是父子關係,沒有特別定義則默認爲 root,任何一個類只會和一個 logger 對應
要麼是定義的 logger,要麼是 root,判斷的關鍵在於找到這個 logger,然後判斷這個 logger 的 appender 和 level
-->
<root level="info">
<appender-ref ref="stdout"/>
<appender-ref ref="appLogAppender"/>
</root>
</configuration>
2. log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- log4j2 自身日誌級別 -->
<Configuration status="WARN">
<Properties>
<!-- 定義日誌文件名稱 -->
<Property name="APP_ID" value="tutorial-spring"/>
<!-- 定義日誌的根目錄 -->
<Property name="LOG_PATH" value="/app/log/${APP_ID}"/>
</Properties>
<Appenders>
<RollingFile name="RollingFile" fileName="${LOG_PATH}/app.log"
filePattern="${LOG_PATH}/app-%d{yyyyMMdd}-%i.log">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
<Policies>
<!--以天爲單位觸發滾動操作,modulate 設置爲 true,會以 00:00 作爲起始時間,每天 00:00 進行一次滾動-->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="1GB"/>
</Policies>
<!--每天最多保存 5G 日誌,最多保存 5 天-->
<DefaultRolloverStrategy max="5">
<Delete basePath="${LOG_PATH}" maxDepth="1">
<IfFileName glob="app-*.log"/>
<IfLastModified age="7d"/>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<logger name="pers.masteryourself.tutorial.spring" level="warn"/>
<root level="INFO">
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="Console"/>
</root>
</Loggers>
</Configuration>
3. log4j.properties
### set log levels ###
log4j.rootLogger=info , stdout , D , E
### 輸出到控制檯 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n
#### 輸出到日誌文件 ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/log.log
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = DEBUG ## 輸出DEBUG級別以上的日誌
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#### 保存異常信息到單獨文件 ###
#log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.E.File = logs/error.log ## 異常日誌文件名
#log4j.appender.E.Append = true
#log4j.appender.E.Threshold = ERROR ## 只輸出ERROR級別以上的日誌!!!
#log4j.appender.E.layout = org.apache.log4j.PatternLayout
#log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n