前言
上一篇主要對root進行了實踐總結,現在基於上一篇中的springboot代碼環境對logback.xml中的logger來進行實踐和自己遇到的坑。
logger簡介
日志屬性,可以根據logger中的name屬性指定某個文件或者文件夾輸出的日志級別,並通過appender-ref指定日志的輸出格式。還有一個additivity屬性,如果設置為false的話就不會向上傳遞。
上代碼
logback.xml:
<?xml version="1.0" encoding="utf-8" ?>
<configuration debug="true" scan="true" scanPeriod="60 seconds">
<appender name="logger_stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d{YYYY-MM-dd HH:mm:ss} | %-5level | %thread | %logger - %msg%n</Pattern>
</encoder>
</appender>
<!-- name=指定打印日志文件級別 level=日志級別 additivity=是否想向上傳遞 先不加上 additivity=false 屬性 -->
<logger name="com.example.logback.logger" level="info">
<!-- 指定輸出的appender -->
<appender-ref ref="logger_stdout"/>
</logger>
</configuration>
測試代碼:
package com.example.logback.logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @ClassName LoggerTest
* @Description TODO
* @Author ouyangkang
* @Date 2019-01-07 17:04
**/
@Component
public class LoggerTest {
private final Logger logger = LoggerFactory.getLogger("測試");
public void loggerTest() {
logger.info("info=====>");
logger.error("error====》");
logger.debug("debug=====》");
}
}
單元測試代碼:
package com.example.logback;
import com.example.logback.logger.LoggerTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class LogbackApplicationTests {
@Resource
private LoggerTest loggerTest;
@Test
public void loggerTest(){
loggerTest.loggerTest();
}
}
輸出結果如下,圖一
看到了沒有,並沒有任何的輸出。
分析
自己看到上面輸出的結果也是很懵逼的,仔細查看了logback.xml中的代碼,logger中的name指定的是自己測試代碼的包啊,日志級別是info。怎么就啥都沒輸出啊。appender中的配置也沒錯啊。當初自己是找了好久的錯誤都沒有找出來。
測試代碼
首先查看自己寫的測試代碼,自己對 private final Logger logger = LoggerFactory.getLogger("測試");這行代碼產生了懷疑,這個logger形參指定的是哪一個日志框架中的對象。於是自己修改上面的測試代碼如下
public class LoggerTest {
public void loggerTest() {
Logger logger = LoggerFactory.getLogger("測試");
logger.info("info=====>");
logger.error("error====》");
logger.debug("debug=====》");
}
}
對logger logger = LoggerFactory.getLogger("測試"); 這行代碼進行debug。進入了,如圖二
getLogger方法是slf4j中獲取logger工廠,查看getILoggerFactory()方法,發現其實他就是獲取一個具體的loggerFactory工廠,看過抽象工廠設計模式的應該就知道了。這里由於我們是新建了logback.xml,那么返回的工廠類其實就是logback-classic中的LoggerContext。然后調用getLogger方法創建一個Logger對象。發現返回的Logger對象如下 圖三
此時我們繼續跟進logger.info。找到最關鍵的代碼,如圖四
該關鍵代碼在logback-classic中的Logger類中,根據這個callAppenders方法名可以知道這就是打印日志的關鍵方法,繼續跟進,查看callAppenders方法。如圖五
這個划重點了,仔細看,這個for循環中,可以看出來257行就是打印這個日志的代碼了。但是這個Logger l = this; 這個代碼是關鍵。這個this就是對象就是我們根據LoggerFactory.getLogger("測試")獲取到的。繼續跟進appendLoopOnAppenders方法。如圖六
發現是aai來打印這行日志,那么aii是怎么打印這行日志的呢,這個就要回到一開始logback.xml配置的地方了,自己配置了測試代碼下的日志輸出格式,但是沒有配置一個logger.name 叫做”測試“的包下面的日志輸出格式啊。於是自己修改logback.xml中的代碼如下
<?xml version="1.0" encoding="utf-8" ?>
<configuration debug="true" scan="true" scanPeriod="60 seconds">
<appender name="logger_stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d{YYYY-MM-dd HH:mm:ss} | %-5level | %thread | %logger - %msg%n</Pattern>
</encoder>
</appender>
<!-- name=指定打印日志文件級別 level=日志級別 additivity=是否想向上傳遞 先不加上 additivity=false 屬性 -->
<logger name="測試" level="info">
<!-- 指定輸出的appender -->
<appender-ref ref="logger_stdout"/>
</logger>
</configuration>
運行單元測試,得出結果如下圖七
有了日志輸出。
思考
我看公司代碼一般獲取日志對象都是 private final Logger logger = LoggerFactory.getLogger(this.getClass()); 然后logback.xml中的logger屬性中name都是指定某一包名。其實這里自己跟代碼跟了挺久了其中在圖五中那個for循環就是一直向上查找,logback.xml是否設置了有該包下的日志輸出格式。如果查找到有該輸出格式就輸出,並查看logback.xml中logger中你是否設置了additivity。不是這就是默認true,繼續向上查找是否還有不同的輸出格式。如果設置為false,就結束。
我舉一個例子吧這樣就很好理解了
logback.xml代碼如下
<?xml version="1.0" encoding="utf-8" ?>
<!-- debug=是否打印logback內部日志 scan=是否重新加載 scanPeriod=多久掃描一次 -->
<configuration debug="true" scan="true" scanPeriod="60 seconds">
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d{YYYY年MM月dd日 HH:mm:ss} | %-5level | %thread | %logger - %msg%n</Pattern>
</encoder>
</appender>
<appender name="logger_stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d{YYYY-MM-dd HH:mm:ss} | %-5level | %thread | %logger - %msg%n</Pattern>
</encoder>
</appender>
<!-- 指定name 在該aa.bb.cc 下的日志輸出格式 這個就是在aa.bb.cc.dd 上的日志輸出格式-->
<logger name="aa.bb.cc" level="info">
<!-- 指定輸出的appender -->
<appender-ref ref="logger_stdout"/>
</logger>
<!-- 指定name 在該aa.bb.cc.dd 下的日志輸出格式 additivity 默認為true 可以向上傳遞-->
<logger name="aa.bb.cc.dd" level="info" >
<appender-ref ref="stdout"/>
</logger>
</configuration>
測試代碼如下
package com.example.logback.logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @ClassName LoggerTest
* @Description TODO
* @Author ouyangkang
* @Date 2019-01-07 17:04
**/
@Component
public class LoggerTest {
private final Logger logger = LoggerFactory.getLogger("aa.bb.cc.dd.ee");
public void loggerTest() {
logger.info("info=====>");
logger.error("error====》");
logger.debug("debug=====》");
}
}
單元測試代碼如 上代碼里面的單元測試代碼一樣。
得到結果如下:
總結
LoggerFactory.getLogger(name),其中name對應着logback.xml中logger屬性中name=對應的子包,當然你也可以設置一樣的。logger中的additivity設置為false是代表該logger日志打印不向上傳遞, appender-ref指定的日志輸出格式。
下一篇把logback.xml中的appender屬性介紹一下並進行實踐。