【日誌】經典日誌框架

0. 學習目標

  1. 日誌的做用和目的
  2. 日誌的框架
  3. JUL的使用
  4. LOG4J的使用
  5. JCL的使用

1. 日誌的概念

1.1 日誌文件

日誌文件是用於記錄系統操做事件的文件集合,可分爲事件日誌和消息日誌。具備處理歷史數據、診斷問題的追蹤以及理解系統的活動等重要做用。java

在計算機中,日誌文件是記錄在操做系統或其餘軟件運行中發生的事件或在通訊軟件的不一樣用戶之間的消息的文件。記錄是保持日誌的行爲。在最簡單的狀況下,消息被寫入單個日誌文件。mysql

許多操做系統,軟件框架和程序包括日誌系統。普遍使用的日誌記錄標準是在因特網工程任務組(IETF)RFC5424中定義的syslog。 syslog標準使專用的標準化子系統可以生成,過濾,記錄和分析日誌消息。sql

1.1.1 調試日誌

軟件開發中,咱們常常須要去調試程序,作一些信息,狀態的輸出便於咱們查詢程序的運行情況。爲了
讓咱們可以更加靈活和方便的控制這些調試的信息,全部咱們須要專業的日誌技術。java中尋找bug會
須要重現。調試也就是debug 能夠在程序運行中暫停程序運行,能夠查看程序在運行中的狀況。日誌主
要是爲了更方便的去重現問題。數據庫

1.1.2 系統日誌

系統日誌是記錄系統中硬件、軟件和系統問題的信息,同時還能夠監視系統中發生的事件。用戶能夠經過它來檢查錯誤發生的緣由,或者尋找受到攻擊時攻擊者留下的痕跡。系統日誌包括系統日誌、應用程序日誌和安全日誌。apache

系統日誌的價值
系統日誌策略能夠在故障剛剛發生時就向你發送警告信息,系統日誌幫助你在最短的時間內發現問題。數組

系統日誌是一種很是關鍵的組件,由於系統日誌可讓你充分了解本身的環境。這種系統日誌信息對於決定故障的根本緣由或者縮小系統攻擊範圍來講是很是關鍵的,由於系統日誌可讓你瞭解故障或者襲擊發生以前的全部事件。爲虛擬化環境制定一套良好的系統日誌策略也是相當重要的,由於系統日誌須要和許多不一樣的外部組件進行關聯。良好的系統日誌能夠防止你從錯誤的角度分析問題,避免浪費寶貴的排錯時間。另一種緣由是藉助於系統日誌,管理員頗有可能會發現一些以前從未意識到的問題,在幾乎全部剛剛部署系統日誌的環境當中。安全

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 學習

JUL全稱Java util Logging是java原生的日誌框架,使用時不須要另外引用第三方類庫,相對其餘日誌框架使用方便,學習簡單,可以在小型應用中靈活使用。

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作關聯以便於咱們過濾消息。
  • Filters:過濾器,根據須要定製哪些信息會被記錄,哪些信息會被放過。

總結一下就是:
用戶使用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.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});
    }
}

3.2 日誌的級別

jul中定義的日誌級別

  • java.util.logging.Level中定義了日誌的級別:
    • SEVERE(最高值)
    • WARNING
    • INFO (默認級別)
    • CONFIG
    • FINE
    • FINER
    • FINEST(最低值)
  • 還有兩個特殊的級別:
    • OFF,可用來關閉日誌記錄。
    • ALL,啓用全部消息的日誌記錄。

雖然咱們測試了7個日誌級別可是默認只實現info以上的級別

@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");

    // 1、自定義日誌級別
    // 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);

    // 2、輸出到日誌文件
    FileHandler fileHandler = new FileHandler("d:/logs/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");
}

3.3 Logger之間的父子關係

JUL中Logger之間存在父子關係,這種父子關係經過樹狀結構存儲,JUL在初始化時會建立一個頂層RootLogger做爲全部Logger父Logger,存儲上做爲樹狀結構的根節點。並父子關係經過路徑來關聯。

@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.LogManager$RootLogger
    name爲""
    System.out.println("logger2 parent:" + logger2.getParent() + ",name:" +
    logger2.getParent().getName());
    // 1、自定義日誌級別
    // a.關閉系統默認配置
    logger2.setUseParentHandlers(false);
    // b.建立handler對象
    ConsoleHandler consoleHandler = new ConsoleHandler();
    // c.建立formatter對象
    SimpleFormatter simpleFormatter = new SimpleFormatter();
    // d.進行關聯
    consoleHandler.setFormatter(simpleFormatter);
    logger2.addHandler(consoleHandler);
    // e.設置日誌級別
    logger2.setLevel(Level.ALL);
    consoleHandler.setLevel(Level.ALL);
    // 測試日誌記錄器對象父子關係
    logger1.severe("severe");
    logger1.warning("warning");
    logger1.info("info");
    logger1.config("config");
    logger1.fine("fine");
    logger1.finer("finer");
    logger1.finest("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字節)
java.util.logging.FileHandler.limit = 50000
# 輸出日誌文件限制個數
java.util.logging.FileHandler.count = 10
# 輸出日誌文件 是不是追加
java.util.logging.FileHandler.append=true

3.5 日誌原理解析

  1. 初始化LogManager
    1. LogManager加載logging.properties配置
    2. 添加Logger到LogManager
  2. 從單例LogManager獲取Logger
  3. 設置級別Level,並指定日誌記錄LogRecord
  4. Filter提供了日誌級別以外更細粒度的控制
  5. Handler是用來處理日誌輸出位置
  6. Formatter是用來格式化LogRecord的

4. LOG4J 學習

Log4j是Apache下的一款開源的日誌框架,經過在項目中使用 Log4J,咱們能夠控制日誌信息輸出到控制檯、文件、甚至是數據庫中。咱們能夠控制每一條日誌的輸出格式,經過定義日誌的輸出級別,能夠更靈活的控制日誌的輸出過程。方便項目的調試。
官方網站: http://logging.apache.org/log4j/1.2/

4.1 Log4j入門

  1. 創建maven工程
  2. 添加依賴

    <dependencies>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
  3. java代碼

    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"); // 追蹤信息,記錄程序的全部流程信息
        }
    }
  4. 日誌的級別
    • 每一個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 把日誌信息保存到數據庫中

4.2.3 Layouts
佈局器 Layouts用於控制日誌輸出內容的格式,讓咱們可使用各類須要的格式輸出日誌。Log4j經常使用的Layouts:

格式化器類型 做用
HTMLLayout 格式化日誌輸出爲HTML表格形式
SimpleLayout 簡單的日誌輸出格式化,打印的日誌格式爲(info - message)
PatternLayout 最強大的格式化期,能夠根據自定義格式輸出日誌,若是沒有指定轉換格式,就是用默認的轉換格式

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_category,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

# 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. JCL 學習

全稱爲Jakarta Commons Logging,是Apache提供的一個通用日誌API。

它是爲「全部的Java日誌實現」提供一個統一的接口,它自身也提供一個日誌的實現,可是功能很是常弱(SimpleLog)。因此通常不會單獨使用它。他容許開發人員使用不一樣的具體日誌實現工具: Log4j, Jdk自帶的日誌(JUL)

JCL 有兩個基本的抽象類:Log(基本記錄器)和LogFactory(負責建立Log實例)。

5.1 JCL入門

  1. 創建maven工程
  2. 添加依賴

    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
  3. 入門代碼

    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");
        }
    }

咱們爲何要使用日誌門面:

  1. 面向接口開發,再也不依賴具體的實現類。減小代碼的耦合
  2. 項目經過導入不一樣的日誌實現類,能夠靈活的切換日誌框架
  3. 統一API,方便開發者學習和使用
  4. 統一配置便於項目日誌的管理

5.2 JCL原理

  1. 經過LogFactory動態加載Log實現類
  2. 日誌門面支持的日誌實現數組

    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"};
  3. 獲取具體的日誌實現

    for(int i = 0; i < classesToDiscover.length && result == null; ++i) {
        result = this.createLogFromClass(classesToDiscover[i], logCategory, true);
    }
相關文章
相關標籤/搜索