JAVA 日誌框架
問題:
- 控制日誌輸出的內容和格式
- 控制日誌輸出的位置
- 日誌優化:非同步日誌,日誌檔案的歸檔和壓縮
- 日誌系統的維護
- 面向介面開發 – 日誌的門面
1.1 爲什麼要用日誌框架
因爲軟件系統發展到今天已經很複雜了,特別是伺服器端軟體,涉及到的知識,內容,問題太多。在某
些方面使用別人成熟的框架,就相當於讓別人幫你完成一些基礎工作,你只需要集中精力完成系統的業
務邏輯設計 。而且框架一般是成熟,穩健的,他可以處理系統很多細節問題,比如,事務處理,安全
性,數據流控制等問題。還有框架一般都經過很多人使用,所以結構很好,所以擴充套件性也很好,而且它
是不斷升級的,你可以直接享受別人升級程式碼帶來的好處。
1.2 現有的日誌框架
JUL(java util logging)、logback、log4j、log4j2
JCL(Jakarta Commons Logging)、slf4j( Simple Logging Facade for Java)
日誌門面
JCL、slf4j
日誌實現
JUL、logback、log4j、log4j2
- JUL 學習
JUL全稱Java util Logging是java原生的日誌框架,使用時不需要另外參照第三方類庫,相對其他日誌框
架使用方便,學習簡單,能夠在小型應用中靈活使用。
2.1 JUL入門
2.1.1 架構介紹
Loggers :被稱爲記錄器,應用程式通過獲取Logger物件,呼叫其API來來發布日誌資訊。Logger
通常時應用程式存取日誌系統的入口程式。
Appenders :也被稱爲Handlers,每個Logger都會關聯一組Handlers,Logger會將日誌交給關聯
Handlers處理,由Handlers負責將日誌做記錄。Handlers在此是一個抽象,其具體的實現決定了
日誌記錄的位置可以是控制檯、檔案、網路上的其他日誌服務或操作系統日誌等。
Layouts :也被稱爲Formatters,它負責對日誌事件中的數據進行轉換和格式化。Layouts決定了
數據在一條日誌記錄中的最終形式。
Level :每條日誌訊息都有一個關聯的日誌級別。該級別粗略指導了日誌訊息的重要性和緊迫,我
可以將Level和Loggers,Appenders做關聯以便於我們過濾訊息。
Filters :過濾器,根據需要定製哪些資訊會被記錄,哪些資訊會被放過。
總結一下就是:
使用者使用Logger來進行日誌記錄,Logger持有若幹個Handler,日誌的輸出操作是由Handler完成的。
在Handler在輸出日誌前,會經過Filter的過濾,判斷哪些日誌級別過濾放行哪些攔截,Handler會將日
志內容輸出到指定位置(日誌檔案、控制檯等)。Handler在輸出日誌時會使用Layout,將輸出內容進
行排版。
3.1.2 入門案例
3.2 日誌的級別
jul中定義的日誌級別
public class JULTest {
@Test
public void testQuick() throws Exception {
// 1.建立日誌記錄器物件
Logger logger = Logger.getLogger(「com.itheima.log.JULTest」);
// 2.日誌記錄輸出
logger.info(「hello jul」);
logger.log(Level.INFO, 「info msg」);
String name = 「jack」;
Integer age = 18;
logger.log(Level.INFO, 「使用者資訊:{0},{1}」, new Object[]{name, age});
}
}
雖然我們測試了 7個日誌級別但是預設只實現info以上的級別
自定義日誌級別設定
- java.util.logging.Level中定義了日誌的級別:
SEVERE(最高值)
WARNING
INFO (預設級別)
CONFIG
FINE
FINER
FINEST(最低值)
- 還有兩個特殊的級別:
OFF,可用來關閉日誌記錄。
ALL,啓用所有訊息的日誌記錄。
@Test
public void testLogLevel() throws Exception {
// 1.獲取日誌物件
Logger logger = Logger.getLogger(「com.itheima.log.QuickTest」);
// 2.日誌記錄輸出
logger.severe(「severe」);
logger.warning(「warning」);
logger.info(「info」);
logger.config(「cofnig」);
logger.fine(「fine」);
logger.finer(「finer」);
logger.finest(「finest」);
}
@Test
public void testLogConfig() throws Exception {
// 1.建立日誌記錄器物件
Logger logger = Logger.getLogger(「com.itheima.log.JULTest」);
// 一、自定義日誌級別
// a.關閉系統預設設定
logger.setUseParentHandlers(false);
// b.建立handler物件
ConsoleHandler consoleHandler = new ConsoleHandler();
// c.建立formatter物件
SimpleFormatter simpleFormatter = new SimpleFormatter();
// d.進行關聯
consoleHandler.setFormatter(simpleFormatter);
logger.addHandler(consoleHandler);
// e.設定日誌級別
logger.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
// 二、輸出到日誌檔案
FileHandler fileHandler = new FileHandler(「d:/logs/jul.log」);
fileHandler.setFormatter(simpleFormatter);
3.3 Logger之間的父子關係
JUL中Logger之間存在父子關係,這種父子關係通過樹狀結構儲存,JUL在初始化時會建立一個頂層
RootLogger作爲所有Logger父Logger,儲存上作爲樹狀結構的根節點。並父子關係通過路徑來關聯。
logger.addHandler(fileHandler);
// 2.日誌記錄輸出
logger.severe(「severe」);
logger.warning(「warning」);
logger.info(「info」);
logger.config(「config」);
logger.fine(「fine」);
logger.finer(「finer」);
logger.finest(「finest」);
}
@Test
public void testLogParent() throws Exception {
// 日誌記錄器物件父子關係
Logger logger1 = Logger.getLogger(「com.itheima.log」);
Logger logger2 = Logger.getLogger(「com.itheima」);
System.out.println(logger1.getParent() == logger2);
// 所有日誌記錄器物件的頂級父元素 class爲java.util.logging.LogManagerKaTeX parse error: Expected 'EOF', got '}' at position 695: …est("finest");
}̲
3.4 日誌的組態檔
預設…JAVAHOME\jre\lib\logging.properties
組態檔:
@Test
public void testProperties() throws Exception {
// 讀取自定義組態檔
InputStream in =
JULTest.class.getClassLoader().getResourceAsStream(「logging.properties」);
// 獲取日誌管理器物件
LogManager logManager = LogManager.getLogManager();
// 通過日誌管理器載入組態檔
logManager.readConfiguration(in);
Logger logger = Logger.getLogger(「com.itheima.log.JULTest」);
logger.severe(「severe」);
logger.warning(「warning」);
logger.info(「info」);
logger.config(「config」);
logger.fine(「fine」);
logger.finer(「finer」);
logger.finest(「finest」);
}
RootLogger使用的處理器(獲取時設定)
handlers= java.util.logging.ConsoleHandler
RootLogger日誌等級
.level= INFO
自定義Logger
com.itheima.handlers= java.util.logging.FileHandler
自定義Logger日誌等級
com.itheima.level= INFO
忽略父日誌設定
com.itheima.useParentHandlers=false
控制檯處理器
輸出日誌級別
java.util.logging.ConsoleHandler.level = INFO
輸出日誌格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
檔案處理器
輸出日誌級別
java.util.logging.FileHandler.level=INFO
輸出日誌格式
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
輸出日誌檔案路徑
java.util.logging.FileHandler.pattern = /java%u.log
輸出日誌檔案限制大小(50000位元組)
3.5 日誌原理解析
- 初始化LogManager
- LogManager載入logging.properties設定
- 新增Logger到LogManager
- 從單例LogManager獲取Logger
- 設定級別Level,並指定日誌記錄LogRecord
- Filter提供了日誌級別之外更細粒度的控制
- Handler是用來處理日誌輸出位置
- Formatter是用來格式化LogRecord的
- LOG4J 學習
Log4j是Apache下的一款開源的日誌框架,通過在專案中使用 Log4J,我們可以控制日誌資訊輸出到控
制臺、檔案、甚至是數據庫中。我們可以控制每一條日誌的輸出格式,通過定義日誌的輸出級別,可以
更靈活的控制日誌的輸出過程。方便專案的偵錯。
官方網站: http://logging.apache.org/log4j/1.2/
4.1 Log4j入門
- 建立maven工程
- 新增依賴
java.util.logging.FileHandler.limit = 50000
輸出日誌檔案限制個數
java.util.logging.FileHandler.count = 10
輸出日誌檔案 是否是追加
java.util.logging.FileHandler.append=true
3 . java程式碼
4. 日誌的級別
log4j
log4j
1.2.17
junit
junit
4.12
public class Log4jTest {
@Test
public void testQuick() throws Exception {
// 初始化系統設定,不需要組態檔
BasicConfigurator.configure();
// 建立日誌記錄器物件
Logger logger = Logger.getLogger(Log4jTest.class);
// 日誌記錄輸出
logger.info(「hello log4j」);
// 日誌級別
logger.fatal(「fatal」); // 嚴重錯誤,一般會造成系統崩潰和終止執行
logger.error(「error」); // 錯誤資訊,但不會影響系統執行
logger.warn(「warn」); // 警告資訊,可能會發生問題
logger.info(「info」); // 程式執行資訊,數據庫的連線、網路、IO操作等
logger.debug(「debug」); // 偵錯資訊,一般在開發階段使用,記錄程式的變數、參
數等
logger.trace(「trace」); // 追蹤資訊,記錄程式的所有流程資訊
}
}
- 每個Logger都被了一個日誌級別(log level),用來控制日誌資訊的輸出。日誌級別從高到低分
爲:
fatal 指出每個嚴重的錯誤事件將會導致應用程式的退出。
error 指出雖然發生錯誤事件,但仍然不影響系統的繼續執行。
warn 表明會出現潛在的錯誤情形。
info 一般和在粗粒度級別上,強調應用程式的執行全程。
debug 一般用於細粒度級別上,對偵錯應用程式非常有幫助。
trace 是程式追蹤,可以用於輸出程式執行中的變數,顯示執行的流程。
- 還有兩個特殊的級別:
OFF,可用來關閉日誌記錄。
ALL,啓用所有訊息的日誌記錄。
注:一般只使用 4個級別,優先順序從高到低爲 ERROR > WARN > INFO > DEBUG
4.2 Log4j元件
Log4J 主要由 Loggers (日誌記錄器)、Appenders(輸出端)和 Layout(日誌格式化器)組成。其中
Loggers 控制日誌的輸出級別與日誌是否輸出;Appenders 指定日誌的輸出方式(輸出到控制檯、檔案
等);Layout 控制日誌資訊的輸出格式。
4.2.1 Loggers
日誌記錄器,負責收集處理日誌記錄,範例的命名就是類「XX」的full quailied name(類的全限定名),
Logger的名字大小寫敏感,其命名有繼承機制 機製:例如:name爲org.apache.commons的logger會繼承
name爲org.apache的logger。
Log4J中有一個特殊的logger叫做「root」,他是所有logger的根,也就意味着其他所有的logger都會直接
或者間接地繼承自root。root logger可以用Logger.getRootLogger()方法獲取。
但是,自log4j 1.2版以來, Logger 類已經取代了 Category 類。對於熟悉早期版本的log4j的人來說,
Logger 類可以被視爲 Category 類的別名。
4.2.2 Appenders
Appender 用來指定日誌輸出到哪個地方,可以同時指定日誌的輸出目的地。Log4j 常用的輸出目的地
有以下幾種:
輸出端型別 作用
ConsoleAppender 將日誌輸出到控制檯
FileAppender 將日誌輸出到檔案中
DailyRollingFileAppender 將日誌輸出到一個日誌檔案,並且每天輸出到一個新的檔案
RollingFileAppender
將日誌資訊輸出到一個日誌檔案,並且指定檔案的尺寸,當檔案大
小達到指定尺寸時,會自動把檔案改名,同時產生一個新的檔案
JDBCAppender 把日誌資訊儲存到數據庫中
格式化器型別 作用
HTMLLayout 格式化日誌輸出爲HTML表格形式
SimpleLayout 簡單的日誌輸出格式化,列印的日誌格式爲(info - message)
PatternLayout
最強大的格式化期,可以根據自定義格式輸出日誌,如果沒有指定轉換格式,
就是用預設的轉換格式
4.2.3 Layouts
佈局器 Layouts用於控制日誌輸出內容的格式,讓我們可以使用各種需要的格式輸出日誌。Log4j常用
的Layouts:
4.3 Layout的格式
在 log4j.properties 組態檔中,我們定義了日誌輸出級別與輸出端,在輸出端中分別設定日誌的輸出
格式。
- 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字元,就從左邊交遠銷出的字元截掉
4.4 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
檔案輸出設定
log4j.appender.A = org.apache.log4j.DailyRollingFileAppender
#指定日誌的輸出路徑
log4j.appender.A.File = D:/log.txt
log4j.appender.A.Append = true
#使用自定義日誌格式化器
log4j.appender.A.layout = org.apache.log4j.PatternLayout
#指定日誌的輸出格式
log4j.appender.A.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [%t:%r] -
[%p] %m%n
#指定日誌的檔案編碼
log4j.appender.A.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.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/test
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_categ
ory,message) values(‘itcast’,’%d{yyyy-MM-dd
HH:mm:ss}’,’%p’,’%c’,’%F’,’%t’,’%L’,’%l’,’%m’)
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
)
);
4.5 自定義Logger
5. JCL 學習
全稱爲Jakarta Commons Logging,是Apache提供的一個通用日誌API。
它是爲 "所有的Java日誌實現"提供一個統一的介面,它自身也提供一個日誌的實現,但是功能非常常弱
(SimpleLog)。所以一般不會單獨使用它。他允許開發人員使用不同的具體日誌實現工具: Log4j, Jdk
自帶的日誌(JUL)
JCL 有兩個基本的抽象類:Log(基本記錄器)和LogFactory(負責建立Log範例)。
RootLogger設定
log4j.rootLogger = trace,console
自定義Logger
log4j.logger.com.itheima = info,file
log4j.logger.org.apache = error
@Test
public void testCustomLogger() throws Exception {
// 自定義 com.itheima
Logger logger1 = Logger.getLogger(Log4jTest.class);
logger1.fatal(「fatal」); // 嚴重錯誤,一般會造成系統崩潰和終止執行
logger1.error(「error」); // 錯誤資訊,但不會影響系統執行
logger1.warn(「warn」); // 警告資訊,可能會發生問題
logger1.info(「info」); // 程式執行資訊,數據庫的連線、網路、IO操作等
logger1.debug(「debug」); // 偵錯資訊,一般在開發階段使用,記錄程式的變數、參數等
logger1.trace(「trace」); // 追蹤資訊,記錄程式的所有流程資訊
// 自定義 org.apache
Logger logger2 = Logger.getLogger(Logger.class);
logger2.fatal(「fatal logger2」); // 嚴重錯誤,一般會造成系統崩潰和終止執行
logger2.error(「error logger2」); // 錯誤資訊,但不會影響系統執行
logger2.warn(「warn logger2」); // 警告資訊,可能會發生問題
logger2.info(「info logger2」); // 程式執行資訊,數據庫的連線、網路、IO操作等
logger2.debug(「debug logger2」); // 偵錯資訊,一般在開發階段使用,記錄程式的變數、參
數等
logger2.trace(「trace logger2」); // 追蹤資訊,記錄程式的所有流程資訊
}
5.1 JCL入門
- 建立maven工程
- 新增依賴
3 . 入門程式碼
我們爲什麼要使用日誌門面:
- 面向介面開發,不再依賴具體的實現類。減少程式碼的耦合
- 專案通過匯入不同的日誌實現類,可以靈活的切換日誌框架
- 統一API,方便開發者學習和使用
commons-logging
commons-logging
1.2
public class JULTest {
@Test
public void testQuick() throws Exception {
// 建立日誌物件
Log log = LogFactory.getLog(JULTest.class);
// 日誌記錄輸出
log.fatal(「fatal」);
log.error(「error」);
log.warn(「warn」);
log.info(「info」);
log.debug(「debug」);
}
}
4 . 統一設定便於專案日誌的管理
5.2 JCL原理
- 通過LogFactory動態載入Log實現類
2 . 日誌門面支援的日誌實現陣列
3 . 獲取具體的日誌實現
private static final String[] classesToDiscover =
new String[]{「org.apache.commons.logging.impl.Log4JLogger」,
「org.apache.commons.logging.impl.Jdk14Logger」,
「org.apache.commons.logging.impl.Jdk13LumberjackLogger」,
「org.apache.commons.logging.impl.SimpleLog」};
for(int i = 0; i < classesToDiscover.length && result == null; ++i) {
result = this.createLogFr