Java 日志框架


title: Java 日志框架
date: 2021/02/05 12:28


一茧痕、Java 日志框架簡介

1.1 日志框架可以為我們做什么野来?

  1. 控制日志輸出的內(nèi)容和格式
  2. 控制日志輸出的位置
  3. 日志優(yōu)化:異步日志、日志文件的歸檔和壓縮

1.2 現(xiàn)有日志框架

日志門面:JCL(Jakarta Commons Logging踪旷,已廢棄)曼氛、slf4j(Simple Logging Facade for Java)、log4j2

日志實(shí)現(xiàn):log4j令野、JUL(java util logging)舀患、logback、log4j2

二气破、Java 原生日志框架

2.1 架構(gòu)(其他日志框架基本上也是這個(gè)架構(gòu))

image

Logger:記錄器聊浅,應(yīng)用程序通過 Logger 對(duì)象調(diào)用它的 api 方法來發(fā)布日志信息。Logger 對(duì)象通常是應(yīng)用程序訪問日志系統(tǒng)的入口。

Appender:也被稱為 Handlers低匙,每個(gè) Logger 會(huì)關(guān)聯(lián)一組 Handlers旷痕,Logger 會(huì)將日志交給關(guān)聯(lián)的 Handlers 處理,由 Handlers 負(fù)責(zé)對(duì)日志進(jìn)行記錄顽冶。Handler 是一個(gè)抽象欺抗,它的具體實(shí)現(xiàn)決定了日志記錄的位置(例如:控制臺(tái)、文件、網(wǎng)絡(luò)上的其他日志服務(wù)等)。

Layout:也被稱為 Formatters郑口,他負(fù)責(zé)對(duì)日志進(jìn)行轉(zhuǎn)換和格式化,Layouts決定了 數(shù)據(jù)在一條日志記錄中的最終形式报强。

Level:每條日志消息都有一個(gè)關(guān)聯(lián)的日志級(jí)別。該級(jí)別粗略的描述了該日志的重要性拱燃。

Filter:過濾器秉溉,根據(jù)需要來定制哪些信息會(huì)被記錄,哪些信息會(huì)被舍棄碗誉。

總結(jié):用戶使用 Logger 來進(jìn)行日志記錄召嘶,Logger 持有若干個(gè) Handler,日志的輸出操作是由Handler完成的哮缺。在 Handler 在輸出日志前弄跌,會(huì)經(jīng)過 Filter 的過濾,判斷哪些日志級(jí)別過濾放行哪些攔截尝苇,Handler 會(huì)將日志內(nèi)容輸出到指定位置(日志文件铛只、控制臺(tái)等)。Handler 在輸出日志時(shí)會(huì)使用 Layout糠溜,將輸出內(nèi)容進(jìn)行排版淳玩。

2.2 日志級(jí)別

JUL 中定義了 7 種日志級(jí)別:

  • SEVERE(最高值)
  • WARNING
  • INFO (默認(rèn)級(jí)別)
  • CONFIG
  • FINE
  • FINER
  • FINEST(最低值)

其中還有兩個(gè)特殊的級(jí)別:

  • OFF,可用來關(guān)閉日志記錄非竿。
  • ALL蜕着,啟用所有消息的日志記錄。

2.3 快速入門

@Test
public void testQuick() throws Exception {

    // 獲取日志記錄器對(duì)象
    Logger logger = Logger.getLogger("cn.x5456.JULTest");

    // 日志記錄輸出
    logger.info("hello jul");
    // 通用方法進(jìn)行日志記錄
    logger.log(Level.INFO, "info msg");
    // 通過占位符 方式輸出變量值
    logger.log(Level.INFO, "用戶信息:{0},{1}", new Object[]{"x5456", 18});
}

2.4 硬編碼方式修改日志級(jí)別

@Test
public void testLogConfig() {
    // 獲取日志記錄器對(duì)象
    Logger logger = Logger.getLogger("cn.x5456.JULTest");
    // 關(guān)閉系統(tǒng)默認(rèn)配置红柱,如果不關(guān)閉 info 級(jí)別以下的會(huì)記錄兩遍承匣,因?yàn)楫?dāng)前 Logger 的 Handler 記錄完之后又交給了父 Logger(RootLogger)的 Handler 記錄了一下
    logger.setUseParentHandlers(false);

    // 自定義配置日志級(jí)別
    // 創(chuàng)建 ConsoleHandler 控制臺(tái)輸出
    ConsoleHandler consoleHandler = new ConsoleHandler();
    // 創(chuàng)建簡單格式轉(zhuǎn)換對(duì)象
    SimpleFormatter simpleFormatter = new SimpleFormatter();
    // 關(guān)聯(lián)輸出的格式
    consoleHandler.setFormatter(simpleFormatter);
    logger.addHandler(consoleHandler);

    // 配置日志具體級(jí)別
    logger.setLevel(Level.ALL);
    // 配置控制臺(tái)輸出的等級(jí)
    consoleHandler.setLevel(Level.ALL);

//    // 場(chǎng)景FileHandler  文件輸出
//    FileHandler fileHandler = new FileHandler("/log/jul.log");
//    // 關(guān)聯(lián)輸出的格式
//    ileHandler.setFormatter(simpleFormatter);
//    logger.addHandler(fileHandler);

    // 2.日志記錄輸出
    logger.severe("severe");
    logger.warning("warning");
    logger.info("info"); // 默認(rèn)日志輸出級(jí)別
    logger.config("config");
    logger.fine("fine");
    logger.finer("finer");
    logger.finest("finest");
}

2.5 Logger 的父子關(guān)系

JUL 中 Logger 之間存在父子關(guān)系,這種父子關(guān)系通過樹狀結(jié)構(gòu)存儲(chǔ)锤悄,JUL在初始化時(shí)會(huì)創(chuàng)建一個(gè)頂層 RootLogger 作為所有 Logger 父 Logger韧骗,存儲(chǔ)上作為樹狀結(jié)構(gòu)的根節(jié)點(diǎn)。并父子關(guān)系通過路徑來關(guān)聯(lián)铁蹈。

@Test
public void testLogParent() {
    Logger logger1 = Logger.getLogger("cn.x5456");
    Logger logger2 = Logger.getLogger("cn");

    // true
    System.out.println(logger1.getParent() == logger2);
    // 所有日志記錄器的頂級(jí)父元素 LogManager$RootLogger
    System.out.println("logger2 Parent:" + logger2.getParent());
}

這樣做的目的是宽闲,我們可以指定某些包路徑下的類打印日志的級(jí)別众眨、格式等信息握牧。

2.6 使用配置文件

默認(rèn)配置文件路徑$JAVAHOME\jre\lib\logging.properties中容诬。

配置文件:

# RootLogger 頂級(jí)父元素指定的默認(rèn)處理器為:ConsoleHandler
handlers= java.util.logging.FileHandler,java.util.logging.ConsoleHandler
# RootLogger 頂級(jí)父元素默認(rèn)的日志級(jí)別為:ALL
.level= ALL

# 自定義 Logger 使用哪些處理器
cn.x5456.handlers = java.util.logging.FileHandler,java.util.logging.ConsoleHandler
cn.x5456.level = CONFIG
# 關(guān)閉默認(rèn)配置
cn.x5456.useParentHandlers = false

# 向日志文件輸出的 handler 對(duì)象
# 指定日志文件路徑 /logs/java0.log
java.util.logging.FileHandler.pattern = /Users/x5456/logs/java%u.log
# 指定日志文件內(nèi)容大小
java.util.logging.FileHandler.limit = 50000
# 指定日志文件數(shù)量
java.util.logging.FileHandler.count = 1
# 指定 handler 對(duì)象日志消息格式對(duì)象
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 指定以追加方式添加日志內(nèi)容
java.util.logging.FileHandler.append = true


# 向控制臺(tái)輸出的 handler 對(duì)象
# 指定 handler 對(duì)象的日志級(jí)別
java.util.logging.ConsoleHandler.level = ALL
# 指定 handler 對(duì)象的日志消息格式對(duì)象
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 指定 handler 對(duì)象的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 指定日志消息格式
java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n

測(cè)試代碼:

@Test
public void testLogProperties() throws IOException {

    // 讀取配置文件,通過類加載器
    InputStream ins = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
    // 創(chuàng)建LogManager
    LogManager logManager = LogManager.getLogManager();
    // 通過LogManager加載配置文件
    logManager.readConfiguration(ins);

    // 獲取到的日志記錄器的父記錄器是我們自定義的日志記錄器
    Logger logger = Logger.getLogger("cn.x5456.JULTest");

    logger.severe("severe");
    logger.warning("warning");
    logger.info("info");
    logger.config("config");
    logger.fine("fine");
    logger.finer("finer");
    logger.finest("finest");

    // 獲取到的日志記錄器的父記錄器是 RootLogger
    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");
}

2.7 JUL 原理

  1. 初始化LogManager
    • LogManager加載logging.properties配置
    • 添加Logger到LogManager
  2. 從單例LogManager獲取Logger
  3. 設(shè)置級(jí)別Level沿腰,并指定日志記錄LogRecord
  4. Filter提供了日志級(jí)別之外更細(xì)粒度的控制
  5. Handler是用來處理日志輸出位置
  6. Formatter是用來格式化LogRecord的
image

三览徒、Log4j

Log4j 是 Apache 下的一款開源的日志框架,通過在項(xiàng)目中使用 Log4J颂龙,我們可以控制日志信息輸出到控 制臺(tái)习蓬、文件、甚至是數(shù)據(jù)庫中措嵌。我們可以控制每一條日志的輸出格式躲叼,通過定義日志的輸出級(jí)別,可以更靈活的控制日志的輸出過程企巢。方便項(xiàng)目的調(diào)試枫慷。

使用 Log4j 記錄日志只需要引入下面的依賴即可:

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

3.1 快速入門

@Test
public void test() {

    /*
    直接運(yùn)行下面的代碼會(huì)提示如下警告。

    log4j:WARN No appenders could be found for logger (cn.x5456.Log4JTest). 沒有為 logger 配置 appenders
    log4j:WARN Please initialize the log4j system properly. 請(qǐng)配置 log4j.properties 以初始化
    log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
    
    也可以直接使用 BasicConfigurator.configure() 方法向 RootLogger 中新增一個(gè)默認(rèn)的 appender浪规,無需 properties 配置文件
    */
    // BasicConfigurator.configure();


    // 獲取日志記錄器對(duì)象
    Logger logger = Logger.getLogger(Log4JTest.class);
    // 日志記錄輸出
    logger.fatal("fatal"); // 嚴(yán)重錯(cuò)誤或听,一般會(huì)造成系統(tǒng)崩潰并終止運(yùn)行

    logger.error("error"); // 錯(cuò)誤信息,不會(huì)影響系統(tǒng)運(yùn)行
    logger.warn("warn");   // 警告信息笋婿,可能會(huì)發(fā)生問題
    logger.info("info");   // 運(yùn)行信息誉裆,數(shù)據(jù)連接、網(wǎng)絡(luò)連接缸濒、IO 操作等等
    logger.debug("debug"); // 調(diào)試信息足丢,一般在開發(fā)中使用,記錄程序變量參數(shù)傳遞信息等等

    logger.trace("trace"); // 追蹤信息庇配,記錄程序所有的流程信息
}

注:還有兩個(gè)特殊的級(jí)別: OFF斩跌,可用來關(guān)閉日志記錄。ALL讨永,啟用所有消息的日志記錄滔驶。

3.2 Log4j 的組件

Log4j 主要由 Loggers (日志記錄器)、Appenders(輸出端)和 Layout(日志格式化器)組成卿闹。

  • Loggers 控制日志的輸出級(jí)別與日志是否輸出
  • Appenders 指定日志的輸出方式(輸出到控制臺(tái)揭糕、文件 等)
  • Layout 控制日志信息的輸出格式。

3.2.1 Loggers

日志記錄器锻霎,負(fù)責(zé)收集處理日志記錄著角,實(shí)例的命名就是類"XX"的full quailied name (類的全限定名),Logger的名字大小寫敏感旋恼,其命名有繼承機(jī)制:例如:name 為 org.apache.commons 的 Logger 會(huì)繼承 name 為 org.apache 的 Logger吏口。

Log4j 中有一個(gè)特殊的 Logger 叫做 RootLogger,他是所有 Logger 的根,也就意味著其他所有的Logger都會(huì)直接或者間接地繼承自 RootLogger产徊。RootLogger 可以用 Logger.getRootLogger() 方法獲取昂勒。

下圖的 Category 是 Log4j 早期的命名,1.2 版本之后已經(jīng)改成了 Logger舟铜。

image

3.2.2 Appenders

Appender 用來指定日志輸出到哪個(gè)地方戈盈,Log4j 常用的輸出目的地有以下幾種:

Appender 實(shí)現(xiàn) 作用
ConsoleAppender 將日志輸出到控制臺(tái)
FileAppender 將日志輸出到文件中
DailyRollingFileAppender 將日志輸出到一個(gè)日志文件,并且每天輸出到一個(gè)新的文件
RollingFileAppender 將日志信息輸出到一個(gè)日志文件谆刨,并且指定文件的尺寸塘娶,當(dāng)文件大 小達(dá)到指定尺寸時(shí),會(huì)自動(dòng)把文件改名痊夭,同時(shí)產(chǎn)生一個(gè)新的文件
JDBCAppender 把日志信息保存到數(shù)據(jù)庫中

3.2.3 Layouts

布局器 Layout 用于控制日志輸出內(nèi)容的格式刁岸,讓我們可以使用各種需要的格式輸出日志。

格式化器類型 作用
HTMLLayout 格式化日志輸出為HTML表格形式
SimpleLayout 簡單的日志輸出格式化她我,打印的日志格式為(info - message)
PatternLayout 最強(qiáng)大的格式化期虹曙,可以根據(jù)自定義格式輸出日志,如果沒有指定轉(zhuǎn)換格式鸦难,就是用默認(rèn)的轉(zhuǎn)換格式
PatternLayout 的格式

log4j 采用類似 C 語言的 printf 函數(shù)的打印格式格式化日志信息根吁,具體的占位符及其含義如下:

  • %m 輸出代碼中指定的日志信息
  • %p 輸出優(yōu)先級(jí),及 DEBUG合蔽、INFO 等
  • %n 換行符(Windows平臺(tái)的換行符為 "\r\n"击敌,Unix 平臺(tái)為 "\n")
  • %r 輸出自應(yīng)用啟動(dòng)到輸出該 log 信息耗費(fèi)的毫秒數(shù)
  • %c 輸出打印語句所屬的類的全名
  • %t 輸出產(chǎn)生該日志的線程全名
  • %d 輸出服務(wù)器當(dāng)前時(shí)間,默認(rèn)為 ISO8601拴事,也可以指定格式沃斤,如:%d{yyyy年MM月dd日 HH:mm:ss}
  • %l 輸出日志時(shí)間發(fā)生的位置,包括類名刃宵、線程衡瓶、及在代碼中的行數(shù)。如:Test.main(Test.java:10)
  • %F 輸出日志消息產(chǎn)生時(shí)所在的文件名稱
  • %L 輸出代碼中的行號(hào)
  • %% 輸出一個(gè) "%" 字符

可以在 % 與字符之間加上修飾符來控制最小寬度牲证、最大寬度和文本的對(duì)其方式哮针。如:

  • %5c 輸出category名稱,最小寬度是5坦袍,category<5十厢,默認(rèn)的情況下右對(duì)齊
  • %-5c 輸出category名稱,最小寬度是5捂齐,category<5蛮放,"-"號(hào)指定左對(duì)齊,會(huì)有空格
  • %.5c 輸出category名稱奠宜,最大寬度是5包颁,category>5瞻想,就會(huì)將左邊多出的字符截掉,<5不會(huì)有空格
  • %20.30c category名稱<20補(bǔ)空格娩嚼,并且右對(duì)齊蘑险,>30字符,就從左邊交遠(yuǎn)銷出的字符截掉

3.3 測(cè)試 Appender

@Test
public void testQuick() {

    // 開啟 log4j 內(nèi)置日志記錄
    LogLog.setInternalDebugging(true);

    // 獲取日志記錄器對(duì)象
    Logger logger = Logger.getLogger(Log4jTest.class);

    //for (int i = 0; i < 10000; i++) {

    // 日志級(jí)別
    logger.fatal("fatal"); // 嚴(yán)重錯(cuò)誤待锈,一般會(huì)造成系統(tǒng)崩潰并終止運(yùn)行

    logger.error("error"); // 錯(cuò)誤信息漠其,不會(huì)影響系統(tǒng)運(yùn)行
    logger.warn("warn");   // 警告信息嘴高,可能會(huì)發(fā)生問題
    logger.info("info");   // 運(yùn)行信息竿音,數(shù)據(jù)連接、網(wǎng)絡(luò)連接拴驮、IO 操作等等
    logger.debug("debug"); // 調(diào)試信息春瞬,一般在開發(fā)中使用,記錄程序變量參數(shù)傳遞信息等等

    logger.trace("trace"); // 追蹤信息套啤,記錄程序所有的流程信息
    //}
}

配置文件:

# 指定 RootLogger 頂級(jí)父元素默認(rèn)配置信息
# 指定日志級(jí)別=trace宽气,使用的 apeender 為=console
log4j.rootLogger = trace,console,dailyFile

# 指定控制臺(tái)日志輸出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定消息格式的內(nèi)容
log4j.appender.console.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n

# 日志文件輸出的 appender 對(duì)象
log4j.appender.file = org.apache.log4j.FileAppender
# 指定消息格式 layout
log4j.appender.file.layout = org.apache.log4j.PatternLayout
# 指定消息格式的內(nèi)容
log4j.appender.file.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件保存路徑
log4j.appender.file.file = /Users/x5456/logs/log4j.log
# 指定日志文件的字符集
log4j.appender.file.encoding = UTF-8

# 按照文件大小拆分的 appender 對(duì)象
# 日志文件輸出的 appender 對(duì)象
log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
# 指定消息格式 layout
log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
# 指定消息格式的內(nèi)容
log4j.appender.rollingFile.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件保存路徑
log4j.appender.rollingFile.file = /Users/x5456/logs/log4j.log
# 指定日志文件的字符集
log4j.appender.rollingFile.encoding = UTF-8
# 指定日志文件內(nèi)容的大小
log4j.appender.rollingFile.maxFileSize = 1MB
# 指定日志文件的數(shù)量
log4j.appender.rollingFile.maxBackupIndex = 10


# 按照時(shí)間規(guī)則拆分的 appender 對(duì)象
log4j.appender.dailyFile = org.apache.log4j.DailyRollingFileAppender
# 指定消息格式 layout
log4j.appender.dailyFile.layout = org.apache.log4j.PatternLayout
# 指定消息格式的內(nèi)容
log4j.appender.dailyFile.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件保存路徑
log4j.appender.dailyFile.file = /Users/x5456/logs/log4j.log
# 指定日志文件的字符集
log4j.appender.dailyFile.encoding = UTF-8
# 指定日期拆分規(guī)則
log4j.appender.dailyFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss

3.4 自定義 Logger

@Test
public void testQuick() {

    // 開啟 log4j 內(nèi)置日志記錄
    LogLog.setInternalDebugging(true);

    // 獲取日志記錄器對(duì)象
    Logger logger = Logger.getLogger(Log4jTest.class);

    // 日志級(jí)別
    logger.fatal("fatal"); // 嚴(yán)重錯(cuò)誤,一般會(huì)造成系統(tǒng)崩潰并終止運(yùn)行

    logger.error("error"); // 錯(cuò)誤信息潜沦,不會(huì)影響系統(tǒng)運(yùn)行
    logger.warn("warn");   // 警告信息萄涯,可能會(huì)發(fā)生問題
    logger.info("info");   // 運(yùn)行信息,數(shù)據(jù)連接唆鸡、網(wǎng)絡(luò)連接涝影、IO 操作等等
    logger.debug("debug"); // 調(diào)試信息,一般在開發(fā)中使用争占,記錄程序變量參數(shù)傳遞信息等等

    logger.trace("trace"); // 追蹤信息燃逻,記錄程序所有的流程信息

    // 再創(chuàng)建一個(gè)日志記錄器對(duì)象
    Logger logger1 = Logger.getLogger(Logger.class);
    logger1.fatal("fatal logger1"); // 嚴(yán)重錯(cuò)誤,一般會(huì)造成系統(tǒng)崩潰并終止運(yùn)行
    logger1.error("error logger1"); // 錯(cuò)誤信息臂痕,不會(huì)影響系統(tǒng)運(yùn)行
    logger1.warn("warn logger1");   // 警告信息伯襟,可能會(huì)發(fā)生問題
    logger1.info("info logger1");   // 運(yùn)行信息,數(shù)據(jù)連接握童、網(wǎng)絡(luò)連接姆怪、IO 操作等等
    logger1.debug("debug logger1"); // 調(diào)試信息,一般在開發(fā)中使用澡绩,記錄程序變量參數(shù)傳遞信息等等
    logger1.trace("trace logger1"); // 追蹤信息稽揭,記錄程序所有的流程信息
}

配置文件:

# 指定 RootLogger 頂級(jí)父元素默認(rèn)配置信息
# 指定日志級(jí)別=trace,使用的 apeender 為=console
log4j.rootLogger = trace,console

# 此時(shí)會(huì)同時(shí)向控制臺(tái)和文件中輸出 info 級(jí)別的日志英古,因?yàn)槔^承了 rootLogger 的 Appender
log4j.logger.cn.x5456=info,file
log4j.logger.org.apache = error

# 指定控制臺(tái)日志輸出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定消息格式的內(nèi)容
log4j.appender.console.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n

# 日志文件輸出的 appender 對(duì)象
log4j.appender.file = org.apache.log4j.FileAppender
# 指定消息格式 layout
log4j.appender.file.layout = org.apache.log4j.PatternLayout
# 指定消息格式的內(nèi)容
log4j.appender.file.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件保存路徑
log4j.appender.file.file = /Users/x5456/logs/log4j.log
# 指定日志文件的字符集
log4j.appender.file.encoding = UTF-8

四淀衣、JCL(Jakarta Commons Logging)

Jakarta Commons Logging 是 Apache 提供的一個(gè)通用日志API。

它是為“所有的Java日志實(shí)現(xiàn)”提供一個(gè)統(tǒng)一的接口召调,它自身也提供一個(gè)日志的實(shí)現(xiàn)膨桥,但是功能非常弱 (SimpleLog)蛮浑,所以一般不會(huì)單獨(dú)使用它。

他允許開發(fā)人員使用不同的具體日志實(shí)現(xiàn)工具: Log4j, Jdk 自帶的日志(JUL)

JCL 有兩個(gè)基本的抽象類:Log(基本記錄器)和 LogFactory(負(fù)責(zé)創(chuàng)建Log實(shí)例)只嚣。

image

JCL 依賴:

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

4.1 為什么要使用日志門面

當(dāng)我們的系統(tǒng)變的更加復(fù)雜的時(shí)候沮稚,我們的日志就容易發(fā)生混亂。隨著系統(tǒng)開發(fā)的進(jìn)行册舞,可能會(huì)更新不同的日志框架蕴掏,造成當(dāng)前系統(tǒng)中存在不同的日志依賴,讓我們難以統(tǒng)一的管理和控制调鲸。就算我們強(qiáng)制要求所有的模塊使用相同的日志框架盛杰,系統(tǒng)中也難以避免使用其他類似 spring,mybatis 等其他的第三方框架,它們依賴于我們規(guī)定不同的日志框架藐石,而且他們自身的日志系統(tǒng)就有著不一致性即供,依然會(huì)出來日志體系的混亂。

所以我們需要借鑒 JDBC 的思想于微,為日志系統(tǒng)也提供一套門面逗嫡,那么我們就可以面向這些接口規(guī)范來開發(fā),避免了直接依賴具體的日志框架株依。這樣我們的系統(tǒng)在日志中驱证,就存在了日志的門面和日志的實(shí)現(xiàn)。

image
  1. 面向接口開發(fā)恋腕,不再依賴具體的實(shí)現(xiàn)類抹锄。減少代碼的耦合
  2. 項(xiàng)目通過導(dǎo)入不同的日志實(shí)現(xiàn)類,可以靈活的切換日志框架
  3. 統(tǒng)一API吗坚,方便開發(fā)者學(xué)習(xí)和使用
  4. 統(tǒng)一配置便于項(xiàng)目日志的管理

4.2 使用

@Test
public void testQuick()throws Exception{
    // 獲取 log日志記錄器對(duì)象
    Log log = LogFactory.getLog(JCLTest.class);
    // 日志記錄輸出
    log.info("hello jcl");
}

4.3 原理

image

五祈远、SLF4J

簡單日志門面(Simple Logging Facade For Java) SLF4J 主要是為了給 Java 日志訪問提供一套標(biāo)準(zhǔn)、規(guī)范的 API 框架商源,其主要意義在于提供接口车份,具體的實(shí)現(xiàn)可以交由其他日志框架(例如 log4j 和 logback 等)。 當(dāng)然 slf4j 自己也提供了功能較為簡單的實(shí)現(xiàn)牡彻,但是一般很少用到扫沼。對(duì)于一般的 Java 項(xiàng)目而言,日志框架會(huì)選擇 slf4j-api 作為門面庄吼,配上具體的實(shí)現(xiàn)框架(log4j缎除、logback等),中間使用橋接器完成橋接总寻。

SLF4J 日志門面主要提供兩大功能:

  1. 日志框架的綁定
  2. 日志框架的橋接

使用 SLF4J 的好處:

  1. 使用SLF4J框架器罐,可以在部署時(shí)遷移到所需的日志記錄框架。
  2. SLF4J提供了對(duì)所有流行的日志框架的綁定渐行,例如 log4j轰坊,JUL铸董,Simple logging 和 NOP。因此可以在部署時(shí)切換到任何這些流行的框架肴沫。
  3. 無論使用哪種綁定粟害,SLF4J 都支持參數(shù)化日志記錄消息。由于 SLF4J 將應(yīng)用程序和日志記錄框架分離颤芬,因此可以輕松編寫?yīng)毩⒂谌罩居涗浛蚣艿膽?yīng)用程序悲幅。而無需擔(dān)心用于編寫應(yīng)用程序的日志記錄框架。
  4. SLF4J 提供了一個(gè)簡單的 Java 工具站蝠,稱為遷移器汰具。使用此工具,可以遷移現(xiàn)有項(xiàng)目沉衣,這些項(xiàng)目使用日志框架(如Jakarta Commons Logging 或 log4j 或 Java.util.logging)到 SLF4J郁副。

5.1 綁定日志的實(shí)現(xiàn)

使用slf4j的日志綁定流程:

  1. 添加slf4j-api的依賴
  2. 使用slf4j的API在項(xiàng)目中進(jìn)行統(tǒng)一的日志記錄
  3. 綁定具體的日志實(shí)現(xiàn)框架
    • 綁定已經(jīng)實(shí)現(xiàn)了slf4j的日志框架,直接添加對(duì)應(yīng)依賴
    • 綁定沒有實(shí)現(xiàn)slf4j的日志框架豌习,先添加日志的適配器,再添加實(shí)現(xiàn)類的依賴
  4. slf4j有且僅有一個(gè)日志實(shí)現(xiàn)框架的綁定(如果出現(xiàn)多個(gè)默認(rèn)使用第一個(gè)依賴日志實(shí)現(xiàn))

代碼:

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jTest {

    public static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);

    // 快速入門
    @Test
    public void test01()throws Exception{
        // 日志輸出
        LOGGER.error("error");
        LOGGER.warn("wring");
        LOGGER.info("info"); // 默認(rèn)級(jí)別
        LOGGER.debug("debug");
        LOGGER.trace("trace");

        // 使用占位符輸出日志信息
        String name = "itheima";
        Integer age = 14;
        LOGGER.info("用戶:{},{}",name,age);

        // 將系統(tǒng)的異常信息輸出
        try {
            int i = 1/0;
        } catch (Exception e) {
           // e.printStackTrace();
            LOGGER.error("出現(xiàn)異常:",e);
        }
    }
}

Maven 依賴:

<!-- slf4j 日志門面 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
</dependency>

<!-- slf4j 內(nèi)置的簡單實(shí)現(xiàn)
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.21</version>
</dependency>
-->

<!--logback 日志實(shí)現(xiàn)-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

<!--nop 日志開關(guān)
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>1.7.25</version>
</dependency>
-->
<!--綁定 log4j 日志實(shí)現(xiàn),需要導(dǎo)入適配器
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.12</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
-->

<!--綁定 jul 日志實(shí)現(xiàn)拔疚,需要導(dǎo)入適配器
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-jdk14</artifactId>
    <version>1.7.25</version>
</dependency>
-->

要切換日志框架肥隆,只需替換類路徑上的slf4j綁定。例如稚失,要從java.util.logging切換到log4j栋艳,只需將 slf4j-jdk14-1.7.27.jar 替換為 slf4j-log4j12-1.7.27.jar即可。

image

5.2 綁定原理

image
image
image
image

5.3 日志橋接

通常句各,您依賴的某些組件依賴于SLF4J以外的日志記錄API吸占。您也可以假設(shè)這些組件在不久的將來會(huì)切換到SLF4J。為了解決這種情況凿宾,SLF4J附帶了幾個(gè)橋接模塊矾屯,這些模塊將對(duì)log4j,JCL和 java.util.logging API的調(diào)用重定向初厚,就好像它們是對(duì)SLF4J API一樣件蚕。

橋接解決的是項(xiàng)目中日志的遺留問題,當(dāng)系統(tǒng)中存在之前的日志API产禾,可以通過橋接轉(zhuǎn)換到slf4j的實(shí)現(xiàn)排作。

使用步驟:

  1. 先去除之前老的日志框架的依賴
  2. 添加SLF4J提供的橋接組件
  3. 為項(xiàng)目添加SLF4J的具體實(shí)現(xiàn)

不常用,不想講了亚情。其實(shí)所謂的橋接器就是把之前的日志框架的一些類全部重寫了妄痪,改為調(diào)用 slf4j 了而已(JUL 除外)。

六楞件、Logback

Logback 是由 log4j 創(chuàng)始人設(shè)計(jì)的另一個(gè)開源日志組件衫生,性能比 log4j 要好僧著。

Logback主要分為三個(gè)模塊:

  • logback-core:其它兩個(gè)模塊的基礎(chǔ)模塊
  • logback-classic:它是log4j的一個(gè)改良版本,同時(shí)它完整實(shí)現(xiàn)了slf4j API
  • logback-access:訪問模塊與Servlet容器集成提供通過Http來訪問日志的功能

注:后續(xù)的日志代碼都是通過 SLF4J 日志門面搭建日志系統(tǒng)障簿,所以在代碼是沒有區(qū)別盹愚,主要是通過修改配置文件和 pom.xml 依賴

6.1 快速開始

maven 依賴:

<!--slf4j 日志門面-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
</dependency>
<!--logback 日志實(shí)現(xiàn)-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

代碼:

public static final Logger LOGGER = LoggerFactory.getLogger(LogbackTest.class);

@Test
public void testQuick() {
    // 日志輸出
    LOGGER.error("error");
    LOGGER.warn("wring");
    LOGGER.info("info");
    LOGGER.debug("debug");// 默認(rèn)級(jí)別
    LOGGER.trace("trace");
}

6.2 logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <!--
        配置集中管理屬性
        我們可以直接改屬性的 value 值
        格式:${name}
    -->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n" />
    <!--
    日志輸出格式:
        %-5level
        %d{yyyy-MM-dd HH:mm:ss.SSS}日期
        %c類的完整名稱
        %M為method
        %L為行號(hào)
        %thread線程名稱
        %m或者%msg為信息
        %n換行
      -->
    <!--定義日志文件保存路徑屬性-->
    <property name="log_dir" value="/Users/x5456/logs" />

    <!--控制臺(tái)日志輸出的 appender-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--控制輸出流對(duì)象 默認(rèn) System.out 改為 System.err-->
        <target>System.err</target>
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--日志文件輸出的 appender-->
    <appender name="file" class="ch.qos.logback.core.FileAppender">
        <!--日志文件保存路徑-->
        <file>${log_dir}/logback.log</file>
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--html 格式日志文件輸出 appender-->
    <appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
        <!--日志文件保存路徑-->
        <file>${log_dir}/logback.html</file>
        <!--html 消息格式配置-->
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>%-5level%d{yyyy-MM-dd HH:mm:ss.SSS}%c%M%L%thread%m</pattern>
            </layout>
        </encoder>
    </appender>


    <!--日志拆分和歸檔壓縮的 appender 對(duì)象-->
    <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--日志文件保存路徑-->
        <file>${log_dir}/roll_logback.log</file>
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
        <!--指定拆分規(guī)則-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--按照時(shí)間和壓縮格式聲明拆分的文件名-->
            <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
            <!--按照文件大小拆分-->
            <maxFileSize>1MB</maxFileSize>
        </rollingPolicy>
        <!--日志級(jí)別過濾器-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!--日志過濾規(guī)則,只保存 ERROR 級(jí)別的錯(cuò)誤-->
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--異步日志-->
    <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
        <!--指定某個(gè)具體的 appender-->
        <appender-ref ref="rollFile"/>
    </appender>


    <!--root logger 配置-->
    <root level="ALL">
        <appender-ref ref="console"/>
        <appender-ref ref="async"/>
        <appender-ref ref="htmlFile"/>
    </root>

    <!--自定義 looger 對(duì)象
        additivity="false" 自定義 logger 對(duì)象是否繼承 rootLogger
     -->
    <logger name="com.itheima" level="info" additivity="false">
        <appender-ref ref="console"/>
    </logger>
</configuration>

七站故、Log4j2

Apache Log4j 2是對(duì) Log4j 的升級(jí)版皆怕,參考了 Logback 的一些優(yōu)秀的設(shè)計(jì),并且修復(fù)了一些問題西篓,因此帶來了一些重大的提升愈腾,主要有:

  • 異常處理,在logback中岂津,Appender中的異常不會(huì)被應(yīng)用感知到虱黄,但是在log4j2中,提供了一些異常處理機(jī)制吮成。
  • 性能提升橱乱,log4j2 相較于 log4j 和 logback 都具有很明顯的性能提升,后面會(huì)有官方測(cè)試的數(shù)據(jù)粱甫。
  • 自動(dòng)重載配置泳叠,參考了logback的設(shè)計(jì),當(dāng)然會(huì)提供自動(dòng)刷新參數(shù)配置茶宵,最實(shí)用的就是我們?cè)谏a(chǎn)上可以動(dòng)態(tài)的修改日志的級(jí)別而不需要重啟應(yīng)用危纫。
  • 無垃圾機(jī)制,log4j2在大部分情況下乌庶,都可以使用其設(shè)計(jì)的一套無垃圾機(jī)制种蝶,避免頻繁的日志收集導(dǎo)致的jvm gc。

7.1 快速入門

使用 log4j2 自己的門面

依賴:

<!--log4j2日志門面-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.11.1</version>
</dependency>
<!--log4j2 日志實(shí)現(xiàn)-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.1</version>
</dependency>

代碼:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;

public class Log4j2Test {

    // 定義日志記錄器對(duì)象
    public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);

    // 快速入門
    @Test
    public void testQuick()throws Exception{
        // 日志消息輸出
        LOGGER.fatal("fatal");
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("inf");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }
}

使用 slf4j 做門面

依賴:

<!--使用slf4j 作為日志門面-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
</dependency>
<!--使用 log4j2 的適配器進(jìn)行綁定-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.9.1</version>
</dependency>


<!--log4j2日志門面-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.11.1</version>
</dependency>
<!--log4j2 日志實(shí)現(xiàn)-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.1</version>
</dependency>

代碼:

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jTest {

    public static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);

    // 快速入門
    @Test
    public void test01()throws Exception{
        // 日志輸出
        LOGGER.error("error");
        LOGGER.warn("wring");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }
}

7.2 log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
    status="warn" 日志框架本身的輸出日志級(jí)別
    monitorInterval="5" 自動(dòng)加載配置文件的間隔時(shí)間瞒大,不低于 5 秒
-->
<Configuration status="debug" monitorInterval="5">

    <!--
        集中配置屬性進(jìn)行管理
        使用時(shí)通過:${name}
    -->
    <properties>
        <property name="LOG_HOME">/logs</property>
    </properties>

    <!--日志處理-->
    <Appenders>
        <!--控制臺(tái)輸出 appender-->
        <Console name="Console" target="SYSTEM_ERR">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
        </Console>

        <!--日志文件輸出 appender-->
        <File name="file" fileName="${LOG_HOME}/myfile.log">
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
        </File>

        <!--<Async name="Async">-->
        <!--<AppenderRef ref="file"/>-->
        <!--</Async>-->

        <!--使用隨機(jī)讀寫劉的日志文件輸出 appender螃征,性能提高-->
        <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
        </RandomAccessFile>

        <!--按照一定規(guī)則拆分的日志文件的 appender-->
        <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
                     filePattern="/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
            <!--日志級(jí)別過濾器-->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <!--日志消息格式-->
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n"/>
            <Policies>
                <!--在系統(tǒng)啟動(dòng)時(shí),出發(fā)拆分規(guī)則糠赦,生產(chǎn)一個(gè)新的日志文件-->
                <OnStartupTriggeringPolicy/>
                <!--按照文件大小拆分会傲,10MB -->
                <SizeBasedTriggeringPolicy size="10 MB"/>
                <!--按照時(shí)間節(jié)點(diǎn)拆分,規(guī)則根據(jù)filePattern定義的-->
                <TimeBasedTriggeringPolicy/>
            </Policies>
            <!--在同一個(gè)目錄下拙泽,文件的個(gè)數(shù)限定為 30 個(gè)淌山,超過進(jìn)行覆蓋-->
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>

    </Appenders>

    <!--logger 定義-->
    <Loggers>
        <!--自定義異步 logger 對(duì)象
            includeLocation="false" 關(guān)閉日志記錄的行號(hào)信息
            additivity="false" 不在繼承 rootlogger 對(duì)象
        -->
        <AsyncLogger name="com.itheima" level="trace" includeLocation="false" additivity="false">
            <AppenderRef ref="Console"/>
        </AsyncLogger>


        <!--使用 rootLogger 配置 日志級(jí)別 level="trace"-->
        <Root level="trace">
            <!--指定日志使用的處理器-->
            <AppenderRef ref="Console"/>

            <!--使用異步 appender-->
            <AppenderRef ref="Async"/>
        </Root>
    </Loggers>
</Configuration>

7.3 異步日志

log4j2最大的特點(diǎn)就是異步日志,其性能的提升主要也是從異步日志中受益顾瞻,我們來看看如何使用 log4j2 的異步日志泼疑。

異步日志需要下面這個(gè)依賴:

<!--異步日志依賴-->
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.4</version>
</dependency>

Log4j2 提供了兩種實(shí)現(xiàn)日志的方式,一個(gè)是通過 AsyncAppender荷荤,一個(gè)是通過 AsyncLogger退渗,分別對(duì)應(yīng)前面我們說的 Appender 組件和 Logger 組件移稳。

AsyncAppender 方式

<Appenders>
    <!--控制臺(tái)輸出 appender-->
    <Console name="Console" target="SYSTEM_ERR">
        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
    </Console>

    <!--日志文件輸出 appender-->
    <File name="file" fileName="${LOG_HOME}/myfile.log">
        <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
    </File>

    <Async name="Async">
        <AppenderRef ref="file"/>
    </Async>

AsyncLogger 方式

AsyncLogger才是log4j2 的重頭戲,也是官方推薦的異步方式会油。它可以使得調(diào)用Logger.log返回的更快个粱。你可以有兩種選擇:全局異步和混合異步。

全局異步就是翻翩,所有的日志都異步的記錄都许,在配置文件上不用做任何改動(dòng),只需要在類路徑中添加一個(gè) log4j2.component.properties 配置:

Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

混合異步就是嫂冻,你可以在應(yīng)用中同時(shí)使用同步日志和異步日志胶征,這使得日志的配置方式更加靈活。

<!--logger 定義-->
<Loggers>
    <!--自定義異步 logger 對(duì)象
        includeLocation="false" 關(guān)閉日志記錄的行號(hào)信息
        additivity="false" 不在繼承 rootlogger 對(duì)象
    -->
    <AsyncLogger name="com.itheima" level="trace" includeLocation="false" additivity="false">
        <AppenderRef ref="Console"/>
    </AsyncLogger>


    <!--使用 rootLogger 配置 日志級(jí)別 level="trace"-->
    <Root level="trace">
        <!--指定日志使用的處理器-->
        <AppenderRef ref="Console"/>

        <!--使用異步 appender-->
        <AppenderRef ref="Async"/>
    </Root>
</Loggers>

如上配置:com.itheima 日志是異步的桨仿,root日志是同步的睛低。

使用異步日志需要注意的問題:

  1. 如果使用異步日志,AsyncAppender服傍、AsyncLogger和全局日志钱雷,不要同時(shí)出現(xiàn)。性能會(huì)和 AsyncAppender 一致伴嗡,降至最低急波。
  2. 設(shè)置includeLocation=false,打印位置信息(行號(hào))會(huì)急劇降低異步日志的性能瘪校,比同步日志還要慢。

八名段、SpringBoot 中日志的使用

8.1 SpringBoot 中日志設(shè)計(jì)

<dependency>
    <artifactId>spring-boot-starter-logging</artifactId>
    <groupId>org.springframework.boot</groupId>
</dependency>

依賴關(guān)系圖:

image

總結(jié):

  1. springboot 底層默認(rèn)使用logback作為日志實(shí)現(xiàn)阱扬。
  2. 使用了SLF4J作為日志門面
  3. 將JUL也轉(zhuǎn)換成slf4j
  4. 也可以使用log4j2作為日志門面,但是最終也是通過slf4j調(diào)用logback

8.2 使用

springboot 支持 logback.xml 和 logback-spring.xml伸辟,兩者的區(qū)別是麻惶,后者可以被 spring 解析 springProfile、springProperties 等屬性信夫。

在 application.properties 中也可以簡單的對(duì)日志進(jìn)行配置:

# 指定自定義 logger 對(duì)象日志級(jí)別
logging.level.com.itheima=trace

# 指定控制臺(tái)輸出消息格式
logging.pattern.console=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread]===== %msg %n

# 指定存放日志文件的具體路徑
# logging.file=/logs/springboot.log
# 指定日志文件存放的目錄窃蹋,默認(rèn)的文件名 spring.log
logging.file.path=/logs/springboot/
# 指定日志文件消息格式
logging.pattern.file=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread]===== %msg %n

8.3 切換日志實(shí)現(xiàn)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!--排除 logback 日志實(shí)現(xiàn)-->
        <exclusion>
            <artifactId>spring-boot-starter-logging</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>

<!--使用 log4j2 的日志啟動(dòng)器-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

此時(shí) SpringBoot 支持解析 log4j2.xml 和 log4j2-spring.xml。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末静稻,一起剝皮案震驚了整個(gè)濱河市警没,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌振湾,老刑警劉巖杀迹,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異押搪,居然都是意外死亡树酪,警方通過查閱死者的電腦和手機(jī)浅碾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來续语,“玉大人垂谢,你說我怎么就攤上這事〈眩” “怎么了滥朱?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長娃豹。 經(jīng)常有香客問我焚虱,道長,這世上最難降的妖魔是什么懂版? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任鹃栽,我火速辦了婚禮,結(jié)果婚禮上躯畴,老公的妹妹穿的比我還像新娘民鼓。我一直安慰自己,他們只是感情好蓬抄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布丰嘉。 她就那樣靜靜地躺著,像睡著了一般嚷缭。 火紅的嫁衣襯著肌膚如雪饮亏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天阅爽,我揣著相機(jī)與錄音路幸,去河邊找鬼。 笑死付翁,一個(gè)胖子當(dāng)著我的面吹牛简肴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播百侧,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼砰识,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了佣渴?” 一聲冷哼從身側(cè)響起辫狼,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎观话,沒想到半個(gè)月后予借,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年灵迫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秦叛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瀑粥,死狀恐怖挣跋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狞换,我是刑警寧澤避咆,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站修噪,受9級(jí)特大地震影響查库,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜黄琼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一樊销、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脏款,春花似錦围苫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至剃盾,卻和暖如春腺占,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背痒谴。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來泰國打工湾笛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闰歪。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像蓖墅,于是被迫代替她去往敵國和親库倘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容