寫在前面的話:
作為《Java程序員修煉之道》博文的第一個(gè)主題Logging,我計(jì)劃中按照如下三篇來寫:
-
Logback的簡單介紹和配置
-
在Java代碼中如何使用SLF4J來寫日志以及寫日志的要點(diǎn)
-
作為一個(gè)程序員,在日常工作中如何分析和挖掘Log。
1. 緣起
寫代碼中的日志是一個(gè)除了用代碼實(shí)現(xiàn)功能之外最基礎(chǔ)最基礎(chǔ)的一個(gè)技能了,是一個(gè)必須掌握的技能。但是目前為止,關(guān)于如何日志的文章和書籍還是不多。
1.1 寫日志的必要性
碰到QA提的一個(gè)bug的時(shí)候,我見識過兩種方式的答復(fù):a)請給我重現(xiàn)步驟和重現(xiàn)數(shù)據(jù);b)把當(dāng)時(shí)的日志給我。答復(fù)前者的,一般需要花很多時(shí)間去找問題出現(xiàn)在那里,如果是別人開發(fā)的模塊的話,花費(fèi)的時(shí)間更多。答復(fù)后者的,一般能很快的找到出問題的點(diǎn),然后就可以開始進(jìn)入修復(fù)的流程。
從概念上來說:日志是一個(gè)可運(yùn)行的、可維護(hù)的軟件的基礎(chǔ)組成部分;通過日志,我們可以了解軟件系統(tǒng)在運(yùn)行中的實(shí)時(shí)狀態(tài),歷史狀態(tài)和異常狀態(tài)等。一個(gè)沒有良好日志的軟件是所有人的噩夢。
如果你不想給自己找麻煩,你還是把日志好好寫寫。
1.2 為什么選Logback?
原因有倆:
-
我最近幾年用的都是Logback
-
在前幾天Log4j 2.0出來之前,logback的Logger用得比較爽
不過不管是Logback、Log4j還是別的什么Log框架,后繼文章里面的關(guān)于寫Log的一些技巧和建議都是適用的。
2. 如何配置Logback
2.1 Logback簡單介紹
簡單來說就是Log4j 1流行了,發(fā)現(xiàn)有一些問題是無法解決的,于是又出來了Logback,在Log4j的基礎(chǔ)上提升了性能,提高了功能等等。不過前幾天有出來了Log4j 2,據(jù)說是相對于Logbak來說又提升了性能提高了功能。
2.2 關(guān)于SLF4J和Logback
SLF4J(slf4j.org)又稱Simple Logging Facade for Java,是一個(gè)通用的logging接口,它試圖一統(tǒng)Logging框架的天下,兼容了(Log4j 1, java.util.logging和Jakarta Commons Logging)這三個(gè)最流行的Logging框架。Logback就是SLF4J的默認(rèn)實(shí)現(xiàn)。
2.3 依賴包導(dǎo)入
2.3.1 一般程序
Maven版
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency>
非Maven版
把http://logback.qos.ch/dist/logback-1.1.2.zip下載下來之后,在其根目錄下有logback-core-1.1.2.jar和logback-classic-1.1.2.jar這倆個(gè)Jar,在logback-examples\lib下有slf4j-api-1.7.6.jar這個(gè)jar,把這三個(gè)Jar添加到你的代碼包路徑中。
2.3.2 非獨(dú)立運(yùn)行程序
如果你做的是一個(gè)Lib或者API,那么你就不應(yīng)該依賴于具體的slf4j實(shí)現(xiàn)。所以你對logback的依賴應(yīng)該是在運(yùn)行測試代碼的時(shí)候,具體實(shí)現(xiàn)方式如下文所示:
Maven版
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.6</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
<scope>test</scope>
</dependency>
當(dāng)然了你是要把版本信息放在pom.xml中的<dependencies>還是<dependencyManagement>里面就你自己決定吧。
非Maven版
在發(fā)布的時(shí)候不要把只有Logback才用到j(luò)ar打包進(jìn)你的發(fā)布程序里面。(如果覺得繞口再多讀兩遍)
2.4 與遺留Logging框架兼容
目前行業(yè)除了Logback之外,廣泛使用的還有其他四種Logging框架:
Log4j 2因?yàn)槭莿偝鰜淼模壳癝LF4J對其的兼容性還未知,對于其他的三種框架,SLF4J都提供了兼容性的支持。下面介紹了如何讓Logbak兼容這些框架,另外,也可以閱讀官方說明:http://www.slf4j.org/legacy.html
2.4.1 兼容Log4j 1和Apache Commons Logging
SLF4J對于Log4J 1和Apache commons Logging的支持方式是提供了實(shí)現(xiàn)Log4j和Apache commons Logging接口的SLF4J實(shí)現(xiàn)。使用方式是
-
去取對Log4J和Apache commons Logging的Jar包的引用
-
引入SLF4J的對應(yīng)接口的實(shí)現(xiàn)包。
2.4.1.1 移除引用
如果你的系統(tǒng)是直接的使用了Log4j或者Apache commons Logging框架的話,你可以直接把對他們的引用去掉就可以了。如果是你所引用的第三方包里面引用了Log4j或者Apache commons Logging,可以使用<exclusions>標(biāo)簽去掉對他們的引用,如下所示:
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
如何找到哪些第三方包引用了Log4j或者Apache commons Logging呢?有倆個(gè)方法:
-
使用 mvn dependency:tree 命令,如下圖所示,可以看出需要在org.springframework.ldap:spring-ldap-core中排除掉對Apache commons Logging的引用。
-
第二種方式是使用Eclipse的m2e Maven插件。如下圖所示,打開pom.xml文件后,選擇Dependency Hierarchy標(biāo)簽,然后在Filter中輸入logging或者log4j進(jìn)行過濾,在左側(cè)的Dependency Hierarchy中使用右鍵菜單就可以自動(dòng)過濾了。
2.4.1.2 Maven導(dǎo)入對應(yīng)的SLF4J實(shí)現(xiàn)包
<!-- Log4j 的SLF4J 實(shí)現(xiàn) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.6</version>
</dependency>
<!-- Apache commons Logging 的SLF4J實(shí)現(xiàn) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.6</version>
</dependency>
2.4.1.3 非Maven版導(dǎo)入對應(yīng)的SLF4J實(shí)現(xiàn)包
直接刪除掉log4j-1.**.jar和commons-logging-1.**.jar文件,把http://slf4j.org/dist/slf4j-1.7.6.zip下載下來,把壓縮包里的log4j-over-slf4j-1.7.6.jar或者(和)jcl-over-slf4j-1.7.6.jar文件放到classpath中。
2.4.2 兼容java.util.logging
SLF4J的jul-to-slf4j模塊實(shí)現(xiàn)了一個(gè)java.util.logging handler,該handler會(huì)把對java.util.logging的調(diào)用都轉(zhuǎn)化成對SLF4J實(shí)現(xiàn)的調(diào)用。所以需要以下倆個(gè)步驟:
-
導(dǎo)入jul-to-slf4j模塊
-
啟用jul-to-slf4j模塊
2.4.2.1 導(dǎo)入jul-to-slf4j模塊maven版
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.6</version>
</dependency>
2.4.2.2 導(dǎo)入jul-to-slf4j模塊非Maven版
把http://slf4j.org/dist/slf4j-1.7.6.zip下載下來,把壓縮包里的jul-to-slf4j-1.7.6.jar放到classpath中。
2.4.2.3 啟用jul-to-slf4j模塊
在logging.properties中添加如下一行:
handlers = org.slf4j.bridge.SLF4JBridgeHandler
2.5 Logback 配置文件簡介
2.5.1 Logback配置文件名稱、位置和編寫策略
Logback 會(huì)按照如下的順序在classpath中讀取配置文件,如果讀取到任何一個(gè),則停止繼續(xù)尋找。
-
logback.groovy 這個(gè)是使用groovy語法的配置文件
-
logback-test.xml
-
logback.xml
如果以上三個(gè)文件都沒能在classpath中找到,則會(huì)使用
默認(rèn)配置。默認(rèn)配置如下:
-
輸出格式 %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
-
輸出方向:System.out
-
輸出級別:Debug
在我的經(jīng)驗(yàn)中,基本上按照如下策略來配置Logback文件:
對于Maven項(xiàng)目
-
在src/test/resources目錄中放置logback-test.xml,該配置文件中l(wèi)og的輸出是到輸出到console,對于本應(yīng)用的代碼是debug級別,對于其他的是info級別。
-
在src/main/resources目錄中放置logback.xml,該配置文件中l(wèi)og的輸出是輸出到文件中,該文件每日滾動(dòng)壓縮打包備份,對于本應(yīng)用的代碼是info級別,對于其他的是warn級別。
對于非Maven項(xiàng)目
近10年里,我印象中沒有做過非Maven的項(xiàng)目,所以提供不了經(jīng)驗(yàn),只能說說我知道的,供大家參考吧。
-
對于web項(xiàng)目,logback.xml放置到WEB-INF/classes目錄下,配置方式建議參考上面說的
-
對于其他項(xiàng)目,放置到應(yīng)用啟動(dòng)運(yùn)行時(shí)的classpath根目錄下
2.5.2 Logback配置文件示例
介紹Logback配置文件如何編寫的有很多,我就不重復(fù)了,大家可以自己搜索一下。下面有兩篇我覺得寫得不錯(cuò)的,大家可以看一下:
公司項(xiàng)目的配置文件不方便貼出來,下面是我自己的個(gè)人項(xiàng)目中用的,加了一些注釋,供大家參考。
logback.xml<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!--Appendar詳解: http://logback.qos.ch/manual/appenders.html#RollingFileAppender -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 當(dāng)前Log文件名 -->
<file>ldap-pwd.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 非當(dāng)天的Log文件壓縮備份為 archive/ldap-pwd.2014-08-10.zip -->
<fileNamePattern>archive/ldap-pwd.%d{yyyy-MM-dd}.zip</fileNamePattern>
<!-- 超過30天的備份文件會(huì)被刪除 -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<!-- 格式說明:http://logback.qos.ch/manual/layouts.html#ClassicPatternLayout -->
<Pattern>%d [%thread] %-5level %40logger{40} - %msg%n</Pattern>
</layout>
</appender>
<logger name="cn.justfly.training.logging" level="info" />
<root level="warn">
<appender-ref ref="FILE" />
</root>
</configuration>
logback-test.xml<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d [%thread] %-5level %40logger{40} - %msg%n</pattern>
</encoder>
</appender>
<logger name="cn.justfly.training.logging" level="debug" />
<root level="info">
<appender-ref ref="Console" />
</root>
</configuration>