Java 日志管理的黃金組合: SLF4J+Logback
Log4J、Log4J2和LogBack的歷史故事
使用過Log4J和LogBack的同學(xué)肯定能發(fā)現(xiàn),這兩個(gè)框架的設(shè)計(jì)理念極為相似,使用方法也如出一轍。其實(shí)這個(gè)兩個(gè)框架的作者都是一個(gè)人,Ceki Gülcü,俄羅斯程序員。
Log4J 最初是基于Java開發(fā)的日志框架,發(fā)展一段時(shí)間后,作者Ceki Gülcü將Log4j捐獻(xiàn)給了Apache軟件基金會(huì),使之成為了Apache日志服務(wù)的一個(gè)子項(xiàng)目。 又由于Log4J出色的表現(xiàn),后續(xù)又被孵化出了支持C, C++, C#, Perl, Python, Ruby等語(yǔ)言的子框架。
然而,偉大的程序員好像都比較有個(gè)性。Ceki Gülcü由于不滿Apache對(duì)Log4J的管理,決定不再參加Log4J的開發(fā)維護(hù)?!俺鲎摺焙蟮腃eki Gülcü另起爐灶,開發(fā)出了LogBack這個(gè)框架(SLF4J是和LogBack一起開發(fā)出來的)。LogBack改進(jìn)了很多Log4J的缺點(diǎn),在性能上有了很大的提升,同時(shí)使用方式幾乎和Log4J一樣,許多用戶開始慢慢開始使用LogBack。
由于受到LogBack的沖擊,Log4J開始式微。終于,2015年9月,Apache軟件基金業(yè)宣布,Log4j不在維護(hù),建議所有相關(guān)項(xiàng)目升級(jí)到Log4j2。Log4J2是Apache開發(fā)的一個(gè)新的日志框架,改進(jìn)了很多Log4J的缺點(diǎn),同時(shí)也借鑒了LogBack,號(hào)稱在性能上也是完勝LogBack。性能這塊后面我會(huì)仔細(xì)分析。
那slf4j和這些有什么關(guān)系?
SLF4J的全稱是Simple Logging Facade for Java,slf4j是門面模式的典型應(yīng)用。
回答這個(gè)問題之前,我們先看看如果需要用上面幾個(gè)日志框架來打印日志,一般怎么做,具體代碼如下:
// 使用log4j,需要log4j.jar 
import org.apache.log4j.Logger; 
Logger logger_log4j = Logger.getLogger(Test.class); 
logger_log4j.info("Hello World!"); 
// 使用log4j2,需要log4j-api.jar、log4j-core.jar 
import org.apache.logging.log4j.LogManager; 
import org.apache.logging.log4j.Logger; 
Logger logger_log4j2 = LogManager.getLogger(Test.class); 
logger_log4j2.info("Hello World!"); 
// logback,需要logback-classic.jar、logback-core.jar 
import ch.qos.logback.classic.Logger; 
import ch.qos.logback.classic.LoggerContext; 
Logger logger_logback = new LoggerContext().getLogger(Test.class); logger_logback.info("Hello World!");從上面不難看出,使用不同的日志框架,就要引入不同的jar包,使用不同的代碼獲取Logger。如果項(xiàng)目升級(jí)需要更換不同的框架,那么就需要修改所有的地方來獲取新的Logger,這將會(huì)產(chǎn)生巨大的工作量。
基于此,我們需要一種接口來將不同的日志框架的使用統(tǒng)一起來,這也是為什么要使用slf4j的原因。
SLF4J,即簡(jiǎn)單日志門面(Simple Logging Facade for Java),不是具體的日志解決方案,它只服務(wù)于各種各樣的日志系統(tǒng)。按照官方的說法,SLF4J是一個(gè)用于日志系統(tǒng)的簡(jiǎn)單Facade,允許最終用戶在部署其應(yīng)用時(shí)使用其所希望的日志系統(tǒng)。
注意:類似的日志門面還有Jakarta Common logging(JCL),主要區(qū)別在于,SLF4J是一個(gè)比較新的日志框架,它更加靈活,性能更好,支持更多的日志實(shí)現(xiàn),而且JCL基于classLoader在運(yùn)行時(shí)動(dòng)態(tài)加載日志框架,可能會(huì)產(chǎn)生很多意想不到的安全問題。
通過上面的介紹,我們可以知道JCL和SLF4J都是日志門面(Facade),而Log4J、Log4J2和LogBack都是子系統(tǒng)角色(SunSystem),也就是具體的日志實(shí)現(xiàn)框架。他們的關(guān)系如下,JUL是JDK本身提供的一種實(shí)現(xiàn)。
圖片
SLF4J 的核心價(jià)值在于它提供了解耦設(shè)計(jì):應(yīng)用程序代碼只依賴 slf4j-api,而具體日志實(shí)現(xiàn)(如 Logback、Log4j2)可以在部署時(shí)動(dòng)態(tài)綁定。這種架構(gòu)使得項(xiàng)目升級(jí)或更換日志框架變得非常簡(jiǎn)單,無需修改業(yè)務(wù)代碼中的日志記錄語(yǔ)句。
slf4j怎么和日志框架結(jié)合使用?
使用slf4j后,當(dāng)我們?cè)诖蛴∪罩緯r(shí),就可以使用下面的方式:
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
Logger logger = LoggerFactory.getLogger(Test.class); 
logger.info("Hello World!")這又引入了另外一個(gè)問題,slf4j如何決定使用哪個(gè)框架日志呢,并且引入哪些jar包呢?官方為我們準(zhǔn)備了組合依賴:
- slf4j + logback: slf4j-api.jar + logback-classic.jar + logback-core.jar
 - slf4j + log4j: slf4j-api.jar + slf4j-log412.jar + log4j.jar
 - slf4j + jul: slf4j-api.jar + slf4j-jdk14.jar
 - 也可以只用slf4j無日志實(shí)現(xiàn):slf4j-api.jar + slf4j-nop.jar
 
SLF4J 的基本使用
在代碼中使用 SLF4J 非常簡(jiǎn)單,首先需要通過 Maven 添加依賴:
<!-- Maven 依賴配置 -->
<dependencies>
    <!-- SLF4J API -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.9</version>
    </dependency>
    <!-- Logback Classic 實(shí)現(xiàn) -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.4.11</version>
    </dependency>
</dependencies>在代碼中獲取 Logger 并記錄日志:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ExampleService {
    // 獲取Logger實(shí)例
    private static final Logger logger = LoggerFactory.getLogger(ExampleService.class);
    
    public void processUser(String userId) {
        logger.debug("Processing user: {}", userId); // 使用占位符,避免字符串拼接
        logger.info("User processing started");
        
        try {
            // 業(yè)務(wù)邏輯
            logger.info("User {} processed successfully", userId);
        } catch (Exception e) {
            logger.error("Failed to process user: " + userId, e); // 記錄異常信息
        }
    }
}SLF4J 的 參數(shù)化日志消息(使用 {}占位符)是其一個(gè)重要特性,它不僅有更好的可讀性,還能提升性能——當(dāng)日志級(jí)別高于當(dāng)前配置時(shí)(如配置為 INFO 級(jí)別時(shí)調(diào)用 debug 語(yǔ)句),不會(huì)執(zhí)行字符串拼接操作。
Logback 架構(gòu)與核心組件
Logback 是 SLF4J 的原生實(shí)現(xiàn)框架,由三個(gè)相互協(xié)作的模塊組成,每個(gè)模塊都有獨(dú)特的功能定位。
Logback 的模塊化設(shè)計(jì)
模塊  | 說明  | 
  | 核心模塊,提供基礎(chǔ)日志服務(wù),其他兩個(gè)模塊都依賴它  | 
  | 實(shí)現(xiàn) SLF4J API,完全兼容 SLF4J 接口,同時(shí)兼容 Log4j  | 
  | 與 Servlet 容器集成,用于 HTTP 訪問日志記錄  | 
Logback 相比 Log4j 有顯著性能提升,特別是在異步日志記錄方面,減少了線程阻塞和上下文切換開銷。它還支持自動(dòng)重載配置,可以在不重啟應(yīng)用的情況下修改日志配置。
Logback 核心概念
Logback 架構(gòu)基于三個(gè)核心概念:Logger、Appender 和 Layout/Encoder。
Logger(日志記錄器):
- 采用層次化命名(如 
com.example.service.UserService) - 具有繼承性:子 Logger 繼承父 Logger 的 Appender 和 Level
 - 通過 
LoggerFactory.getLogger()獲取實(shí)例 
Appender(輸出目的地):
Appender 負(fù)責(zé)將日志事件發(fā)送到不同目標(biāo),Logback 支持多種 Appender:
Appender 類型  | 說明  | 
  | 輸出到控制臺(tái)  | 
  | 輸出到文件  | 
  | 滾動(dòng)文件(按大小/時(shí)間)  | 
  | 發(fā)送到遠(yuǎn)程服務(wù)器  | 
  | 郵件告警  | 
  | 發(fā)送到 Kafka(需擴(kuò)展)  | 
Layout/Encoder(格式化器):
定義日志輸出格式,常用占位符包括:
%d{yyyy-MM-dd HH:mm:ss.SSS}:時(shí)間戳%level:日志級(jí)別%thread:線程名%logger{36}:Logger 名(縮寫)%msg:日志消息%n:換行符
Logback 配置詳解與案例
Logback 支持 XML 和 Groovy 兩種配置格式,其中 XML 是最常用的方式。下面通過實(shí)際案例詳細(xì)講解 Logback 的配置。
基礎(chǔ)配置結(jié)構(gòu)
Logback 配置文件通常命名為 logback.xml或 logback-spring.xml(Spring 環(huán)境),放置在 src/main/resources/目錄下。
<?xml versinotallow="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
    
    <!-- 定義變量 -->
    <property name="LOG_HOME" value="./logs"/>
    <property name="APP_NAME" value="myapp"/>
    
    <!-- 開發(fā)環(huán)境開關(guān) -->
    <springProfile name="dev">
        <property name="LOG_LEVEL" value="DEBUG"/>
    </springProfile>
    <springProfile name="prod">
        <property name="LOG_LEVEL" value="INFO"/>
    </springProfile>
    
    <!-- 控制臺(tái)輸出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- 更多Appender配置 -->
    
    <!-- 根日志器 -->
    <root level="${LOG_LEVEL:-INFO}">
        <appender-ref ref="CONSOLE"/>
    </root>
    
    <!-- 特定包的日志級(jí)別 -->
    <logger name="com.example.service" level="DEBUG"/>
</configuration>Console Appender 配置
Console Appender 用于將日志輸出到控制臺(tái),是開發(fā)環(huán)境中最常用的 Appender:
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
    <!-- 過濾器:只輸出INFO及以上級(jí)別 -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>INFO</level>
    </filter>
</appender>File Appender 與滾動(dòng)策略
生產(chǎn)環(huán)境中通常需要將日志輸出到文件,并使用滾動(dòng)策略防止文件過大:
<!-- 滾動(dòng)文件輸出(按天) -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_HOME}/${APP_NAME}.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- 每天生成一個(gè)文件 -->
        <fileNamePattern>${LOG_HOME}/archive/%d{yyyy-MM-dd}/${APP_NAME}.%i.log.gz</fileNamePattern>
        <!-- 保留30天 -->
        <maxHistory>30</maxHistory>
        <!-- 單個(gè)文件最大100MB -->
        <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>100MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>日志級(jí)別配置
Logback 支持多個(gè)日志級(jí)別,合理配置級(jí)別對(duì)系統(tǒng)性能和可觀測(cè)性至關(guān)重要。
<!-- 根日志器設(shè)置 -->
<root level="${LOG_LEVEL:-INFO}">
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="FILE"/>
</root>
<!-- 特定包/類級(jí)別設(shè)置 -->
<!-- 特定日志器:將 com.example.service 包的日志級(jí)別設(shè)為 DEBUG,且日志僅輸出到文件,不傳遞給根日志器(避免控制臺(tái)重復(fù)輸出) -->
<logger name="com.example.service" level="DEBUG" additivity="false">
    <appender-ref ref="FILE" />
</logger>
<!-- 特定日志器:將 org.springframework 包的日志級(jí)別設(shè)為 WARN,減少不必要的日志輸出 -->
<logger name="org.springframework" level="WARN" />在這個(gè)配置中:
- 絕大多數(shù)日志遵循根的 
INFO級(jí)別設(shè)置。 - 唯獨(dú) 
com.example.service包下的日志可以輸出DEBUG級(jí)別及以上的內(nèi)容,并且這些調(diào)試信息只寫入文件,不會(huì)出現(xiàn)在控制臺(tái)(因?yàn)?nbsp;additivity="false")。 - 所有來自 
org.springframework包的日志,只有WARN和ERROR級(jí)別才會(huì)被記錄。 
根日志器 (<root>) 和特定包/類日志器 (<logger>) 的設(shè)置是日志配置的兩個(gè)核心層面,主要在于作用和范圍的區(qū)別:
特性  | 根日志器 (  | 特定包/類日志器 (  | 
作用范圍  | 全局默認(rèn) 。影響所有未被特定   | 局部特定 。僅影響通過   | 
配置目的  | 設(shè)置應(yīng)用程序的基礎(chǔ)日志級(jí)別和輸出策略。  | 為特定模塊提供更精細(xì)的日志控制(如更詳細(xì)或更嚴(yán)格的級(jí)別)。  | 
繼承性  | 是所有日志器層次的根節(jié)點(diǎn),其他日志器默認(rèn)繼承其配置。  | 從其父日志器(可能是根或其他上層日志器)繼承未被自身覆蓋的設(shè)置。  | 
常用級(jí)別  | 生產(chǎn)環(huán)境常設(shè)為   | 根據(jù)需求靈活設(shè)置,如將關(guān)注模塊設(shè)為   | 
Logback 支持的日志級(jí)別從低到高依次為:
TRACE:最細(xì)粒度的信息,通常只在開發(fā)過程中使用DEBUG:logger.debug信息INFO:logger.info 信息WARN:logger.warn 信息ERROR:logger.error 信息
高級(jí)特性與性能優(yōu)化
Logback 提供了多種高級(jí)功能,可以滿足復(fù)雜場(chǎng)景下的日志需求。
MDC(Mapped Diagnostic Context)
MDC 用于在日志中添加上下文信息(如請(qǐng)求 ID、用戶 ID),非常適合分布式系統(tǒng)跟蹤:
// 在代碼中使用MDC
import org.slf4j.MDC;
public class UserService {
    public void processUserRequest(String userId, String requestId) {
        // 將上下文信息放入MDC
        MDC.put("userId", userId);
        MDC.put("requestId", requestId);
        
        logger.info("Processing user request"); // 自動(dòng)包含MDC信息
        
        // 業(yè)務(wù)處理...
        
        // 清理MDC
        MDC.clear();
    }
}在配置中使用 MDC:
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{userId}] [%X{requestId}] %-5level %logger{36} - %msg%n</pattern>異步日志提升性能
對(duì)于生產(chǎn)環(huán)境,特別是高并發(fā)場(chǎng)景,使用異步日志可以顯著提升性能。
<!-- 異步日志(提升性能) -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="FILE"/>
    <!-- 隊(duì)列大小 -->
    <queueSize>256</queueSize>
    <!-- 丟棄級(jí)別低于ERROR的日志(可選) -->
    <discardingThreshold>0</discardingThreshold>
</appender>條件化配置
Logback 支持根據(jù)不同的環(huán)境(如開發(fā)、測(cè)試、生產(chǎn))使用不同的配置。
<!-- 開發(fā)環(huán)境配置 -->
<springProfile name="dev">
    <root level="DEBUG">
        <appender-ref ref="CONSOLE"/>
    </root>
</springProfile>
<!-- 生產(chǎn)環(huán)境配置 -->
<springProfile name="prod">
    <root level="INFO">
        <appender-ref ref="ASYNC_FILE"/>
    </root>
</springProfile>自定義過濾器
Logback 允許創(chuàng)建自定義過濾器來實(shí)現(xiàn)復(fù)雜的日志過濾邏輯。
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 只記錄ERROR級(jí)別日志 -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>ERROR</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>














 
 
 













 
 
 
 