1.前言
說到日志工具,日常工作或學習中肯定聽過這些名詞:log4j、logback、jdk-logging、slf4j、commons-logging等,它們之間有什么關系,在整個日志體系中又扮演什么角色呢?
日志框架分為三大類,包括日志門面、日志適配器、日志庫。利用門面設計模式,即Facade來進行解耦,使日志使用變得更簡單。
2.日志門面
門面設計模式是面向對象設計模式中的一種,日志框架采用的就是這種模式,類似JDBC的設計理念。它只提供一套接口規范,自身不負責日志功能的實現,目的是讓使用者不需要關注底層具體是哪個日志庫來負責日志打印機具體的使用細節等。目前用得最為廣泛的日志門面有兩種:slf4j和commons-logging
3.日志庫
它巨頭實現了日志的相關功能,主流的日志庫有三個,分別是log4j、log-jdk、logback。最早Java想要記錄日志只能通過System.out或System.err來完成,非常不方便。log4j就是為了解決這一問題而提出的,它是最早誕生的日志庫。接着JDK也在1.4版本引入了一個日志庫java.util.logging.Logger,簡稱log-jdk。這樣市面上就出現了兩種日志功能的實現,開發者在使用時需要關注所使用的日志庫的具體細節。logback是最晚出現的,它與log4j出自同一個作者,是log4j的升級版且本身就實現了slf4j的接口。
4.日志適配器
日志適配器分為兩種場景:
-
日志門面適配器,因為slf4j規范是后來提出的,在此之前的日志庫是沒有實現slf4j的接口的,例如log4j.所以,在工程里要想使用slf4j+log4j的模式,就額外需要一個適配器(slf4j+log4j12)來解決接口不兼容的問
-
日志庫適配器,在一些老的工程里,一開始為了開發簡單而直接使用了日志庫API來完成日志打印,隨着時間的推移想將原來直接調用日志庫的模式改為業界標准的門面模式(例如slf4j+logback組合),但老工程代碼里打印日志的地方太多,難以改動,所以需要一個適配器來完成從舊日志庫的API到slf4j的路由,這樣在不改動原有代碼的情況下也能使用slf4j來統一管理日志,而且后續自由替換具體日志庫也不是問題。
5.日志庫的選用
如果是新工程,則推薦使用slf4j+logback模式,因為logback自身實現了slf4j的接口,不需要額外引入適配器,另外logback是log4j的升級版,具備比log4j更多的優點,可通過如下配置進行集成:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback-core.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback-classic.version}</version>
</dependency>
如果是老工程,則需要根據所使用的日志庫來確定門面適配器,通常情況下老工程使用的都是log4j,因此以log4j日志庫為例,可通過如下配置進行集成:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
如果老代碼直接使用了log4j日志庫提供的接口來打印日志,則還需要引入日志庫適配器,配置實例如下所示:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${log4j-over-slf4j.version}</version>
</dependency>
6.logback.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
<property name="application_name" value="web" />
<property name="LOG_PATH" value="c:" />
<!-- 控制台輸出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<pattern>%date %-5level %logger{80} [%X{trans_id}] - %msg%n</pattern>
<!-- <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern> -->
</encoder>
</appender>
<!-- 時間滾動輸出 文件日志 -->
<appender name="file—debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_PATH}/logs/${application_name}/${application_name}_debug.%d{yyyy-MM-dd}_%i.log</FileNamePattern>
<MaxHistory>100</MaxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10mb</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder charset="UTF-8">
<pattern>%date [%thread] %-5level %logger{80} [%X{trans_id}] - %msg%n</pattern>
</encoder>
</appender>
<!-- 時間滾動輸出 文件日志 -->
<appender name="file—info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_PATH}/logs/${application_name}/${application_name}_info.%d{yyyy-MM-dd}_%i.log</FileNamePattern>
<MaxHistory>100</MaxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10mb</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder charset="UTF-8">
<pattern>%date [%thread] %-5level %logger{80} [%X{trans_id}] - %msg%n</pattern>
</encoder>
</appender>
<!-- 時間滾動輸出 level為 ERROR 日志 -->
<appender name="file—error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_PATH}/logs/${application_name}/${application_name}_error.%d{yyyy-MM-dd}_%i.log</FileNamePattern>
<MaxHistory>100</MaxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10mb</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder charset="UTF-8">
<pattern>%date [%thread] %-5level %logger{80} [%X{trans_id}] - %msg%n</pattern>
</encoder>
</appender>
<Logger name="org.apache.http.impl.conn.PoolingHttpClientConnectionManager" level="DEBUG" additivity="false">
<appender-ref ref="file—debug" />
</Logger>
<appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丟失日志.默認的,如果隊列的80%已滿,則會丟棄TRACT、DEBUG、INFO級別的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默認的隊列的深度,該值會影響性能.默認值為256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一個 -->
<appender-ref ref="file—error"/>
</appender>
<root level="info">
<appender-ref ref="stdout" />
<appender-ref ref="file—info" />
<appender-ref ref="file—error" />
</root>
</configuration>
實例代碼如下:
private static final Logger logger =LoggerFactory.getLogger(ConfigureQuartz.class);
注意,logger對象被定義為static變量,這是因為這個logger與當前類綁定,避免每次都new一個新對象,造成資源浪費,甚至引發OutOfMemoryError問題。
在使用slf4j+具體日志庫模式時,由於slf4j相當於充當api抽象接口,所以我們的日志打印是也是面向接口編程的,當我們需要更換具體的日志庫時,我們只需要引入具體的maven依賴就可以了,並對原有的日志庫依賴進行移除,而不需要改動代碼。至此,slf4j的架構原理講解完成,之后會對具體的日志庫logback的配置文件進行講解,本章只是先簡單給出logback.xml配置文件的基本模板,下一章節《日志系列2——logback配置文件詳解》敬請期待。