logback
1.1 簡介
師出同門宝磨,與log4j一樣疯潭,logback也是由Ceki Gülcü開發(fā)的開源日志組件两蟀,可以說是log4j的改進(jìn)版;在現(xiàn)如今的項(xiàng)目中碧注,logback的出現(xiàn)次數(shù)越來越多嚣伐,是目前主流首選的日志記錄工具。
1.2 logback結(jié)構(gòu)
logback分成三個(gè)模塊:logback-core萍丐,logback- classic轩端,logback-access。
logback-core提供了logBack的核心功能逝变,是另外兩個(gè)組件的基礎(chǔ)基茵;
logback-classic模塊實(shí)現(xiàn)了SLF4J API;
logback-access模塊與Servlet容器集成提供Http來訪問日志的功能壳影。
1.3 使用
首先拱层,需要在應(yīng)用的pom.xml中添加依賴:
<!--slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
</dependency>
<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.1.7</version>
</dependency>
其次,聲明測試代碼:
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class slf4j_logbackDemo {
Logger logger= LoggerFactory.getLogger(slf4j_logbackDemo.class);
@Test
public void test() {
logger.debug("debug message");
logger.info("info message");
logger.warn("warning message");
logger.error("error message");
logger.warn("login message");
}
}
最后宴咧,在classpath下聲明配置文件:logback.xml;
<!--每天生成一個(gè)文件根灯,歸檔文件保存30天:-->
<configuration >
<!--設(shè)置自定義pattern屬性-->
<property name="pattern" value="%d{HH:mm:ss.SSS} [%-5level] [%thread] [%logger] %msg%n"/>
<!--控制臺輸出日志-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--設(shè)置控制臺輸出日志的格式-->
<encoder>
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!--滾動記錄日志文件:-->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--當(dāng)天生成的日志文件名稱:-->
<file>e:/log.out</file>
<!--根據(jù)時(shí)間來記錄日志文件:-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--歸檔日志文件的名稱:-->
<fileNamePattern>testLog-%d{yyyy-MM-dd}.log</fileNamePattern>
<!--歸檔文件保存30天-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<!--生成的日志信息格式-->
<encoder>
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!--根root logger-->
<root level="DEBUG">
<!--設(shè)置根logger的日志輸出目的地-->
<appender-ref ref="FILE" />
<appender-ref ref="CONSOLE" />
</root>
</configuration>
通過以上步驟,logback就可以正常的運(yùn)行了掺栅。
1.4 配置文件詳解
(1)configuration:配置根節(jié)點(diǎn)
<configuration scan="true" scanPeriod="60" debug="false"></configuration>
scan:程序運(yùn)行時(shí)配置文件被修改烙肺,是否重新加載。true柿冲,重新加載茬高;false,不重新加載;默認(rèn)為true;
scanPeriod:監(jiān)測配置文件被修改的時(shí)間間隔假抄,scan屬性必須設(shè)置為true才可生效怎栽;默認(rèn)為1分鐘,默認(rèn)單位是毫秒宿饱;
debug:是否打印logback程序運(yùn)行的日志信息熏瞄。true,打印谬以;false,不打忧恳;默認(rèn)為false;
(2)property:屬性變量
<configuration scan="true" scanPeriod="60" debug="false">
<property name="pattern" value="%d{HH:mm:ss.SSS} [%-5level] [%logger] %msg%n" ></property>
</configuration>
name:變量的名稱为黎,可以隨意起名邮丰,但建議名字要簡明直譯;
value:變量的值铭乾;
在配置文件中剪廉,我們可以用 ${} 的方式來使用,將變量引入到其他節(jié)點(diǎn)中去炕檩。如果有多處使用相同的內(nèi)容斗蒋,便可使用屬性變量的方式進(jìn)行統(tǒng)一,減少很多不必要的代碼;
(3)logger<logger>:日志對象
logger分為2種泉沾,一種是普通日志對象捞蚂,另一種是根日志對象。對于大部分應(yīng)用來說跷究,只設(shè)置根日志對象即可姓迅。
在java日志系統(tǒng)中,無論是log4j還是logback俊马,他們的日志對象體系都是呈現(xiàn)“樹”的形式队贱,根日志對象為最頂層節(jié)點(diǎn),其余包或者類中的日志對象都繼承于根日志節(jié)點(diǎn)潭袱;
對于普通日志對象來說柱嫌,我們可以設(shè)置某一個(gè)包或者某一個(gè)類的日志級別,還可以單獨(dú)設(shè)置日志的輸出目的地屯换;
<configuration scan="true" scanPeriod="60" debug="false">
<logger name="java.sql" level="debug" addtivity="true">
<appender-ref ref="CONSOLE" />
</logger>
</configuration>
name:用來指定此logger屬于哪個(gè)包或者哪個(gè)類编丘;
level:用來指定此logger的日志打印級別;
addtivity:是否向上傳遞日志打印信息彤悔。之前說了麸恍,logger對象呈現(xiàn)一個(gè)樹的結(jié)構(gòu)庞钢,根logger是樹的頂端媳瞪,下面的子logger的addtivity屬性如果設(shè)置為true則會向上傳遞打印信息睹栖,出現(xiàn)日志重復(fù)打印的現(xiàn)象;
appender-ref:日志輸出目的地杨赤,將此logger所打印的日志交給此appender敞斋;
值得注意的是,上面的例子中疾牲,如果此logger沒有指定appender植捎,而且addtivity也設(shè)置為true,那么此logger對應(yīng)的日志信息只會打印一遍阳柔,是由root來完成的焰枢;但是如果addtivity設(shè)置成false,那么此logger將不會輸出任何日志信息舌剂;
(3)logger<root>
:根日志對象
<root>也是日志對象中的一種济锄,但它位于logger體系中的最頂層。當(dāng)一個(gè)類中的logger對象進(jìn)行打印請求時(shí)霍转,如果配置文件中沒有為該類單獨(dú)指定日志對象荐绝,那么都會交給root根日志對象來完成;
<root>節(jié)點(diǎn)中只有一個(gè)level屬性谴忧,還可以單獨(dú)指定日志輸除目的地<apender-ref>;
<configuration scan="true" scanPeriod="60" debug="false">
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
(4)appender:日志輸出目的地
與log4j中的appender一樣很泊,logback中的<appender>
節(jié)點(diǎn)也同樣負(fù)責(zé)日志輸出的目的地。
appender中有2個(gè)必填屬性--name和class沾谓。name為<appender>
節(jié)點(diǎn)的名稱委造,class為<appender>
的全限定類名,也就是日志輸出目的地的處理類均驶。此外昏兆,我們還可以在<appender>
中單獨(dú)指定日志的格式,設(shè)置日志過濾器等操作妇穴;
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
在logback中爬虱,主要有以下三種日志目的地處理類:
①ch.qos.logback.core.ConsoleAppender
將日志輸出到控制臺,可以在其節(jié)點(diǎn)中設(shè)置<encoder>子節(jié)點(diǎn)腾它,設(shè)置日志輸出的格式跑筝;
例子:
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
②ch.qos.logback.core.FileAppender
將日志輸出到具體的磁盤文件中,可以單獨(dú)指定具體的位置瞒滴,也可以設(shè)置日志的輸出格式曲梗;
例子:
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>e:/log.out</file>
<append>true</append>
<prudent>false</prudent>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
子節(jié)點(diǎn)<append>:新增的日志是否以追加到文件結(jié)尾的方式寫入到log.out文件中,true為追加妓忍,fasle為清空現(xiàn)存文件寫入虏两;
子節(jié)點(diǎn)<prudent>:日志是否被安全的寫入磁盤文件,默認(rèn)為false世剖。如果為true定罢,則效率低下;
③ch.qos.logback.core.rolling.RollingFileAppender
滾動記錄日志旁瘫,當(dāng)符合<rollingPolicy>節(jié)點(diǎn)中設(shè)置的條件時(shí)祖凫,會將現(xiàn)有日志移到新的文件中去。<rollingPolicy>節(jié)點(diǎn)中可設(shè)置的條件為:文件的大小酬凳、時(shí)間等蝙场;
例子:
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>e:/log.out</file>
<append>true</append>
<prudent>false</prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>testLog-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
④ch.qos.logback.classic.AsyncAppender
異步記錄日志,內(nèi)部通過使用緩存的方式來實(shí)現(xiàn)異步打印粱年,將日志打印事件event放入緩存中售滤。具體數(shù)據(jù)結(jié)構(gòu)為BlockingQueue;
例子:
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>e:/log.out</file>
<append>true</append>
<prudent>false</prudent>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<appender name ="ASYNC" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>512</queueSize>
<appender-ref ref ="FILE"/>
</appender>
子節(jié)點(diǎn)<queueSize>:指的是BlockingQueue的隊(duì)列容量大小台诗,默認(rèn)為256個(gè)完箩;
子節(jié)點(diǎn)<discardingThreshold>:如果BlockingQueue中還剩余20%的容量,那么程序會丟棄TRACE拉队、DEBUG和INFO級別的日志打印事件event弊知,只保留WARN和ERROR級別的。為了保留所有的日志打印事件粱快,可以將該值設(shè)置為0秩彤。
(5)rollingPolicy
日志文件的滾動策略叔扼,與RollingFileAppender搭配使用,當(dāng)日志文件發(fā)生變動時(shí)決定RollingFileAppender的行為漫雷;
在<rollingPolicy>節(jié)點(diǎn)中有一個(gè)class屬性瓜富,可選的值為TimeBasedRollingPolicy、FixedWindowRollingPolicy降盹、TriggeringPolicy与柑;
其中ch.qos.logback.core.rolling.TimeBasedRollingPolicy表示根據(jù)時(shí)間制定日志文件的滾動策略;
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>testLog-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
ch.qos.logback.core.rolling.FixedWindowRollingPolicy表示如果日志文件大小超過指定范圍時(shí)蓄坏,會根據(jù)文件名拆分成多個(gè)文件价捧;
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>tests.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
(6)triggeringPolicy
與<rollingPolicy>節(jié)點(diǎn)一樣,<triggeringPolicy>節(jié)點(diǎn)也屬于日志滾動策略中的一種涡戳。
ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy表示根據(jù)日志文件大小结蟋,超過制定大小會觸發(fā)日志滾動;
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
(7)encoder
日志格式化節(jié)點(diǎn)渔彰,負(fù)責(zé)格式化日志信息椎眯。<encoder>只負(fù)責(zé)了兩件事情,第一負(fù)責(zé)將日志信息轉(zhuǎn)換成字節(jié)數(shù)組胳岂,第二將字節(jié)數(shù)組寫到輸出流當(dāng)中去编整;
在<encoder>中使用<pattern>來設(shè)置對應(yīng)的格式;
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder
%logger:表示輸出logger名稱乳丰,后面跟一個(gè){0}表示:只輸出logger最右邊點(diǎn)符號之后的字符串掌测;例如:com.jiaboyan.test ---> test;
%d{HH:mm:ss.SSS}:表示格式化日期輸出,14:06:49.812产园;
%line:輸出執(zhí)行日志請求的行號汞斧。
%thread:表示產(chǎn)生日志的線程名;
%level:輸出日志級別什燕;
%method:輸出執(zhí)行日志請求的方法名;
%class:輸出日志打印類的全限定名粘勒,后面跟{0}表示,含義為全限定類名最右邊點(diǎn)符號之后的字符串屎即。例如:com.jiaboyan.test ---> test;
%-4relative含義:符號減號“-”是左對齊 標(biāo)志庙睡,接著是可選的最小寬度 修飾符,用十進(jìn)制數(shù)表示技俐。relative是輸出從程序啟動到創(chuàng)建日志記錄的時(shí)間乘陪,單位是毫秒;
%msg:表示應(yīng)用程序提供需要打印的日志信息;
%n:表示換行符雕擂;
1.5 logback性能測試
接下來啡邑,我們對logback進(jìn)行下性能測試,分為同步和異步井赌;
(1)測試代碼:(單線程谤逼、同步)
public class slf4j_logbackDemo {
Logger logger = LoggerFactory.getLogger(slf4j_logbackDemo.class);
@Test
public void test() {
long start = System.currentTimeMillis();
for(int x=0;x<50;x++) {
long startss = System.currentTimeMillis();
for (int y = 0; y < 10000; y++) {
logger.info("Info Message!");
}
System.out.print(System.currentTimeMillis()-startss+" ");
}
System.out.print(System.currentTimeMillis()-start+" ");
}
}
配置文件:(注意日志輸出的格式贵扰,只有簡單的日志信息)
在筆者測試前,并沒有對日志格式化做太多的關(guān)注流部。在測試完成后戚绕,發(fā)現(xiàn)日志格式化對性能的影響很大,所以此次的測試為了節(jié)約時(shí)間贵涵,就只輸出日志信息,并沒有具體的時(shí)間恰画、logger對象宾茂、日志級別等信息;
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>e:/log.out</file>
<append>true</append>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="FILE" />
</root>
</configuration>
結(jié)果:(單位毫秒)
182 86 34 31 44 38 30 31 45 57 33 29 35 42 46 43 48 40 40 45 50 40 44 53 65 38 59 32 26 29 32 30 34 32 34 23 26 20 20 21 20 22 20 22 21 20 23 22 20 23
總耗時(shí):1900
(2)測試代碼:(多線程拴还、同步)
public class slf4j_logbackDemo {
Logger logger = LoggerFactory.getLogger(slf4j_logbackDemo.class);
@Test
public void testThread() throws InterruptedException {
long start = System.currentTimeMillis();
final CountDownLatch countDownLatch = new CountDownLatch(50);
for(int x= 0;x<50;x++){
new Thread(new Runnable() {
public void run() {
for (int y = 0; y < 10000; y++) {
logger.info("Info Message!");
}
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
long time = System.currentTimeMillis() - start;
System.out.println(time);
}
}
配置文件:(同上)
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>e:/log.out</file>
<append>true</append>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="FILE" />
</root>
</configuration>
結(jié)果:(單位毫秒)
總耗時(shí):2229
使用了CountDownLatch對象跨晴,可以計(jì)算出50個(gè)線程執(zhí)行完畢后所花費(fèi)的總時(shí)間∑郑可以看出端盆,由于線程創(chuàng)建的原因,在多線程的情況下并沒有體現(xiàn)出更優(yōu)秀的性能费封。
當(dāng)然焕妙,還有一方面因素就是日志總量太小,不足以體現(xiàn)出多線程的優(yōu)勢弓摘。
(3)測試代碼:(單線程焚鹊、異步)
public class slf4j_logbackDemo {
Logger logger = LoggerFactory.getLogger(slf4j_logbackDemo.class);
@Test
public void test() throws InterruptedException {
long start = System.currentTimeMillis();
for(int x=0;x<50;x++) {
long startss = System.currentTimeMillis();
for (int y = 0; y < 10000; y++) {
logger.info("Info Message!");
}
System.out.print(System.currentTimeMillis()-startss+" ");
}
System.out.print(System.currentTimeMillis()-start+" ");
}
}
配置文件:(同上)
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>e:/log.out</file>
<append>true</append>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<appender name ="ASYNC" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>512</queueSize>
<appender-ref ref ="FILE"/>
</appender>
<root level="info">
<appender-ref ref="ASYNC" />
</root>
</configuration>
結(jié)果:(單位毫秒)
183 123 48 54 45 40 45 47 48 48 44 79 51 56 57 91 116 79 95 126 53 73 47 62 50 49 42 43 39 42 41 37 28 30 28 30 26 34 34 39 29 31 31 32 31 32 32 32 34 32
總耗時(shí):2619
奇怪的是,在異步情況下韧献,logback并沒有體現(xiàn)出應(yīng)有的性能優(yōu)勢末患。后來繼續(xù)通過測試發(fā)現(xiàn),日志信息的輸出量锤窑、格式化影響很大璧针。
在調(diào)整之前的日志文件大小為7.15M,在調(diào)整之后為19M渊啰。調(diào)整之前的格式為%msg%n探橱,調(diào)整之后的為%d{HH:mm:ss.SSS} [%-5level] [%thread] [%logger] %msg%n;
在調(diào)整后進(jìn)行測試绘证,同步下24760ms,異步下10921ms走搁,性能提升一倍;
(4)測試代碼:(多線程迈窟、異步)
public class slf4j_logbackDemo {
Logger logger = LoggerFactory.getLogger(slf4j_logbackDemo.class);
@Test
public void test() throws InterruptedException {
long start = System.currentTimeMillis();
final CountDownLatch countDownLatch = new CountDownLatch(50);
for(int x= 0;x<50;x++){
new Thread(new Runnable() {
public void run() {
for (int y = 0; y < 10000; y++) {
logger.info("Info Message!");
}
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
long time = System.currentTimeMillis() - start;
System.out.println(time);
}
}
配置文件:(同上)
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>e:/log.out</file>
<append>true</append>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<appender name ="ASYNC" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>512</queueSize>
<appender-ref ref ="FILE"/>
</appender>
<root level="info">
<appender-ref ref="ASYNC" />
</root>
</configuration>
結(jié)果:(單位毫秒)
總耗時(shí):2780