1. 日志的概念
1.1 日志文件
日志文件是用於記錄系統操作事件的文件集合,可分為事件日志和消息日志。具有處理歷史數據、診斷問題的追蹤以及理解系統的活動等重要作用。
在計算機中,日志文件是記錄在操作系統或其他軟件運行中發生的事件或在通信軟件的不同用戶之間的消息的文件。記錄是保持日志的行為。在最簡單的情況下,消息被寫入單個日志文件 。
許多操作系統,軟件框架和程序包括日志系統。廣泛使用的日志記錄標准是在因特網 工程任務組(IETF )RFC 5424中定義的 syslog。 syslog標准使專用的標准化子系統能夠生成,過濾,記錄和分析日志消息。
1.1.1 調試日志
軟件開發中,我們經常需要去調試程序,做一些信息,狀態的輸出便於我們查詢程序的運行狀況。為了讓我們能夠更加靈活和方便的控制這些調試的信息,所有我們需要專業的日志技術。java中尋找bug會需要重現。調試也就是debug 可以在程序運行中暫停程序運行,可以查看程序在運行中的情況。日志主
要是為了更方便的去重現問題。
1.1.2 系統日志
系統日志是記錄系統中硬件、軟件和系統問題的信息,同時還可以監視系統中發生的事件。用戶可以通過它來檢查錯誤發生的原因,或者尋找受到攻擊時攻擊者留下的痕跡。系統日志包括系統日志、應用程序日志和安全日志。
系統日志的價值
系統日志策略可以在故障剛剛發生時就向你發送警告信息,系統日志幫助你在最短的時間內發現問題。系統日志是一種非常關鍵的組件,因為系統日志可以讓你充分了解自己的環境。這種系統日志信息對於
決定故障的根本原因或者縮小系統攻擊范圍來說是非常關鍵的,因為系統日志可以讓你了解故障或者襲擊發生之前的所有事件。為虛擬化環境制定一套良好的系統日志策略也是至關重要的,因為系統日志需
要和許多不同的外部組件進行關聯。良好的系統日志可以防止你從錯誤的角度分析問題,避免浪費寶貴的排錯時間。另外一種原因是借助於系統日志,管理員很有可能會發現一些之前從未意識到的問題,在
幾乎所有剛剛部署系統日志的環境當中。
2. JAVA 日志框架
問題:
1. 控制日志輸出的內容和格式
2. 控制日志輸出的位置
3. 日志優化:異步日志,日志文件的歸檔和壓縮
4. 日志系統的維護
5. 面向接口開發 -- 日志的門面
2.1 為什么要用日志框架
因為軟件系統發展到今天已經很復雜了,特別是服務器端軟件,涉及到的知識,內容,問題太多。在某些方面使用別人成熟的框架,就相當於讓別人幫你完成一些基礎工作,你只需要集中精力完成系統的業
務邏輯設計 。而且框架一般是成熟,穩健的,他可以處理系統很多細節問題,比如,事務處理,安全性,數據流控制等問題。還有框架一般都經過很多人使用,所以結構很好,所以擴展性也很好,而且它
是不斷升級的,你可以直接享受別人升級代碼帶來的好處。
2.2 現有的日志框架
JUL(java util logging)、logback、log4j、log4j2
JCL(Jakarta Commons Logging)、slf4j( Simple Logging Facade for Java)
日志門面
JCL、slf4j
日志實現
JUL、logback、log4j、log4j2
3. JUL 學習
3.1 JUL入門
3.1.1 架構介紹
Loggers:被稱為記錄器,應用程序通過獲取Logger對象,調用其API來來發布日志信息。Logger通常時應用程序訪問日志系統的入口程序。
Appenders:也被稱為Handlers,每個Logger都會關聯一組Handlers,Logger會將日志交給關聯Handlers處理,由Handlers負責將日志做記錄。Handlers在此是一個抽象,其具體的實現決定了日志記錄的位置可以是控制台、文件、網絡上的其他日志服務或操作系統日志等。
Layouts:也被稱為Formatters,它負責對日志事件中的數據進行轉換和格式化。Layouts決定了數據在一條日志記錄中的最終形式。
Level:每條日志消息都有一個關聯的日志級別。該級別粗略指導了日志消息的重要性和緊迫,我可以將Level和Loggers,Appenders做關聯以便於我們過濾消息。
總結一下就是:
用戶使用Logger來進行日志記錄,Logger持有若干個Handler,日志的輸出操作是由Handler完成的。
在Handler在輸出日志前,會經過Filter的過濾,判斷哪些日志級別過濾放行哪些攔截,Handler會將日
志內容輸出到指定位置(日志文件、控制台等)。Handler在輸出日志時會使用Layout,將輸出內容進
行排版。
3.1.2 入門案例
public class JulTest { // 快速入門 @Test public void testQuick()throws Exception{ // 1.獲取日志記錄器對象 Logger logger = Logger.getLogger("com.example.test.JULTest"); // 2.日志記錄輸出 logger.info("hello jul"); // 通用方法進行日志記錄 logger.log(Level.INFO,"info msg"); // 通過占位符 方式輸出變量值 String name = "test"; Integer age = 13; logger.log(Level.INFO,"用戶信息:{0},{1}",new Object[]{name,age}); } }
3.2 日志的級別
jul中定義的日志級別

雖然我們測試了 7個日志級別但是默認只實現info以上的級別
// 日志級別 @Test public void testLogLevel()throws Exception{ // 1.獲取日志記錄器對象 Logger logger = Logger.getLogger("com.example.test.JULTest"); // 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 testLogConfig()throws Exception{ // 1.獲取日志記錄器對象 Logger logger = Logger.getLogger("com.example.test.JULTest"); // 關閉系統默認配置 logger.setUseParentHandlers(false); // 自定義配置日志級別 // 創建ConsolHhandler 控制台輸出 ConsoleHandler consoleHandler = new ConsoleHandler(); // 創建簡單格式轉換對象 SimpleFormatter simpleFormatter = new SimpleFormatter(); // 進行關聯 consoleHandler.setFormatter(simpleFormatter); logger.addHandler(consoleHandler); // 配置日志具體級別 logger.setLevel(Level.ALL); consoleHandler.setLevel(Level.ALL); // 場景FileHandler 文件輸出 FileHandler fileHandler = new FileHandler("d:jul.log"); // 進行關聯 fileHandler.setFormatter(simpleFormatter); 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"); }
日志:
一月 21, 2020 4:10:45 下午 com.example.test.JulTest testLogConfig 嚴重: severe 一月 21, 2020 4:10:45 下午 com.example.test.JulTest testLogConfig 警告: warning 一月 21, 2020 4:10:45 下午 com.example.test.JulTest testLogConfig 信息: info 一月 21, 2020 4:10:45 下午 com.example.test.JulTest testLogConfig 配置: config 一月 21, 2020 4:10:45 下午 com.example.test.JulTest testLogConfig 詳細: fine 一月 21, 2020 4:10:45 下午 com.example.test.JulTest testLogConfig 較詳細: finer 一月 21, 2020 4:10:45 下午 com.example.test.JulTest testLogConfig 非常詳細: finest
3.3 Logger之間的父子關系
JUL中Logger之間存在父子關系,這種父子關系通過樹狀結構存儲,JUL在初始化時會創建一個頂層
RootLogger作為所有Logger父Logger,存儲上作為樹狀結構的根節點。並父子關系通過路徑來關聯。
// Logger對象父子關系 @Test public void testLogParent()throws Exception{ Logger logger1 = Logger.getLogger("com.example.test.JULTest"); Logger logger2 = Logger.getLogger("com"); // 測試 System.out.println(logger1.getParent() == logger2); // 所有日志記錄器的頂級父元素 LogManager$RootLogger,name "" System.out.println("logger2 Parent:"+logger2.getParent() + ",name:" + logger2.getParent().getName()); // 關閉默認配置 logger2.setUseParentHandlers(false); // 設置logger2日志級別 // 自定義配置日志級別 // 創建ConsolHhandler 控制台輸出 ConsoleHandler consoleHandler = new ConsoleHandler(); // 創建簡單格式轉換對象 SimpleFormatter simpleFormatter = new SimpleFormatter(); // 進行關聯 consoleHandler.setFormatter(simpleFormatter); logger2.addHandler(consoleHandler); // 配置日志具體級別 logger2.setLevel(Level.ALL); consoleHandler.setLevel(Level.ALL); logger2.severe("severe"); logger2.warning("warning"); logger2.info("info"); logger2.config("config"); logger2.fine("fine"); logger2.finer("finer"); logger2.finest("finest"); }
3.4 日志的配置文件
默認配置文件路徑$JAVAHOME\jre\lib\logging.properties
// 加載自定義配置文件 @Test public void testLogProperties()throws Exception{ // 讀取配置文件,通過類加載器 InputStream ins = JulTest.class.getClassLoader().getResourceAsStream("logging.properties"); // 創建LogManager LogManager logManager = LogManager.getLogManager(); // 通過LogManager加載配置文件 logManager.readConfiguration(ins); // 創建日志記錄器 Logger logger = Logger.getLogger("com.example"); logger.severe("severe"); logger.warning("warning"); logger.info("info"); logger.config("config"); logger.fine("fine"); logger.finer("finer"); logger.finest("finest"); Logger logger2 = Logger.getLogger("test"); logger2.severe("severe test"); logger2.warning("warning test"); logger2.info("info test"); logger2.config("config test"); logger2.fine("fine test"); logger2.finer("finer test"); logger2.finest("finest test"); }
# RootLogger 頂級父元素指定的默認處理器為:ConsoleHandler handlers= java.util.logging.ConsoleHandler # RootLogger 頂級父元素默認的日志級別為:ALL .level= SEVERE # 自定義 Logger 使用 # com.example.handlers = java.util.logging.ConsoleHandler # com.example.level = CONFIG # 關閉默認配置 com.example.useParentHanlders = false # 向日志文件輸出的 handler 對象 # 指定日志文件路徑 /logs/java0.log java.util.logging.FileHandler.pattern = d:java%u.log # 指定日志文件內容大小 java.util.logging.FileHandler.limit = 50000 # 指定日志文件數量 java.util.logging.FileHandler.count = 1 # 指定 handler 對象日志消息格式對象 java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter # 指定以追加方式添加日志內容 java.util.logging.FileHandler.append = true # 向控制台輸出的 handler 對象 # 指定 handler 對象的日志級別 java.util.logging.ConsoleHandler.level = ALL # 指定 handler 對象的日志消息格式對象 java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # 指定 handler 對象的字符集 java.util.logging.ConsoleHandler.encoding = UTF-8 # 指定日志消息格式 java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n
3.5 日志原理解析
1. 初始化LogManager
1. LogManager加載logging.properties配置
2. 添加Logger到LogManager
2. 從單例LogManager獲取Logger
3. 設置級別Level,並指定日志記錄LogRecord
4. Filter提供了日志級別之外更細粒度的控制
5. Handler是用來處理日志輸出位置
6. Formatter是用來格式化LogRecord的