大部分內容均來自Logback官網。
Logback
Logback是一種日志框架,它是由log4j和SLF4J日志框架的設計者設計,意在取代log4j。通常和SLF4J配合使用。
1 架構(Architecture)
Logback框架分為3個模塊,分別是logback-core,logback-classic和logback-access。
顧名思義,
core
模塊是其它兩個模塊的基礎。classic
模塊繼承自core模塊,它是對log4j的重大改進。Logback-classic原生實現了SLF4J的API,這樣就可以讓使用者在logback和其它日志系統之間自由切換,比如log4j或者java.util.logging(JUL)。access
模塊集成了Servlet容器,提供了基于HTTP訪問的日志功能。
2 Logger,Appenders和Layouts
Logback框架主要基于3個類(接口):Logger
,Appender
和Layout
。
通過上述3種組件的聯合使用,可以幫助開發者針對不同的消息類型和等級記錄日志,并控制應用運行時的日志格式和輸出位置。
Logger
類是logback-classic模塊的一部分,而Appender
和Layout
接口是logback-core的一部分。Logger類沒有包含在logback-core
中是考慮到模塊的通用性。
2.1 Logger上下文(Logger context)
相比于普通的System.out.println
而言,任何一種日志API首要且最重要的優勢就在于選擇性輸出或打印特定的日志語句(log statements)。這種能力通常假設存在一個日志空間(logging space),該空間里包含了所有可能的日志語句,開發者能根據設定的某些準則將這些語句劃分為不同的類別。簡單地說,比如info、warn、error等。
注:不管是logback,還是slf4j,亦或是log4j中,對“記錄日志”這樣的行為都是基于一種模型假設:日志器(logger)負責記錄日志(log)。這有點類似于英文的work和worker的關系,其中work表示工作,worker表示工人。同樣的道理,log表示日志,logger表示記錄日志的主體——日志器(p.s. 譯得很low,但確實沒想到更好的譯名)
每個獨立的日志器(logger)都將綁定一個日志上下文LoggerContext
,它負責產生日志器,并在一個類似層級結構的樹結構中安置日志器。
日志器(loggers)是命名的實體(named entities)。它們的名字是大小寫敏感,并遵循層級命名規則:
Named Hierarchy
A logger is said to be an ancestor of another logger if its name followed by a dot is a prefix of the descendant logger name. A logger is said to be a parent of a child logger if there are no ancestors between itself and the descendant logger.
熟悉Java包命名規則的一定會覺得上述規則很眼熟。是的,沒錯,該規則跟Java的包命名規則基本是相同的。例如,名為“com.codershangfeng”的日志器是名為“com.codershangfeng.backup”日志器的父一級日志器。
根日志器(root logger)位于所有日志器層級結構的頂部。任何一個日志器層級結構中都包含這樣一個根日志器。它也可以像獲取其他日志器一樣,通過它的名字來獲取,如下:
Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
所有日志器都包含5類打印方法,還有很多重載(overload)的方法未逐一列出(遠不止下面的5個)。
package org.slf4j;
public interface Logger {
// Printing methods:
public void trace(String message);
public void debug(String message);
public void info(String message);
public void warn(String message);
public void error(String message);
}
開發者根據開發需要調用合適的方法打印輸出日志。
2.2 日志等級
從剛才的Logger
接口中可以看到,每個日志器其實包含了5種日志輸出等級,即:TRACE(跟蹤)、DEBUG(調試)、INFO(信息)、WARN(警告)和ERROR(錯誤)。TRACE等級最低,ERROR等級最高。
例如,當指定日志器的輸出等級為TRACE時,那么,該日志器調用的trace()
、debug()
、info()
、warn()
和error()
方法都將輸出日志。反之,若指定該日志器的輸出等級為ERROR,則僅輸出該日志器調用的error()
方法的日志。
每個日志器都可以指定它的輸出等級,當未指定時,則選用它的非空父一級日志器的等級,若非空父一級日志器也未指定,則繼續上溯,最終可上溯至日志層級結構的頂級日志器——root,并采用root的輸出等級作為該日志器的輸出等級。
可以參考官方的解釋:
The effective level for a given logger L, is equal to the first non-null level in its hierarchy, starting at L itself and proceeding upwards in the hierarchy towards the root logger.
特別注意,這與實際使用日志器時,在方法里實際調用的Logger方法無關!也就是說,在方法里你仍然可以調用某個日志器實例的logger.×××()
方法,但至于是否輸出打印,則取決于開發者對該Logger及其父一級,甚至根日志器的輸出等級配置。
再來幾個例子解釋下日志器的日志等級設置:
- 例1
Logger name | Assigned level | Effective level |
---|---|---|
root | DEBUG | DEBUG |
X | none | DEBUG |
X.Y | none | DEBUG |
X.Y.Z | none | DEBUG |
例1中,只有root日志器(根日志器)設定了輸出等級——DEBUG,則該應用中所有繼承自根日志器(root
)的日志器,如X
、X.Y
和X.Y.Z
都將繼承root
日志器的DEBUG輸出等級。
- 例2
Logger name | Assigned level | Effective level |
---|---|---|
root | ERROR | ERROR |
X | INFO | INFO |
X.Y | DEBUG | DEBUG |
X.Y.Z | WARN | WARN |
例2中,每個日志器都指定了輸出等級,不需要再使用繼承關系進行指定輸出等級。
- 例3
Logger name | Assigned level | Effective level |
---|---|---|
root | DEBUG | DEBUG |
X | INFO | INFO |
X.Y | none | INFO |
X.Y.Z | none | INFO |
例3中,root
和X
分別指定DEBUG和INFO的輸出等級;X.Y
和X.Y.Z
繼承它最近的父一級的日志器X
,故其輸出等級為INFO。
2.3 獲取日志器
例如:
Logger x = LoggerFactory.getLogger("wombat");
Logger y = LoggerFactory.getLogger("wombat");
日志器x
和y
都指向名為wombat
的相同日志器實例。
1.2.4 Appenders and Layouts
如果說,輸出等級表示的是選擇哪些日志進行輸出,那么,Layouts和Appenders則分別是選擇以哪些格式輸出日志,將日志輸出到哪些地方。
在logback的術語中,一個輸出目的地被稱為一個附加器(Appender)。目前,可供選擇的附加器包括控制臺(console),文件(files),遠程套接字服務器(remote socket server),MySQL、PostgreSQL和其它數據庫,Java消息服務(JMS),遠程UNIX系統日志守護線程(remote UNIX Syslog daemons)。
一個日志器可以綁定多個附加器(appender)。
類似日志器的輸出等級層級結構,附加器也存在類似的繼承關系,但是其決定關系剛好是逆向的,即“子-->父”:
Appender Additivity
The output of a log statement of logger L will go to all the appenders in L and its ancestors. This is the meaning of the term "appender additivity".
However, if an ancestor of logger L, say P, has the additivity flag set to false, then L's output will be directed to all the appenders in L and its ancestors up to and including P but not the appenders in any of the ancestors of P.
Loggers have their additivity flag set to true by default.
從上述定義可以得出三點結論:
(1)子一級日志器產生的日志將輸出至父一級的所有附加器中;
(2)若父一級日志器(假設為P)的additivity標識位設置為false,則子一級日志器(假設為L)的日志將輸出至L自己的附加器,P的附加器,但不包含P的父一級日志器的附加器;
(3)所有日志器的additivity標識位默認為ture(root除外)。
舉例:
Logger Name | Attached Appenders | Additivity Flag | Output Targets | Comment |
---|---|---|---|---|
root | A1 | not applicable | A1 | Since the root logger stands at the top of the logger hierarchy, the additivity flag does not apply to it. |
x | A-x1, A-x2 | true | A1, A-x1, A-x2 | Appenders of "x" and of root. |
x.y | none | true | A1, A-x1, A-x2 | Appenders of "x" and of root. |
x.y.z | A-xyz1 | true | A1, A-x1, A-x2, A-xyz1 | Appenders of "x.y.z", "x" and of root. |
security | A-sec | false | A-sec | No appender accumulation since the additivity flag is set to false. Only appender A-sec will be used. |
security.access | none | true | A-sec | Only appenders of "security" because the additivity flag in "security" is set to false. |
至于Layout,標準logback中發布PatternLayout
可以幫助使用者利用類似C語言中printf
函數的方法轉換日志的輸出格式。
例如,轉換格式"%-4relative [%thread] %-5level %logger{32} - %msg%n"
將輸出類似如下:
176 [main] DEBUG manual.architecture.HelloWorld2 - Hello world.
2 配置
logback在查找配置文件遵循以下規則:
Logback tries to find a file called logback-test.xml in the classpath.
If no such file is found, logback tries to find a file called logback.groovy in the classpath.
If no such file is found, it checks for the file logback.xml in the classpath.
If no such file is found, service-provider loading facility ( introduced in JDK 1.6) is used to resolve the implementation of com.qos.logback.classic.spi.Configurator interface by looking up the META-INF\services\ch.qos.logback.classic.spi.Configurator in the class path. It contents should specify the fully qualified class name of the desired
Configurator
implementation.If none of the above succeeds, logback configures itself automatically using the BasicConfigurator which will cause logging output to be directed to the console.
其中,1-3都是通過文件直接進行配置;4-5是通過接口或類進行配置。
較為常見的是第3種,即通過logback.xml
XML文件進行配置。
如果使用Maven進行項目開發,可以將logback-test.xml放到src/test/resources文件夾下,這樣就可以在測試時使用logback-test.xml配置文件,而在產品中使用logback.xml配置文件。
不過,即使不配置,logback也能利用第5種方案進行默認配置(有興趣可參考鏈接)。
2.1 使用logback-test.xml或logback.xml自動配置
例1:基本配置
<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>
例1中,“<”和“>”兩個尖括號表示XML文件結構中的一個元素或標簽;“<foo>”和“</foo>”表示元素“foo”的起始和結束位置;'appender'標簽表示日志最終輸出的目的地,類似通信模型中的“信宿”;encoder
標簽就相當于上文提到的Layout
(也可以用繼承接口或抽象類的方法實現更為復雜的格式邏輯,但通常來說用encoder的字符串格式符就足夠了),用于以C語言的printf
函數形式設置每條日志語句的格式;'root'是根日志器的標簽;appender-ref
表示該日志器所指向或掛靠的附加器。
例2:為日志器設置輸出等級
<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>
<logger name="chapters.configuration" level="INFO"/>
<!-- Strictly speaking, the level attribute is not necessary since -->
<!-- the level of the root level is set to DEBUG by default. -->
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
例2中,對名為“chapter.configuration”
的日志器設置了INFO輸出等級,并按照logback框架日志器層級關系,“chapter.configuration”
日志器也將使用root
日志器的附加器,即“STDOUT”
,這一點有別于log4j(沒用過,文檔里這么說的)。
例3:為多個日志器設置輸出等級
<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="chapters.configuration" level="INFO" />
<logger name="chapters.configuration.Foo" level="DEBUG" />
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
例4: 多個附加器
<configuration>
<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>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
<appender-ref ref="STDOUT" />
</root>
</configuration>
例5:附加器復用
<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="chapters.configuration">
<appender-ref ref="STDOUT" />
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
例6:多日志器,多附加器
<configuration>
<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>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<logger name="chapters.configuration">
<appender-ref ref="FILE" />
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
例7:定義變量
<configuration>
<property name="USER_HOME" value="/home/sebastien" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${USER_HOME}/myApp.log</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
還有關于Layout、Filter、JMX Configuration、Using SSL等內容暫不記錄。
所有代碼均來自logback官網文檔The logback manual。
EOF