Log4j日誌框架
阿新 • • 發佈:2020-12-30
簡介
- 本篇是關於
Log4j
日誌框架的介紹。 - 閱讀本文請至少了解
JUL
日誌框架,因為大部分的知識點是類似的。
Log4j日誌框架
Log4j
全稱是Log for Java
,它是Apache
的一個開源專案,通過使用Log4j
,我們可以控制日誌資訊輸出的位置是控制檯、檔案還是GUI
元件,輸出位置甚至可以是套介面伺服器、NT
的事件記錄器、UNIX Syslog
守護程序等;- 使用
Log4j
也可以控制每一條日誌的輸出格式;通過定義每一條日誌資訊的級別,我們能夠更加細緻地控制日誌的生成過程。
入門案例
- 所使用的專案使用
Maven
構建,需要引入Log4j
依賴:
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
Log4j
預設情況下是需要提供配置檔案的,如果resource
目錄下不存在log4j.properties
配置檔案,則控制檯中會輸出相應的警告資訊。- 考慮以下程式碼:
@Test
public void test() {
// 1.在沒有log4j.properties的情況下,獲取日誌記錄器物件Logger
Logger logger = Logger.getLogger(Log4jTest.class);
// 2.嘗試輸出日誌記錄
logger.info("Hey, log4j.");
}
- 此時控制檯輸出為:
- 接觸過
JUL
的都知道,當普通的Logger
RootLogger
的配置。 - 同樣地,
Log4j
中也存在RootLogger
,但由於預設情況下RootLogger
不具有任何的Appender
(即Handler
)。 - 如果程式碼僅為了測試某項功能,並不想編寫複雜的
log4j.properties
,可以使用Log4j
提供的預設配置,在獲取Logger
前使用以下程式碼載入預設配置:BasicConfigurator.configure();
- 檢視
configure()
方法的原始碼:
/** Add a {@link ConsoleAppender} that uses {@link PatternLayout} using the {@link PatternLayout#TTCC_CONVERSION_PATTERN} and prints to <code>System.out</code> to the root category. */ static public void configure() { Logger root = Logger.getRootLogger(); root.addAppender(new ConsoleAppender( new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN))); }
- 古早時期的原始碼格式有點不太現代,但意義明確:為
RootLogger
物件新增一個Appender
,其中Appender
的型別為控制器輸出的ConsoleAppender
,輸出的格式使用PatternLayout.TTCC_CONVERSION_PATTERN
。 - 以下為
PatternLayout.TTCC_CONVERSION_PATTERN
原始碼:
/** A conversion pattern equivalent to the TTCCCLayout.
Current value is <b>%r [%t] %p %c %x - %m%n</b>. */
public final static String TTCC_CONVERSION_PATTERN
= "%r [%t] %p %c %x - %m%n";
- 關於
PatternLayout
的相關格式化規則,後續會列舉出來。 - 於是,使用以下程式碼載入預設配置並輸出日誌:
@Test
public void testQuick() {
// 1.初始化配置資訊,使用預設的配置,如果不載入預設配置同時不具有log4j.properties配置檔案,程式將發出警告
BasicConfigurator.configure();
// 2.獲取日誌記錄器物件Logger
Logger logger = Logger.getLogger(Log4jTest.class);
// 3.日誌記錄輸出
logger.info("Hey, log4j.");
}
- 此時控制檯輸出為:
日誌級別
Log4j
中的日誌級別與JUL
的不同,一共提供了6
中日誌級別:FATAL
:嚴重錯誤,一般會造成系統崩潰並終止執行;ERROR
:錯誤資訊,不會影響系統執行;WARN
:警告資訊,可能會發生問題;INFO
:執行資訊,資料連線、網路連線、I/O
操作等等;DEBUG
:除錯資訊,一般在開發中使用,記錄程式變數引數傳遞資訊等等。預設級別;TRACE
:追蹤資訊,記錄程式所有的流程資訊。
- 參考以下示例程式碼:
@Test
public void testQuick() {
// 1.初始化配置資訊,使用預設的配置,如果不載入預設配置同時不具有log4j.properties配置檔案,程式將發出警告
BasicConfigurator.configure();
// 2.獲取日誌記錄器物件Logger
Logger logger = Logger.getLogger(Log4jTest.class);
// 3.日誌級別測試
logger.fatal("[FATAL] 嚴重錯誤,一般會造成系統崩潰並終止執行。");
logger.error("[ERROR] 錯誤資訊,不會影響系統執行。");
logger.warn("[WARN] 警告資訊,可能會發生問題。");
logger.info("[INFO] 執行資訊,資料連線、網路連線、I/O操作等等。");
logger.debug("[DEBUG] 除錯資訊,一般在開發中使用,記錄程式變數引數傳遞資訊等等。預設級別。");
logger.trace("[TRACE] 追蹤資訊,記錄程式所有的流程資訊。");
}
- 執行輸出,得到:
- 輸出的日誌仍然由預設的日誌級別所決定,其中預設級別為
DEBUG
。 - 為了測試預設日誌級別,可以使用以下程式碼測試
RootLogger
:- 使用
getRootLogger()
獲取RootLogger
物件; - 使用
RootLogger
中的相關方法獲取日誌物件的日誌等級及其關聯的Appender
詳情。
- 使用
@Test
public void testDetails() {
// 1.初始化配置資訊,使用預設的配置,如果不載入預設配置,將無法正常執行
BasicConfigurator.configure();
// 2.獲取日誌記錄器物件RootLogger
final Logger rootLogger = Logger.getRootLogger();
// 3.輸出配置詳情
System.out.println("Logger level: " + rootLogger.getLevel());
final Enumeration allAppenders = rootLogger.getAllAppenders();
while (allAppenders != null && allAppenders.hasMoreElements()) {
final Appender appender = (Appender) allAppenders.nextElement();
System.out.println("Appender is: " +appender.getClass().getSimpleName());
}
}
- 執行輸出:
- 注意,對於
Log4j
中的Appender
來說,它們不具有日誌等級,只有Logger
物件具有日誌等級。
Log4j相關元件
Log4J
主要由Loggers
(日誌記錄器)、Appenders
(輸出端)和Layout
(日誌格式化器)組成:Loggers
:控制日誌的輸出級別與日誌是否輸出;Appenders
:指定日誌的輸出方式(輸出到控制檯、檔案等);Layout
:控制日誌資訊的輸出格式。
a. Logger
- 日誌記錄器,負責收集處理日誌記錄,
Logger
的例項命名通常是類的全限定類名。 Logger
的名字大小寫敏感,其命名有繼承機制。- 例如:
name
為org.apache.commons
的logger
會繼承name
為org.apache
的logger
。
- 例如:
- 自
log4j 1.2
版以來,Logger
類已經取代了Category
類。對於熟悉早期版本的log4j
的人來說,Logger
類可以被視為Category
類的別名。
b. Appenders
Appender
用來指定日誌輸出到哪個地方,可以同時指定日誌的輸出目的地。Log4j
常用的輸出目的地有以下幾種:
輸出端型別 | 作用 |
---|---|
ConsoleAppender |
將日誌輸出到控制檯 |
FileAppender |
將日誌輸出到檔案中 |
DailyRollingFileAppender |
將日誌輸出到一個日誌檔案,週期為天,即每天輸出 |
RollingFileAppender |
將日誌資訊輸出到一個日誌檔案,並且指定檔案的大小,當超過指定大小,會自動將檔案重新命名,同時產生一個新的檔案 |
JDBCAppender |
將日誌資訊儲存到資料庫中 |
c. Layouts
- 佈局器
Layouts
用於控制日誌輸出內容的格式,我們可以使用各種自定義格式輸出日誌。 Log4j
常用的Layouts
有以下幾種:
格式化器型別 | 作用 |
---|---|
HTMLLayout |
格式化日誌輸出為HTML表格形式 |
SimpleLayout |
簡單的日誌輸出格式,列印的日誌格式為info-message |
PatternLayout |
最強大的格式化方式,可以根據自定義格式輸出日誌,如果沒有指定轉換格式,則使用預設的轉換格式 |
PatternLayout
中的格式化規則:
* log4j採用類似C語言的printf函式的列印格式格式化日誌資訊,具體的佔位符及其含義如下:
%m 輸出程式碼中指定的日誌資訊
%p 輸出優先順序,及DEBUG、INFO等
%n 換行符(Windows平臺的換行符為"\n",Unix平臺為"\n")
%r 輸出自應用啟動到輸出該 log 資訊耗費的毫秒數
%c 輸出列印語句所屬的類的全名
%t 輸出產生該日誌的執行緒全名
%d 輸出伺服器當前時間,預設為ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss}
%l 輸出日誌時間發生的位置,包括類名、執行緒、及在程式碼中的行數。如:Test.main(Test.java:10)
%F 輸出日誌訊息產生時所在的檔名稱
%L 輸出程式碼中的行號
%% 輸出一個"%"字元
* 可以在%與字元之間加上修飾符來控制最小寬度、最大寬度和文字的對其方式。如:
%5c 輸出category名稱,最小寬度是5,category<5,預設的情況下右對齊
%-5c 輸出category名稱,最小寬度是5,category<5,"-"號指定左對齊,會有空格
%.5c 輸出category名稱,最大寬度是5,category>5,就會將左邊多出的字元截掉,<5不會有空格
%20.30c category名稱<20補空格,並且右對齊,>30字元,就從左邊交遠銷出的字元截掉
自定義配置
-
使用
Log4j
不需要顯式地載入配置檔案,對於Maven
專案來說,程式會自動掃描resources
目錄下的log4j.properties
配置檔案。 -
自定義配置如下:
- 日誌輸出等級為
INFO
,此時RootLogger
擁有三個Appender
:ConsoleAppender
:配置中的命名為Console
;DailyRollingFileAppender
:配置中的命名為UserDefinedName
;JDBCAppender
:配置中的命名為logDB
。
RootLogger
的預設輸出級別配置在log4j.rootLogger
的首位,隨後緊跟的是關聯的Appender
的名稱,表示其預設支援輸出的方式有哪些。
- 日誌輸出等級為
# 指定日誌的輸出級別與輸出端
log4j.rootLogger=INFO, Console, UserDefinedName, logDB
# 控制檯輸出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
# 檔案輸出配置
log4j.appender.UserDefinedName=org.apache.log4j.DailyRollingFileAppender
# 指定日誌的輸出路徑
log4j.appender.UserDefinedName.File=log4j.log
# 是否以追加日誌的形式新增
log4j.appender.UserDefinedName.Append=true
# 使用自定義日誌格式化器
log4j.appender.UserDefinedName.layout=org.apache.log4j.PatternLayout
# 指定日誌的輸出格式
log4j.appender.UserDefinedName.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%t:%r] - [%p] %m%n
# 指定日誌的檔案編碼
log4j.appender.UserDefinedName.encoding=UTF-8
# MySQL輸出配置
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.cj.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useAffectedRows=true
log4j.appender.logDB.User=root
log4j.appender.logDB.Password=root
log4j.appender.logDB.Sql=INSERT INTO log(project_name, create_date, level, category, file_name, thread_name, line, all_category, message) \
values('log4j', '%d{yyyy-MM-dd HH:mm:ss}', '%p', '%c', '%F', '%t', '%L', '%l', '%m')
- 配置中包含了
sql
輸出日誌的方式,log
表的建立程式碼為:
CREATE TABLE `log` (
`log_id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`project_name` VARCHAR ( 255 ) DEFAULT NULL COMMENT '專案名稱',
`create_date` VARCHAR ( 255 ) DEFAULT NULL COMMENT '建立時間',
`level` VARCHAR ( 255 ) DEFAULT NULL COMMENT '優先順序',
`category` VARCHAR ( 255 ) DEFAULT NULL COMMENT '所在類的全名',
`file_name` VARCHAR ( 255 ) DEFAULT NULL COMMENT '輸出日誌訊息產生時所在的檔名稱 ',
`thread_name` VARCHAR ( 255 ) DEFAULT NULL COMMENT '日誌事件的執行緒名',
`line` VARCHAR ( 255 ) DEFAULT NULL COMMENT '行號',
`all_category` VARCHAR ( 255 ) DEFAULT NULL COMMENT '日誌事件的發生位置',
`message` VARCHAR ( 4000 ) DEFAULT NULL COMMENT '輸出程式碼中指定的訊息',
PRIMARY KEY ( `log_id` )
);
- 讓特定名稱的
logger
使用特定的配置:- 一個
log4j.properties
中可以配置多個且可重複的Appender
,但Appender
的命名不可重複; - 通過
log4j.logger.{loggerName}
的方式,讓指定名為loggerName
的logger
使用該配置; - 由於該
logger
仍然是隸屬於rootLogger
,因此輸出是累加的形式:- 例:如果
RootLogger
使用了ConsoleAppender
,同時Logger
也使用了ConsoleAppender
,此時控制檯將輸出兩次日誌記錄,一次為Logger
繼承自RootLogger
的輸出,另一次則為Logger
自身的輸出。
- 例:如果
- 但日誌等級
level
則取決於子日誌Logger
與RootLogger
,以等級高的一方為準。- 例:此時
RootLogger
和Logger
同時使用了ConsoleAppender
,但輸出等級分別為INFO
和WARN
,此時控制檯輸出的日誌等級僅有高於等於WARN
的記錄,即使此時RootLogger
的等級為INFO
。
- 例:此時
- 一個
- 有如下
log4j.properties
配置檔案:- 其中
RootLogger
和名為cn.hanna
的Logger
使用同一個ConsoleAppender
,但日誌等級不一致。
- 其中
# 指定日誌的輸出級別與輸出端
log4j.rootLogger=INFO, Console
# 控制檯輸出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
# 讓名為“cn.hanna”的logger使用名為Another的Appender
# 此時仍會使用rootLogger中的控制檯輸出,而Level則以兩者間較高的為準
log4j.logger.cn.hanna=WARN, Console
- 測試程式碼如下:
@Test
public void testAnother() {
// 1.獲取日誌記錄器物件Logger
Logger logger = Logger.getLogger("cn.hanna");
// 2.日誌級別測試
logger.fatal("[FATAL] 嚴重錯誤,一般會造成系統崩潰並終止執行。");
logger.error("[ERROR] 錯誤資訊,不會影響系統執行。");
logger.warn("[WARN] 警告資訊,可能會發生問題。");
logger.info("[INFO] 執行資訊,資料連線、網路連線、I/O操作等等。");
logger.debug("[DEBUG] 除錯資訊,一般在開發中使用,記錄程式變數引數傳遞資訊等等。預設級別。");
logger.trace("[TRACE] 追蹤資訊,記錄程式所有的流程資訊。");
}
- 執行輸出:
- 輸出印證了之前所陳述的觀點,不再贅述。
- 此時,如果一定需要使用
ConsoleAppender
,但不希望控制檯輸出兩次記錄,有兩種方式:- 摒棄
RootLogger
的輸出,即斷開指定Logger
與RootLogger
的繼承關係; - 摒棄
Logger
的輸出,即指定名稱的Logger
直接使用RootLogger
關聯的Appender
,不再額外指定。
- 摒棄
- 如果選擇斷開指定
Logger
與RootLogger
的繼承關係,需修改配置檔案如下:
# 指定日誌的輸出級別與輸出端
log4j.rootLogger=INFO, Console
# 控制檯輸出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
# 名為“cn.hanna”的Logger不再繼承使用RootLogger中的Appender
log4j.additivity.cn.hanna=false
log4j.logger.cn.hanna=WARN, Console
- 如果選擇直接使用
RootLogger
關聯的Appender
,需修改配置檔案如下:
# 指定日誌的輸出級別與輸出端
log4j.rootLogger=INFO, Console
# 控制檯輸出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
# 對於名稱“cn.hanna”的Logger來說,將直接使用RootLogger中關聯的Appender,但日誌等級由自身決定
log4j.logger.cn.hanna=WARN
- 兩種方式的測試類執行結果均為:
- 實際應用中,應該靈活選擇適合與程式碼的方式,去編寫配置檔案。
完整的配置檔案示例
- 同樣,在此給出一個完整的
log4j
配置檔案示例:
log4j.rootLogger=INFO, Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.appender.UserDefinedName=org.apache.log4j.DailyRollingFileAppender
log4j.appender.UserDefinedName.File=log4j.log
log4j.appender.UserDefinedName.Append=true
log4j.appender.UserDefinedName.layout=org.apache.log4j.PatternLayout
log4j.appender.UserDefinedName.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%t:%r] - [%p] %m%n
log4j.appender.UserDefinedName.encoding=UTF-8
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.cj.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useAffectedRows=true
log4j.appender.logDB.User=root
log4j.appender.logDB.Password=root
log4j.appender.logDB.Sql=INSERT INTO log(project_name, create_date, level, category, file_name, thread_name, line, all_category, message) \
values('log4j', '%d{yyyy-MM-dd HH:mm:ss}', '%p', '%c', '%F', '%t', '%L', '%l', '%m')
log4j.appender.Another=org.apache.log4j.DailyRollingFileAppender
log4j.appender.Another.File=hanna.log
log4j.appender.Another.Append=true
log4j.appender.Another.layout=org.apache.log4j.PatternLayout
log4j.appender.Another.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%t:%r] - [%p] %m%n
log4j.appender.Another.encoding=UTF-8
log4j.logger.cn.xyz=WARN, Another
log4j.additivity.cn.xyz=false
- 說明:其中
log4j.appeder.APPENDER_NAME
中的APPENDER_NAME
為相應Appender
的名稱,可以自行定義。
總結
Log4j
日誌框架近似於JUL
日誌框架,但功能性上要優於JUL
。