Log Java日志:(slf4j猎醇、log4j若债、logback、common-logging )
- slf4j 是規(guī)范/接口
- 日志實(shí)現(xiàn):log4j减江、logback、common-logging
簡(jiǎn)單地說(shuō)捻爷,Logback 是一個(gè) Java 領(lǐng)域的日志框架辈灼。它被認(rèn)為是 Log4J 的繼承人。
Logback 主要由三個(gè)模塊組成:
- logback-core
- logback-classic
- logback-access
logback-core 是其它模塊的基礎(chǔ)設(shè)施役衡,其它模塊基于它構(gòu)建,顯然薪棒,logback-core 提供了一些關(guān)鍵的通用機(jī)制手蝎。
logback-classic 的地位和作用等同于 Log4J榕莺,它也被認(rèn)為是 Log4J 的一個(gè)改進(jìn)版,并且它實(shí)現(xiàn)了簡(jiǎn)單日志門(mén)面 SLF4J棵介。
logback-access 主要作為一個(gè)與 Servlet 容器交互的模塊钉鸯,比如說(shuō) tomcat 或者 jetty,提供一些與 HTTP 訪問(wèn)相關(guān)的功能邮辽。
根據(jù)不同的日志系統(tǒng)唠雕,你可以按如下規(guī)則組織配置文件名,就能被正確加載:
- Logback:
logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
- Log4j:
log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
- Log4j2:
log4j2-spring.xml, log4j2.xml
- JDK (Java Util Logging):
logging.properties
Logback 與 Log4J
實(shí)際上吨述,這兩個(gè)日志框架都出自同一個(gè)開(kāi)發(fā)者之手岩睁,Logback 相對(duì)于 Log4J 有更多的優(yōu)點(diǎn):
- 同樣的代碼路徑,Logback 執(zhí)行更快
- 更充分的測(cè)試
- 原生實(shí)現(xiàn)了 SLF4J API(Log4J 還需要有一個(gè)中間轉(zhuǎn)換層)
- 內(nèi)容更豐富的文檔
- 支持 XML 或者 Groovy 方式配置
- 配置文件自動(dòng)熱加載
- 從 IO 錯(cuò)誤中優(yōu)雅恢復(fù)
- 自動(dòng)刪除日志歸檔
- 自動(dòng)壓縮日志成為歸檔文件
- 支持 Prudent 模式揣云,使多個(gè) JVM 進(jìn)程能記錄同一個(gè)日志文件
- 支持配置文件中加入條件判斷來(lái)適應(yīng)不同的環(huán)境
- 更強(qiáng)大的過(guò)濾器
- 支持 SiftingAppender(可篩選 Appender)
- 異常棧信息帶有包信息
添加依賴
<!-- slf4j + logback: -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.1</version>
</dependency>
<!-- 實(shí)現(xiàn)lsf4j接口并整合 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.1</version>
</dependency>
添加配置
Automatic configuration with logback-test.xml or logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder
by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
這是和默認(rèn)配置等效的一條配置捕儒。
使用logback
package chapters.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyApp1 {
final static Logger logger = LoggerFactory.getLogger(MyApp1.class);
public static void main(String[] args) {
logger.info("Entering application.");
}
}
16:06:09.031 [main] INFO chapters.configuration.MyApp1 - Entering application.
注意到這里,代碼里并沒(méi)有引用任何一個(gè)跟 Logback 相關(guān)的類邓夕,而是引用了 SLF4J 相關(guān)的類刘莹,這就是使用 SLF4J 的好處,在需要將日志框架切換為其它日志框架時(shí)焚刚,無(wú)需改動(dòng)已有的代碼点弯。
LoggerFactory
的 getLogger()
方法接收一個(gè)參數(shù),以這個(gè)參數(shù)決定logger
的名字矿咕,這里傳入了 MyApp1
這個(gè)類的 Class 實(shí)例抢肛,那么 logger 的名字便是 MyApp1
這個(gè)類的全限定類名:chapters.configuration.MyApp1
。
下面三種方法是等效的:
1. Logger logger = LoggerFactory.getLogger(MyApp1.class)
2. Logger logger = LoggerFactory.getLogger(MyApp1.class.getName())
3. Logger logger = LoggerFactory.getLogger("chapters.configuration.MyApp1")
讓 Logback 打印出一些它自身的內(nèi)部消息
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);
日志打印級(jí)別
TRACE < DEBUG < INFO < WARN < ERROR
如果一個(gè) logger 允許打印一條具有某個(gè)日志級(jí)別的信息痴腌,那么它也必須允許打印具有比這個(gè)日志級(jí)別更高級(jí)別的信息雌团,而不允許打印具有比這個(gè)日志級(jí)別更低級(jí)別的信息。
Logger士聪,Appenders 與 Layouts
在后端logger系統(tǒng)中锦援,有三個(gè)最基礎(chǔ)的概念需要先熟悉:
- Logger 日志記錄器 - 日志記錄器就是一個(gè)普通的Java類而已。
- Appender 輸出源 - 輸出源是日志最終輸出的地方剥悟,比如控制臺(tái)或者文件
- Layout(Encoder) 布局 - 布局決定了日志的打印格式灵寺,比如使用
%r [%t] %-5p %c %x - %m%n
可以打印出467 [main] INFO org.apache.log4j.examples.Sort - Exiting main method.
這樣的日志。
Logger 類位于 logback-classic 模塊中区岗, 而 Appender 和 Layout 位于 logback-core 中略板,
這意味著, Appender 和 Layout 并不關(guān)心 Logger 的存在慈缔,不依賴于 Logger叮称,同時(shí)也能看出, Logger 會(huì)依賴于 Appender 和 Layout 的協(xié)助,日志信息才能被正常打印出來(lái)瓤檐。
日志記錄器 - logger
在logback中日志記錄器是繼承的赂韵,繼承的規(guī)則是 com.hello.foo
會(huì)繼承 com.hello
的日志配置,父子關(guān)系通過(guò).
來(lái)分割挠蛉,所以 com
是com.hello
的父節(jié)點(diǎn)祭示。在logback中默認(rèn)會(huì)有一個(gè)root-logger
(根 - 日志記錄器)的存在,所有的其他日志記錄器都會(huì)默認(rèn)繼承它的配置谴古。
在配置文件中看到的:
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
就是“根”质涛。所以當(dāng)我們調(diào)用Logger logger = LoggerFactory.getLogger(App.class);
的時(shí)候,默認(rèn)是從root-logger
那里繼承了日志輸出配置掰担,而root-logger
默認(rèn)的log打印級(jí)別是debug
汇陆,所以用logger.info
打印不出日志。
那么如何配置一個(gè)普通的日志記錄器呢恩敌?這是非常簡(jiǎn)單的:
<logger name="com.hello" level="INFO"/>
這樣便配置了一個(gè)名為com.hello
的日志記錄器(一般會(huì)用包名作為日志記錄器的名字)瞬测,name
是必須配置的屬性。
然后可以通過(guò) Logger logger = LoggerFactory.getLogger("com.hello")
來(lái)得到這個(gè)日志記錄器纠炮。
為了可以控制哪些信息需要輸出月趟,哪些信息不需要輸出,logback 中引進(jìn)了一個(gè) **分層 **概念恢口。每個(gè) logger 都有一個(gè) name孝宗,這個(gè) name 的格式與 Java 語(yǔ)言中的包名格式相同。
日志輸出源 - Appenders
輸出源配置把日志打印到控制臺(tái)耕肩,輸出源的 name
和 class
屬性是必須配置的選項(xiàng)因妇。
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder
by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
配置把日志打印到文件:
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>myApp.log</file>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
有了輸出源之后,就可以給logger配置輸出源猿诸,一個(gè)logger可以配置多個(gè)輸出源:
<root level="debug">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
這里給 root-logger
配置了兩個(gè)輸出源婚被,此時(shí)如果我們調(diào)用logger.debug
方法會(huì)發(fā)現(xiàn)控制臺(tái)和根目錄下的myApp.log
文件都打印了相同的日志。
在logback中梳虽,level的配置會(huì)被繼承址芯,但是appender的配置會(huì)被子logger保留。這么說(shuō)有點(diǎn)抽象窜觉,看下面的例子:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.hello">
<appender-ref ref="STDOUT" />
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
這個(gè)配置會(huì)導(dǎo)致控制臺(tái)打印兩次:
11:38:06.068 [main] INFO com.hello.App - Hello world.Info
11:38:06.068 [main] INFO com.hello.App - Hello world.Info
當(dāng)調(diào)用LoggerFactory.getLogger("com.hello.App")
的時(shí)候谷炸,它繼承了來(lái)自 <logger name="com.hello">
和 root-logger
的輸出源,而他們的輸出源都是控制臺(tái)禀挫,所以導(dǎo)致在控制臺(tái)輸出了兩次旬陡。
解決辦法是,要么在有繼承關(guān)系的logger中配置不同的輸出源(從業(yè)務(wù)上避免)语婴,要么在子logger中覆蓋父節(jié)點(diǎn)中的配置描孟∈荒溃可以通過(guò)additivity="false"
配置實(shí)現(xiàn):
<logger name="com.hello" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
additivity
屬性告訴logback不要繼承來(lái)自父類的設(shè)置。
布局 - Layout 和 編碼 - Encoder
Layout主要用來(lái)把log事件轉(zhuǎn)換成String匿醒。一般布局都是配置在 Appender
里面的:
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n" />
</layout>
</appender>
注意啥繁,上面的示例使用的Appender是org.apache.log4j.ConsoleAppender
,這是log4j的配置而不是這里講的logback青抛,這是因?yàn)樵谶^(guò)去日志系統(tǒng)確實(shí)都是使用Layout來(lái)做日志轉(zhuǎn)換的,但是由于一些 固有的問(wèn)題 酬核,logback在Layout上面又封裝了一層 - Encoder蜜另,表現(xiàn)在配置上就是這樣的(這才是logback的配置):
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
但是Encoder的出現(xiàn)并不影響我們的配置,只是在形式上多了一個(gè)<encoder/>
標(biāo)簽嫡意。一般使用最多的是 PatternLayout
或PatternLayouEncoder
举瑰,他們的特點(diǎn)是提供了很多約定的標(biāo)記,這些標(biāo)記都以%
開(kāi)頭蔬螟,比如logger名稱此迅、日志等級(jí)日期、線程名等旧巾。
-
%d{HH: mm:ss.SSS}
——日志輸出時(shí)間 -
%thread
——輸出日志的進(jìn)程名字耸序,這在Web應(yīng)用以及異步任務(wù)處理中很有用 -
%-5level
——日志級(jí)別,并且使用5個(gè)字符靠左對(duì)齊 -
%logger{36}
——日志輸出者的名字 -
%msg
——日志消息 -
%n
——平臺(tái)的換行符
事實(shí)上我們?cè)谑褂胠ogback的時(shí)候很少見(jiàn)到直接使用layout鲁猩,這是因?yàn)閘ayout只能把一條log事件轉(zhuǎn)化成String坎怪,layout不能控制log什么時(shí)候被寫(xiě)入文件,也不能做到批量處理廓握。Encoder是被發(fā)明出來(lái)解決這些問(wèn)題的搅窿。
目前為止Encoders只有一個(gè)實(shí)現(xiàn) - PatternLayoutEncoder,它內(nèi)部包含了一個(gè)PatternLayout隙券,所以可以像使用PatternLayout一樣使用Encoder男应。
常見(jiàn)配置
根節(jié)點(diǎn)<configuration>
包含的屬性
- scan:當(dāng)此屬性設(shè)置為true時(shí),配置文件如果發(fā)生改變娱仔,將會(huì)被重新加載沐飘,默認(rèn)值為true。
- scanPeriod:設(shè)置監(jiān)測(cè)配置文件是否有修改的時(shí)間間隔拟枚,如果沒(méi)有給出時(shí)間單位薪铜,默認(rèn)單位是毫秒。當(dāng)scan為true時(shí)恩溅,此屬性生效隔箍。默認(rèn)的時(shí)間間隔為1分鐘。
- debug:當(dāng)此屬性設(shè)置為true時(shí)脚乡,將打印出logback內(nèi)部日志信息蜒滩,實(shí)時(shí)查看logback運(yùn)行狀態(tài)滨达。默認(rèn)值為false。
<configuration>
下面一共有2個(gè)屬性俯艰,3個(gè)子節(jié)點(diǎn)捡遍,分別是:
屬性一:設(shè)置上下文名稱<contextName>
每個(gè)logger都關(guān)聯(lián)到logger上下文,默認(rèn)上下文名稱為“default”竹握。但可以使用<contextName>
設(shè)置成其他名字画株,用于區(qū)分不同應(yīng)用程序的記錄。一旦設(shè)置啦辐,不能修改,可以通過(guò)%contextName
來(lái)打印日志上下文名稱谓传。
<contextName>logback</contextName>
屬性二:設(shè)置變量<property>
用來(lái)定義變量值的標(biāo)簽,<property>
有兩個(gè)屬性芹关,name
和value
续挟;其中name的值是變量的名稱,value的值時(shí)變量定義的值侥衬。通過(guò)<property>定義的值會(huì)被插入到logger上下文中诗祸。定義變量后,可以使${}
來(lái)使用變量轴总。
<property name="log.path" value="E:\\logback.log" />
變量有三個(gè)作用域:
- local
- context
- system
local 作用域在配置文件內(nèi)有效直颅,context 作用域的有效范圍延伸至 logger context,system 作用域的范圍最廣怀樟,整個(gè) JVM 內(nèi)都有效际乘。
logback 在替換變量時(shí),首先搜索 local 變量漂佩,然后搜索 context脖含,然后搜索 system。
<property scope="context" name="nodeId" value="firstNode" />
也可以通過(guò)外部文件來(lái)定義:
<property file="src/main/java/chapters/configuration/variables1.properties" />
子節(jié)點(diǎn)一:<appender>
appender
用來(lái)格式化日志輸出節(jié)點(diǎn)投蝉,有倆個(gè)屬性name
和class
养葵,class用來(lái)指定哪種輸出策略,常用就是控制臺(tái)輸出策略和文件輸出策略瘩缆。
1关拒、控制臺(tái)輸出ConsoleAppender:
<!--輸出到控制臺(tái)-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
ThresholdFilter為系統(tǒng)定義的攔截器,例如我們用ThresholdFilter來(lái)過(guò)濾掉ERROR級(jí)別以下的日志不輸出到文件中庸娱。如果不用記得注釋掉着绊,不然你控制臺(tái)會(huì)發(fā)現(xiàn)沒(méi)日志~
2、輸出到文件RollingFileAppender
另一種常見(jiàn)的日志輸出到文件熟尉,隨著應(yīng)用的運(yùn)行時(shí)間越來(lái)越長(zhǎng)归露,日志也會(huì)增長(zhǎng)的越來(lái)越多,將他們輸出到同一個(gè)文件并非一個(gè)好辦法斤儿。
RollingFileAppender用于切分文件日志:
<!--輸出到文件-->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logback.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
-
<fileNamePattern>logback.%d{yyyy-MM-dd}.log</fileNamePattern>
定義了日志的切分方式——把每一天的日志歸檔到一個(gè)文件中剧包。 -
<maxHistory>30</maxHistory>
表示只保留最近30天的日志恐锦,以防止日志填滿整個(gè)磁盤(pán)空間。同理疆液,可以使用%d{yyyy-MM-dd_HH-mm}
來(lái)定義精確到分的日志切分方式一铅。 -
<totalSizeCap>1GB</totalSizeCap>
用來(lái)指定日志文件的上限大小,例如設(shè)置為1GB的話堕油,那么到了這個(gè)值潘飘,就會(huì)刪除舊的日志。
子節(jié)點(diǎn)二:<root>
root節(jié)點(diǎn)是必選節(jié)點(diǎn)掉缺,用來(lái)指定最基礎(chǔ)的日志輸出級(jí)別福也,只有一個(gè)level屬性。
<root level="debug">
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
子節(jié)點(diǎn)三: <loger>
<loger>
用來(lái)設(shè)置某一個(gè)包或者具體的某一個(gè)類的日志打印級(jí)別攀圈、以及指定<appender>
。
<loger>
僅有一個(gè)name
屬性峦甩,一個(gè)可選的level
和一個(gè)可選的addtivity
屬性赘来。
if表達(dá)式(條件化處理配置文件)
logback 允許在配置文件中定義條件語(yǔ)句,以決定配置的不同行為凯傲,
<!-- if-then form -->
<if condition="some conditional expression">
<then>
...
</then>
</if>
<!-- if-then-else form -->
<if condition="some conditional expression">
<then>
...
</then>
<else>
...
</else>
</if>
常用條件表達(dá)式函數(shù)
1. property('key')
2. isDefined('key')
3. isNull("key")
文件包含
可以使用 ?include> 標(biāo)簽在一個(gè)配置文件中包含另外一個(gè)配置文件犬辰。
<configuration>
<include file="src/main/java/chapters/configuration/includedConfig.xml"/>
<root level="DEBUG">
<appender-ref ref="includedConsole" />
</root>
</configuration>
被包含的文件必須有以下格式:
<included>
<appender name="includedConsole" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>"%d - %m%n"</pattern>
</encoder>
</appender>
</included>
支持從多種源頭包含
- 從文件中包含
<include file="src/main/java/chapters/configuration/includedConfig.xml"/>
- 從 classpath 中包含
<include resource="includedConfig.xml"/>
- 從 URL 中包含
<include url="http://some.host.com/includedConfig.xml"/>
如果包含不成功,那么 logback 會(huì)打印出一條警告信息冰单,如果不希望 logback 抱錯(cuò)幌缝,只需這樣做:
<include optional="true" ..../>
添加一個(gè) Context Listener
LoggerContextListener
接口的實(shí)例能監(jiān)聽(tīng) logger context 上發(fā)生的事件,比如說(shuō)日志級(jí)別的變化诫欠,添加的方式如下所示:
<configuration debug="true">
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
....
</configuration>
多環(huán)境日志輸出
據(jù)不同環(huán)境(prod:生產(chǎn)環(huán)境涵卵,test:測(cè)試環(huán)境,dev:開(kāi)發(fā)環(huán)境)來(lái)定義不同的日志輸出荒叼。
文件名稱不是logback.xml轿偎,想使用spring擴(kuò)展profile支持,要以logback-spring.xml命名
<!-- 測(cè)試環(huán)境+開(kāi)發(fā)環(huán)境. 多個(gè)使用逗號(hào)隔開(kāi). -->
<springProfile name="test,dev">
<logger name="com.dudu.controller" level="info" />
</springProfile>
<!-- 生產(chǎn)環(huán)境. -->
<springProfile name="prod">
<logger name="com.dudu.controller" level="ERROR" />
</springProfile>