Java中日誌簡介

1 日誌文件

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

1 調試日誌

軟件開發中,咱們常常須要去調試程序,作一些信息,狀態的輸出便於咱們查詢程序的運行情況。java

2 系統日誌

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

2 日誌框架

1 日誌框架簡述

現有日誌框架:web

JUL(java util logging)、logback、log4j、log4j2spring

JCL(Jakarta Commons Logging)、slf4j( Simple Logging Facade for Java)sql

日誌門面: JCL SLF4J數據庫

日誌實現: JUL logback log4j log4j2apache

日誌級別說明:api

級別 說明
ALL 全部信息
TRACE 程序運行軌跡
DEBUG debug調試信息
INFO 應用運行過程信息
WARN 潛在的危險情況
ERROR 錯誤信息,程序還能運行
FATAL 嚴重的錯誤,致使程序終止
OFF 不輸出日誌

日誌級別排序: 從上到下,依次增長.數組

TRACE < DEBUG < INFO < WARN < ERROR < FATAL
複製代碼

3 JUL框架

1 JUL架構介紹

graph LR
A[Application] -->B[Logger]
B-->C[Handler]
C-->D[Outside World]
B-->E[Filter]
C-->F[Filter]
C-->G[Filter]

參數說明:

  • Loggers: 記錄器,應用程序經過獲取Logger對象,調用API來發布日誌信息.Logger一般當作應用程序訪問日誌系統的入口.
  • Appenders: 稱爲Handers,每一個Logger都會關聯一組Handers,Loggeer會將日誌交給關聯Handers處理,由Handers負責將日誌作記錄. Handers在這裏是一個抽象,其具體的實現決定日誌的記錄位置(控制檯or文件等).
  • Layouts: 稱爲Formatters,負責對日誌事件中的數據進行轉換和格式化.Layouts決定了數據在一條日誌記錄中的最終形式.
  • Level: 每條日誌消息都有一個關聯的日誌級別.
  • Filters: 過濾器,根據須要定製規則,記錄哪些信息,不記錄哪些信息.

總結: 用戶使用Logger來進行記錄日誌,Logger持有若干個Handler,日誌的輸出操做有Handler完成. 在Handler輸出日誌前,會通過Filter的過濾,判斷哪些日誌級別過濾,哪些攔截. Handler將日誌輸出到指定的位置(控制檯or文件等).在輸出日誌時會使用Layout,將輸出內容按照指定格式進行排版.

案列:

@Test
    public void contextLoads() {

        //建立日誌記錄器對象
        Logger logger = Logger.getLogger("com.cf.logger.LoggerApplicationTests");

        //日誌輸出
        logger.info("Hello Jack");

        logger.log(Level.INFO,"Hello Info Msg");

        String name = "Jack";
        String userName ="Mary";
        logger.log(Level.INFO,"日誌信息: {0},{1}",new Object[]{name,userName});

    }
//結果
/* 三月 30, 2021 9:39:18 下午 com.cf.logger.LoggerApplicationTests contextLoads 信息: Hello Jack 三月 30, 2021 9:39:18 下午 com.cf.logger.LoggerApplicationTests contextLoads 信息: Hello Info Msg 三月 30, 2021 9:39:18 下午 com.cf.logger.LoggerApplicationTests contextLoads 信息: 日誌信息: Jack,Mary */
複製代碼

2 日誌級別

JUL日誌的級別:

java.util.logging.Level源碼:

public static final Level OFF = new Level("OFF",Integer.MAX_VALUE, defaultBundle);

    public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle);

    public static final Level WARNING = new Level("WARNING", 900, defaultBundle);

    public static final Level INFO = new Level("INFO", 800, defaultBundle);

    public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle);

    public static final Level FINE = new Level("FINE", 500, defaultBundle);

    public static final Level FINER = new Level("FINER", 400, defaultBundle);

    public static final Level FINEST = new Level("FINEST", 300, defaultBundle);

    public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
複製代碼

從源碼中能夠得知JUL的日誌級別以下:

ALL(特殊->全部日誌) < FINEST < FINER < FINE < CONFIG<INFO(默認)<WARNING<SEVERE(最高)<OFF(特殊->關閉日誌)
複製代碼

JUL默認INFO級別日誌,因此不指定級別使用時,只能輸出INFO級別以上的日誌信息.

案列:

@Test
    public void testLogLevel() throws Exception {
        // 1.獲取日誌對象
        Logger logger = Logger.getLogger("com.cf.logger.LoggerApplicationTests");
        // 2.日誌記錄輸出
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        logger.config("cofnig");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }
// 結果
/* 三月 30, 2021 9:51:23 下午 com.cf.logger.LoggerApplicationTests testLogLevel 嚴重: severe 三月 30, 2021 9:51:23 下午 com.cf.logger.LoggerApplicationTests testLogLevel 警告: warning 三月 30, 2021 9:51:23 下午 com.cf.logger.LoggerApplicationTests testLogLevel 信息: info */
複製代碼

自定義日誌級別配置:

@Test
    public void testLogConfig() throws Exception {
        // 1.建立日誌記錄器對象
        Logger logger = Logger.getLogger("com.cf.logger.LoggerApplicationTests");
        // 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:/jul.log");
        fileHandler.setFormatter(simpleFormatter);
        logger.addHandler(fileHandler);

        // 2.日誌記錄輸出
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        logger.config("cofnig");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");

    }
//結果
/* 三月 30, 2021 9:57:56 下午 com.cf.logger.LoggerApplicationTests testLogConfig 嚴重: severe 三月 30, 2021 9:57:56 下午 com.cf.logger.LoggerApplicationTests testLogConfig 警告: warning 三月 30, 2021 9:57:56 下午 com.cf.logger.LoggerApplicationTests testLogConfig 信息: info 三月 30, 2021 9:57:56 下午 com.cf.logger.LoggerApplicationTests testLogConfig 配置: cofnig 三月 30, 2021 9:57:56 下午 com.cf.logger.LoggerApplicationTests testLogConfig 詳細: fine 三月 30, 2021 9:57:56 下午 com.cf.logger.LoggerApplicationTests testLogConfig 較詳細: finer 三月 30, 2021 9:57:56 下午 com.cf.logger.LoggerApplicationTests testLogConfig 很是詳細: finest 在D盤下生成jul.log.txt文件 */
複製代碼

3 Logger間的父子關係

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

案列:

@Test
    public void testLogParent() throws Exception {
        // 日誌記錄器對象父子關係
        Logger logger1 = Logger.getLogger("com.cf.logger.LoggerApplicationTests");
        Logger logger2 = Logger.getLogger("com.cf.logger");
        System.out.println(logger1.getParent() == logger2);
        // 全部日誌記錄器對象的頂級父元素 logger2 parent:java.util.logging.LogManager$RootLogger@299a06ac,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");
    }
複製代碼

4 日誌的配置文件

默認的配置文件路徑: $JAVAHOME\jre\lib\logging.properties

logging.properties日誌配置

## RootLogger使用的處理器(獲取時設置)
handlers= java.util.logging.ConsoleHandler
# RootLogger日誌等級
.level= INFO
## 自定義Logger
com.cf.handlers= java.util.logging.FileHandler
# 自定義Logger日誌等級
com.cf.level= INFO
# 忽略父日誌設置
com.cf.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
複製代碼

案列:

@Test
    public void testProperties() throws Exception {
        // 讀取自定義配置文件
        InputStream in = LoggerApplicationTests.class.getClassLoader().getResourceAsStream("logging.properties");
        // 獲取日誌管理器對象
        LogManager logManager = LogManager.getLogManager();
        // 經過日誌管理器加載配置文件
        logManager.readConfiguration(in);
        Logger logger = Logger.getLogger("com.cf.logger.LoggerApplicationTests");
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }
//結果
/* 在D盤中生成 java0.log.0文件名的日誌 */
複製代碼

5 JUL日誌運行原理

1 初始化LogManager

  • 1 LogManager加載logging.properties文件
  • 2 添加Logger到LogManager

2 從單例LogManager中獲取Logger

3 設置級別Level.並指定日誌記錄LogRecord

4 Filter提供了日誌級別以外更細粒度的控制

5 Handler是用來處理日誌輸出位置

6 Formatter是用來格式化LogRecord的.

JUL流程示意圖

graph LR
A[應用] -->B[Logger]
B-->C[LogRecord]
C-->D[Handler]
D-->E[輸出]
F[LogManager]-->B
G[Level]-->C
H[Filter]-->D
I[Formatter]-->E

4 Log4j框架

Log4j是Apache下的一款開源的日誌框架.

官網: logging.apache.org/log4j/1.2/

1 Log4j入門

添加pom.xml座標

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

複製代碼

案例:

package com.cf.logger;


import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.junit.Test;

public class LoggerApplicationTests {
@Test
    public void testQuick() throws Exception {
        // 初始化系統配置,不須要配置文件
        BasicConfigurator.configure();
        // 建立日誌記錄器對象
        Logger logger = Logger.getLogger(LoggerApplicationTests.class);
        // 日誌記錄輸出
        logger.info("hello log4j");
        // 日誌級別
        logger.fatal("fatal"); // 嚴重錯誤,通常會形成系統崩潰和終止運行
        logger.error("error"); // 錯誤信息,但不會影響系統運行
        logger.warn("warn"); // 警告信息,可能會發生問題
        logger.info("info"); // 程序運行信息,數據庫的鏈接、網絡、IO操做等
        logger.debug("debug"); // 調試信息,通常在開發階段使用,記錄程序的變量、參數等
        logger.trace("trace"); // 追蹤信息,記錄程序的全部流程信息
    }
}    
複製代碼

2 日誌的級別

org.apache.log4j.Level源碼:

final static public Level OFF = new Level(OFF_INT, "OFF", 0);

  final static public Level FATAL = new Level(FATAL_INT, "FATAL", 0);

  final static public Level ERROR = new Level(ERROR_INT, "ERROR", 3);

  final static public Level WARN  = new Level(WARN_INT, "WARN",  4);

  final static public Level INFO  = new Level(INFO_INT, "INFO",  6);

  final static public Level DEBUG = new Level(DEBUG_INT, "DEBUG", 7);

  public static final Level TRACE = new Level(TRACE_INT, "TRACE", 7);

  final static public Level ALL = new Level(ALL_INT, "ALL", 7);
複製代碼

從源碼中能夠得知JUL的日誌級別以下:

OFF(特殊->關閉日誌) > Fatal(嚴重錯誤,致使程序退出) > ERROR(可能錯誤,但不影響系統運行) > WARN(潛在錯誤)>INFO(粗粒度)>DUBUG(細粒度)>TRACE(程序追蹤)>ALL(特殊->全部日誌) 
複製代碼

一般使用級別: ERROR > WARN > INFO > DEBUG

3 Log4j組件

由下列三大組件組成:

  • Loggers(日誌記錄器): 控制日誌的輸出級別與日誌是否輸出.
  • Appenders(輸出端): 日誌的輸出方式(控制檯or文件等)
  • Layout(日誌格式化器): 規定日誌信息的輸出格式

1 Loggers

Loggers: 日誌記錄器,收集處理日誌記錄,實例的命名就是類的全限定名,Logger名字大小寫敏感,且命名有繼承機制.

name爲com.cf.logger的logger會繼承name爲con.cf的logger

log4j中有一個特殊的logger叫作root,它是全部logger的根.根logger能夠用Logger.getRootLogger()方法獲取.

log4j從1.2版本,Logger類取代了Category類

2 Appenders

Appenders: 指定日誌輸出的地方.

輸出地方總結圖:

輸出地方 說明
ConsoleAppender 輸出到控制檯
FileAppender 輸出到文件
DailyRollingFileAppender 天天輸出到一個新文件
RollingFileAppender 輸出到日誌文件,指定文件的尺寸,當文件到達指定尺寸,自定把文件更名,同時產生一個新的文件
JDBCAppender 保存到數據庫中

3 Layouts

Layouts: 控制日誌輸出內容的格式.

經常使用格式總結:

格式化器類型 做用
HTMLLayout 輸出爲HTML表格形式
SimpleLayout 志輸出爲HTML表格形式
PatternLayout 能夠自定義格式輸出日誌,沒有定義,就用默認格式

4 總結

1 Layout格式

* 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字符,就從左邊交遠銷出的字符截掉
複製代碼

2 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')

複製代碼

sql腳本:

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`)
);
複製代碼

3 自定義Logger

# RootLogger配置
log4j.rootLogger = trace,console
# 自定義Logger
log4j.logger.com.cf.logger = info,file
log4j.logger.org.apache = error
複製代碼

案例:

@Test
public void testCustomLogger() throws Exception {
        // 自定義 com.cf
        Logger logger1 = Logger.getLogger(LoggerApplicationTests.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框架

JCL: Jakarta Commons Logging , 是Apache提供的一個通用日誌API,它自帶一個日誌的實現(SimpleLog),功能比較弱.它也爲Java日誌實現提供一個統一的接口,容許開發人員使用不一樣的具體日誌實現工具: Log4j,JDK自帶的日誌JUL.

JCL的兩個基本抽象類:

  • Log基本記錄器
  • LogFactory(負責建立Log實例)
graph TD
A[Java應用]-->B[JCL]
B-->C[log4j]
B-->D[jdk14]
B-->E[simpleLog]

1 JCL的簡介

添加pom.xml

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
複製代碼

案例:

@Test
    public void testQuick() throws Exception {
        // 建立日誌對象
        Log log = LogFactory.getLog(LoggerApplicationTests.class);
        // 日誌記錄輸出
        log.fatal("fatal");
        log.error("error");
        log.warn("warn");
        log.info("info");
        log.debug("debug");
    }
//結果
/* 三月 31, 2021 9:03:50 下午 com.cf.logger.LoggerApplicationTests testQuick 嚴重: fatal 三月 31, 2021 9:03:50 下午 com.cf.logger.LoggerApplicationTests testQuick 嚴重: error 三月 31, 2021 9:03:50 下午 com.cf.logger.LoggerApplicationTests testQuick 警告: warn 三月 31, 2021 9:03:50 下午 com.cf.logger.LoggerApplicationTests testQuick 信息: info */
複製代碼

總結: 使用日誌門面的優勢.

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

2 JCL的原理

1 經過LogFactory動態加載Log實現類.(Log實現類有不少)

image-20210331211245201

2 日誌門面支持的日誌實現數組org.apache.commons.logging.impl.LogFactoryImpl

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 獲取具體的日誌實現org.apache.commons.logging.impl.LogFactoryImpl

for(int i = 0; i < classesToDiscover.length && result == null; ++i) {
result = this.createLogFromClass(classesToDiscover[i], logCategory,
true);
}
複製代碼

6 日誌門面

1 日誌門面簡述

常見的日誌門面:

JCL slf4j

常見的日誌實現:

JUL log4j logback log4j2

image-20210331213002365

日誌框架發展順序:

log4j --> JUL --> JCL --> slf4j --> logback --> log4j2
複製代碼

2 SLF4J框架

官網: www.slf4j.org/

SLF4J: (Simple Logging Facade For Java)簡單日誌門面. 主要給Java日誌訪問提供一套標準,規範的API框架.提供接口,具體的實現由其餘日誌框架. 經常使用的日誌框架,選擇slf4j-api做爲門面,由log4j,logback等框架去實現,中間使用橋接器完成橋接.

1 SLF4J的簡介

添加pom.xml座標

<!--slf4j core 使用slf4j必須添加-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.27</version>
</dependency>
<!--slf4j 自帶的簡單日誌實現 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.27</version>
</dependency>
複製代碼

案列:

public class LoggerApplicationTests {
    
    // 聲明日誌對象
    public final static Logger LOGGER = LoggerFactory.getLogger(LoggerApplicationTests.class);
    @Test
    public void test() throws Exception {
        //打印日誌信息
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
        // 使用佔位符輸出日誌信息
        String name = "jack";
        Integer age = 18;
        LOGGER.info("用戶:{},{}", name, age);
        // 將系統異常信息寫入日誌
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            // e.printStackTrace();
            LOGGER.info("出現異常:", e);
        }
    }
}
//結果
/* [main] ERROR com.cf.logger.LoggerApplicationTests - error [main] WARN com.cf.logger.LoggerApplicationTests - warn [main] INFO com.cf.logger.LoggerApplicationTests - info [main] INFO com.cf.logger.LoggerApplicationTests - 用戶:jack,18 [main] INFO com.cf.logger.LoggerApplicationTests - 出現異常: java.lang.ArithmeticException: / by zero */
複製代碼

總結:

  • 1 使用SLF4J框架,能夠在部署時遷移到須要的日誌記錄框架
  • 2 SLF4J提供了對全部流行的日誌框架的綁定,像log4j,JUL,Simple logging和NOP。所以能夠在部署時任意切換。
  • 3 不管使用哪一種綁定,SLF4J都支持參數化日誌記錄消息.因爲SLF4J將應用程序和日誌記錄框架分離, 所以能夠輕鬆編寫獨立於日誌記錄框架的應用程序.
  • 4 SLF4J提供了一個簡單的Java工具,稱爲遷移器。使用此工具,能夠遷移現有項目,這些項目使用日誌 框架(如Jakarta Commons Logging(JCL)或log4j或Java.util.logging(JUL))到SLF4J.

2 綁定日誌(Binding)

使用slf4j的日誌綁定流程:

  • 1 添加slf4j-api的依賴
  • 2 使用slf4j的API在項目中進行統一的日誌記錄
  • 3 綁定具體的日誌實現框架
    • 1 綁定已經實現了slf4j的日誌框架,直接添加對應依賴.
    • 2 綁定沒有實現slf4j的日誌框架,先添加日誌的適配器,再添加實現類的依賴
  • 4 slf4j有且僅有一個日誌實現框架的綁定(有多個,默認使用第一個日誌實現)

使用時導入依賴:

<!--slf4j core 使用slf4j必須添加-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.27</version>
</dependency>

<!-- log4j-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.27</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

<!-- jul -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-jdk14</artifactId>
    <version>1.7.27</version>
</dependency>

<!--jcl -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-jcl</artifactId>
    <version>1.7.27</version>
</dependency>

<!-- nop -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>1.7.27</version>
</dependency>
複製代碼

如上所示,要切換日誌框架,只須要替換路徑上的slf4j綁定.

e.g: 要從java.util.logging切換到log4j,只須要將slf4j-jdk14 替換成slf4j-log4j12

image-20210331221130213

SLF4J不依賴任何特殊的類裝載.每一個SLF4J綁定在編譯時都是硬鏈接的,使用一個且只有一個特定的日誌記錄框架.

e.g: slf4j-log4j12-1.7.27.jar綁定在編譯時綁定以使用log4j。在代碼中,除了slf4j-api-1.7.27.jar以外,只需將選擇的一個且只有一個綁定放到相應的類路徑位置。不要在類路徑上放置多個綁定.

3 橋接舊的日誌框架(Briding)

橋接: 解決項目中日誌遺留問題.,當系統中存在以前的日誌API,能夠經過橋接轉換到slf4j的實現.

  • 1 先去除老的日誌框架依賴
  • 2 添加SLF4J提供的橋接組件
  • 3 添加SLF4J的具體實現

image-20210331221930071

遷移的方式:

要使用SLF4J的橋接器,替換原有的日誌框架,首先就是刪除掉原項目中的日誌框架的依賴。而後替換成SLF4J提供的橋接器。

<!-- log4j-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.27</version>
</dependency>

<!-- jul -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>1.7.27</version>
</dependency>

<!--jcl -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.27</version>
</dependency>

複製代碼

tips:

  • 1 jcl-over-slf4j.jarslf4j-jcl.jar不能同時部署。前一個jar文件將致使JCL將日誌系統的選擇委託給 SLF4J,後一個jar文件將致使SLF4J將日誌系統的選擇委託給JCL,從而致使無限循環。
  • 2 log4j-over-slf4j.jarslf4j-log4j12.jar不能同時出現.
  • 3 jul-to-slf4j.jarslf4j-jdk14.jar不能同時出現.
  • 4 全部的橋接都只對Logger日誌記錄器對象有效,若是程序中調用了內部的配置類或者是 Appender,Filter等對象,將沒法產生效果。

4 SLF4J的原理

1 SLF4J經過LoggerFactory加載日誌具體的實現對象。

2 LoggerFactory在初始化的過程當中,會經過performInitialization()方法綁定具體的日誌實現。

3 在綁定具體實現的時候,經過類加載器,加載org/slf4j/impl/StaticLoggerBinder.class。

4 只要是一個日誌實現框架,在org.slf4j.impl包中提供一個本身的StaticLoggerBinder類,在其中提供具體日誌實現的LoggerFactory就能夠被SLF4J所加載。

3 Logback框架

官網: logback.qos.ch/index.html

Logback: 由log4j創始人設計的開源日誌組件,性能比log4j好.

主要分紅三個模塊:

  • logback-core:其它兩個模塊的基礎模塊
  • logback-classic:它是log4j的一個改良版本,同時它完整實現了slf4j API
  • logback-access:訪問模塊與Servlet容器集成提供經過Http來訪問日誌的功能

1 logback簡介

添加pom.xml座標

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
複製代碼

案例:

public class LoggerApplicationTests {

    // 聲明日誌對象
    public final static Logger LOGGER = LoggerFactory.getLogger(LoggerApplicationTests.class);

    @Test
    public void testSlf4j(){
        //打印日誌信息
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }
}
//結果
/* 22:32:30.844 [main] ERROR com.cf.logger.LoggerApplicationTests - error 22:32:30.853 [main] WARN com.cf.logger.LoggerApplicationTests - warn 22:32:30.853 [main] INFO com.cf.logger.LoggerApplicationTests - info 22:32:30.853 [main] DEBUG com.cf.logger.LoggerApplicationTests - debug */
複製代碼

2 logback配置

logback會依次讀取如下類型配置文件:(都不存在,採用默認配置)

  • logback.groovy
  • logback-test.xml
  • logback.xml

1 logback組件間的關係

  • Logger: 日誌記錄器,,把它關聯到應用的對應的context上後,主要用於存放日誌對象,也 能夠定義日誌類型、級別。
  • Appender: 用於指定日誌輸出的目的地,目的地能夠是控制檯、文件、數據庫等等。
  • Layout: 負責把事件轉換成字符串,格式化的日誌信息的輸出。在logback中Layout對象被封 裝在encoder中。

2 基本配置信息

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日誌輸出格式: %-5level %d{yyyy-MM-dd HH:mm:ss.SSS}日期 %c類的完整名稱 %M爲method %L爲行號 %thread線程名稱 %m或者%msg爲信息 %n換行 -->
<!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度 %msg:日誌消息,%n是換行符-->
<property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %c [%thread] %-5level %msg%n"/>
<!-- Appender: 設置日誌信息的去向,經常使用的有如下幾個 ch.qos.logback.core.ConsoleAppender (控制檯) ch.qos.logback.core.rolling.RollingFileAppender (文件大小到達指定尺 寸的時候產生一個新文件) ch.qos.logback.core.FileAppender (文件) -->

<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--輸出流對象 默認 System.out 改成 System.err-->
<target>System.err</target>
<!--日誌格式配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!-- 用來設置某一個包或者具體的某一個類的日誌打印級別、以及指定<appender>。 <loger>僅有一個name屬性,一個可選的level和一個可選的addtivity屬性 name: 用來指定受此logger約束的某一個包或者具體的某一個類。 level: 用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 若是未設置此屬性,那麼當前logger將會繼承上級的級別。 additivity: 是否向上級loger傳遞打印信息。默認是true。 <logger>能夠包含零個或多個<appender-ref>元素,標識這個appender將會添加到這個 logger -->
<!-- 也是<logger>元素,可是它是根logger。默認debug level:用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, <root>能夠包含零個或多個<appender-ref>元素,標識這個appender將會添加到這個 logger。 -->
<root level="ALL">
<appender-ref ref="console"/>
</root>
</configuration>
複製代碼

3 FileAppender配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 自定義屬性 能夠經過${name}進行引用-->
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c %M %L [%thread] %m %n"/>
<!-- 日誌輸出格式: %d{pattern}日期 %m或者%msg爲信息 %M爲method %L爲行號 %c類的完整名稱 %thread線程名稱 %n換行 %-5level -->
<!-- 日誌文件存放目錄 -->
<property name="log_dir" value="d:/logs"></property>
<!--控制檯輸出appender對象-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--輸出流對象 默認 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">
<!--日誌格式配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<!--日誌輸出路徑-->
<file>${log_dir}/logback.log</file>
</appender>
<!-- 生成html格式appender對象 -->
<appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
<!--日誌格式配置-->
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>%level%d{yyyy-MM-dd
HH:mm:ss}%c%M%L%thread%m</pattern>
</layout>
</encoder>
<!--日誌輸出路徑-->
<file>${log_dir}/logback.html</file>
</appender>
<!--RootLogger對象-->
<root level="all">
<appender-ref ref="console"/>
<appender-ref ref="file"/>
<appender-ref ref="htmlFile"/>
</root>
</configuration>
複製代碼

4 RollingFileAppender配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration> 
<!-- 自定義屬性 能夠經過${name}進行引用-->
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c %M %L [%thread] %m %n"/>
<!-- 日誌輸出格式: %d{pattern}日期 %m或者%msg爲信息 %M爲method %L爲行號 %c類的完整名稱 %thread線程名稱 %n換行 %-5level -->
<!-- 日誌文件存放目錄 -->
<property name="log_dir" value="d:/logs"></property>
<!--控制檯輸出appender對象-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--輸出流對象 默認 System.out 改成 System.err-->
<target>System.err</target>
<!--日誌格式配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!-- 日誌文件拆分和歸檔的appender對象-->
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日誌格式配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<!--日誌輸出路徑-->
<file>${log_dir}/roll_logback.log</file>
<!--指定日誌文件拆分和壓縮規則-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--經過指定壓縮文件名稱,來肯定分割文件方式-->
<fileNamePattern>${log_dir}/rolling.%d{yyyy-MMdd}.log%i.gz</fileNamePattern>
<!--文件拆分大小-->
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
</appender>
<!--RootLogger對象-->
<root level="all">
<appender-ref ref="console"/>
<appender-ref ref="rollFile"/>
</root>
</configuration    
複製代碼

5 Filter和異步日誌配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 自定義屬性 能夠經過${name}進行引用-->
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c %M %L [%thread] %m %n"/>
<!-- 日誌輸出格式: %d{pattern}日期 %m或者%msg爲信息 %M爲method %L爲行號 %c類的完整名稱 %thread線程名稱 %n換行 %-5level -->
<!-- 日誌文件存放目錄 -->
<property name="log_dir" value="d:/logs/"></property>
<!--控制檯輸出appender對象-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--輸出流對象 默認 System.out 改成 System.err-->
<target>System.err</target>
<!--日誌格式配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!-- 日誌文件拆分和歸檔的appender對象-->
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日誌格式配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<!--日誌輸出路徑-->
<file>${log_dir}roll_logback.log</file>
<!--指定日誌文件拆分和壓縮規則-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--經過指定壓縮文件名稱,來肯定分割文件方式-->
<fileNamePattern>${log_dir}rolling.%d{yyyy-MMdd}.log%i.gz</fileNamePattern>
<!--文件拆分大小-->
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
<!--filter配置-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!--設置攔截日誌級別-->
<level>error</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--異步日誌-->
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="rollFile"/>
</appender>
<!--RootLogger對象-->
<root level="all">
<appender-ref ref="console"/>
<appender-ref ref="async"/>
</root>
<!--自定義logger additivity表示是否從 rootLogger繼承配置-->
<logger name="com.itheima" level="debug" additivity="false">
<appender-ref ref="console"/>
</logger>
</configuration>                 
複製代碼

6 官方提供的log4j.properties轉換成logback.xml

logback.qos.ch/translator/

3 logback-access的使用

logback-access模塊與Servlet容器(如Tomcat和Jetty)集成,以提供HTTP訪問日誌功能。咱們可使 用logback-access模塊來替換tomcat的訪問日誌。

使用方法:

1 將logback-access.jar與logback-core.jar複製到$TOMCAT_HOME/lib/目錄下

2 修改$TOMCAT_HOME/conf/server.xml中的Host元素中添加:

<Valve className="ch.qos.logback.access.tomcat.LogbackValve" />
複製代碼

3 logback默認會在$TOMCAT_HOME/conf下查找文件 logback-access.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- always a good activate OnConsoleStatusListener -->
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>
<property name="LOG_DIR" value="${catalina.base}/logs"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/access.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>access.%d{yyyy-MM-dd}.log.zip</fileNamePattern>
</rollingPolicy> 
    <encoder>
<!-- 訪問日誌的格式 -->
<pattern>combined</pattern>
</encoder>
</appender>
<appender-ref ref="FILE"/>
</configuration>

複製代碼

4 官方配置: logback.qos.ch/access.html…

4 log4j2框架

官網: logging.apache.org/log4j/2.x/

log4j2是log4j的升級版,參考了logback的一些優秀的設計,而且修復了一些問題:

  • 參考了logback的一些優秀的設計,而且修復了一些問題,
  • 性能提高, log4j2相較於log4j 和logback都具備很明顯的性能提高.
  • 自動重載配置,參考了logback的設計,固然會提供自動刷新參數配置,最實用的就是咱們在生產 上能夠動態的修改日誌的級別而不須要重啓應用。
  • 無垃圾機制,log4j2在大部分狀況下,均可以使用其設計的一套無垃圾機制,避免頻繁的日誌收集 致使的jvm gc。

1 log4j2簡介

目前使用最多的日誌門面是SLF4J. 雖然Log4j2也是日誌門面,由於它的日誌實現功能很是強 大,性能優越。因此你們通常仍是將Log4j2看做是日誌的實現,Slf4j + Log4j2應該是將來的大勢所趨。

1 添加依賴:

<!-- Log4j2 門面API-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.11.1</version>
</dependency>
<!-- Log4j2 日誌實現 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.1</version>
</dependency>
複製代碼

2 案列:

public class LoggerApplicationTests {

    // 定義日誌記錄器對象
    public static final Logger LOGGER = LogManager.getLogger(LoggerApplicationTests.class);
    @Test
    public void test() throws Exception {
        LOGGER.fatal("fatal");
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }
}
//結果
/* 23:12:26.384 [main] FATAL com.cf.logger.LoggerApplicationTests - fatal 23:12:26.388 [main] ERROR com.cf.logger.LoggerApplicationTests - error */
複製代碼

3 使用slf4j做爲日誌的門面,使用log4j2做爲日誌的實現

<!-- Log4j2 門面API-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.1</version>
</dependency>
<!-- Log4j2 日誌實現 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
<!--使用slf4j做爲日誌的門面,使用log4j2來記錄日誌 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!--爲slf4j綁定日誌實現 log4j2的適配器 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.10.0</version>
</dependency>
複製代碼

2 Log4j2配置

log4j2默認加載classpath下的 log4j2.xml 文件中的配置。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="5">
<properties>
<property name="LOG_HOME">D:/logs</property>
</properties> 
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L - -- %m%n" />
</Console>
<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>
<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>
<RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log" filePattern="D:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyyMM-dd-HH-mm}-%i.log">
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n" />
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB" />
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="30" />
</RollingFile>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
    
複製代碼

3 Log4j2 異步日誌

同步日誌:

image-20210331231835943

異步日誌:

image-20210331231921090

Log4j2提供了兩種實現日誌的方式,一個是經過AsyncAppender,一個是經過AsyncLogger,分別對應前面的Appender組件和Logger組件。

tips: 配置異步日誌須要下面依賴

<!--異步日誌依賴-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency>
複製代碼

1 AsyncAppender方式

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<properties>
<property name="LOG_HOME">D:/logs</property>
</properties>
<Appenders>
<File name="file" fileName="${LOG_HOME}/myfile.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
</File>
<Async name="Async">
<AppenderRef ref="file"/>
</Async>
</Appenders>
<Loggers>
<Root level="error" <AppenderRef ref="Async"/>
</Root>
</Loggers>
</Configuration>
複製代碼

2 AsyncLogger方式

AsyncLogger纔是log4j2的重點,是官方推薦的異步方式。它可使得調用Logger.log返回的更快。包括: 全局異步和混合異步。

  • 全局異步: 全部的日誌都異步的記錄,在配置文件上不用作任何改動,只須要添加一個 log4j2.component.properties 配置;

    Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerCon
    textSelector
    複製代碼
  • 混合異步: 在應用中同時使用同步日誌和異步日誌,這使得日誌的配置方式更加靈活。

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="WARN">
    <properties>
    <property name="LOG_HOME">D:/logs</property>
    </properties>
    <Appenders>
    <File name="file" fileName="${LOG_HOME}/myfile.log">
    <PatternLayout>
    <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
    </PatternLayout>
    </File>
    <Async name="Async">
    <AppenderRef ref="file"/>
    </Async>
    </Appenders>
    <Loggers>
    <AsyncLogger name="com.cf.logging" level="trace" includeLocation="false" additivity="false">
    <AppenderRef ref="file"/>
    </AsyncLogger>
    <Root level="info" includeLocation="true">
    <AppenderRef ref="file"/>
    </Root>
    </Loggers>
    </Configuration>
    複製代碼

如上配置: com.cf.logging日誌是異步的,root日誌是同步的。

tips:

  • 1 使用異步日誌,AsyncAppender、AsyncLogger和全局日誌,不要同時出現。性能會和 AsyncAppender一致,降至最低。
  • 2 設置includeLocation=false ,打印位置信息會急劇下降異步日誌的性能,比同步日誌還要慢。

7 SpringBoot中日誌使用

springboot 默認就是使用SLF4J做爲日誌門面,logback做爲日誌實現來記錄日誌。

1 SpringBoot中日誌簡介

springboot中的日誌jar包:

<dependency>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
複製代碼

image-20210329215159972

總結:

1 springboot 底層默認使用logback做爲日誌實現

2 使用了SLF4J做爲日誌門面

3 將JUL也轉換成slf4j

4 也可使用log4j2做爲日誌門面,可是最終也是經過slf4j調用logback

2 SpringBoot中日誌使用

1 案列

@SpringBootTest
public class LoggerApplicationTests {

    //記錄器
    public static final Logger LOGGER = LoggerFactory.getLogger(LoggerApplicationTests.class);
    @Test
    public void contextLoads() {
        // 打印日誌信息
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info"); // 默認日誌級別
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }
}
//結果
/* 23:41:25.191 [main] ERROR com.cf.logger.LoggerApplicationTests - error 23:41:25.196 [main] WARN com.cf.logger.LoggerApplicationTests - warn 23:41:25.196 [main] INFO com.cf.logger.LoggerApplicationTests - info 23:41:25.196 [main] DEBUG com.cf.logger.LoggerApplicationTests - debug */
複製代碼

2 修改默認日誌配置

logging.level.com.cf.logger=trace
# 在控制檯輸出的日誌的格式 同logback
logging.pattern.console=%d{yyyy-MM-dd} [%thread] [%-5level] %logger{50} -%msg%n
 # 指定文件中日誌輸出的格式
logging.file.path=D:\\springboot.log
logging.pattern.file=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
複製代碼

3 指定配置

給類路徑下放上每一個日誌框架本身的配置文件,SpringBoot就不使用默認配置.

框架 配置文件
logback logback-spring.xml , logback.xml
Log4j2 log4j2-spring.xml , log4j2.xml
JUL logging.properties

4 使用SpringBoot解析日誌配置

logback-spring.xml,由SpringBoot解析日誌配置.

<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<springProfile name="dev">
<pattern>${pattern}</pattern>
</springProfile>
<springProfile name="pro">
<pattern>%d{yyyyMMdd:HH:mm:ss.SSS} [%thread] %-5level
%msg%n</pattern>
</springProfile>
</encoder>

複製代碼

application.properties文件

spring.profiles.active=dev
複製代碼

5 將日誌切換爲log4j2

<dependency>
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!--排除logback-->
        <exclusion>
        <artifactId>spring-boot-starter-logging</artifactId>
        <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!-- 添加log4j2 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>    
複製代碼

3 使用lombok的@Slf4j註解

導入pom.xml

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
複製代碼
package com.cf.logger;


import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class LoggerApplicationTests {
    @Test
   public void contextLoads() {
        log.info("[{}]","小明");
        log.error("[{}]","小紅");
        log.debug("[{}]","小白");
        log.warn("[{}]","小藍");
        log.trace("[{}]","小黃");

    }

}
//結果
/* 2021-03-29 21:59:52.805 INFO 9260 --- [ main] com.cf.logger.LoggerApplicationTests : [小明] 2021-03-29 21:59:52.805 ERROR 9260 --- [ main] com.cf.logger.LoggerApplicationTests : [小紅] 2021-03-29 21:59:52.805 WARN 9260 --- [ main] com.cf.logger.LoggerApplicationTests : [小藍] */
複製代碼

說明:

@Slf4j註解,被編譯後的源文件會自動加上private static final Logger log = LoggerFactory.getLogger方法.

官方地址: projectlombok.org/api/lombok/…

相關文章
相關標籤/搜索