我們一起聊聊日志怎么都喜歡用SLF4J
SLF4J,即Simple Logging Facade for Java,是Java日志框架的一個(gè)抽象層。它本身并不提供日志的實(shí)現(xiàn),而是為各種日志框架(如log4j、logback、java.util.logging等) 提供統(tǒng)一的接口,使開發(fā)者可以更方便地更換日志框架而無(wú)需修改代碼。
使用示例
使用slf4j時(shí)我們需要優(yōu)先引入其依賴
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
前面說(shuō)到,Slf4j只是一個(gè)日志門面,那么真實(shí)使用時(shí)我們還需要添加一個(gè)該日志的具體的實(shí)現(xiàn),比如slf4j-simple、logback,這里選擇slf4j-simple做示例
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.36</version>
</dependency>
然后,通過(guò)以下步驟使用SLF4J進(jìn)行日志記錄:
- 導(dǎo)入SLF4J的Logger和LoggerFactory類:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
- 在類中創(chuàng)建一個(gè)Logger對(duì)象:
private static final Logger logger=LoggerFactory.getLogger(Printer.class);
- 使用Logger對(duì)象記錄日志:
logger.info("This is an info message");
logger.warn("This is an warn message");
logger.error("This is an error message",e);
這里可以通過(guò)在classpath中添加simplelogger.properties配置對(duì)日志打印級(jí)別與內(nèi)容進(jìn)行調(diào)整
# 全局日志默認(rèn)級(jí)別
org.slf4j.simpleLogger.defaultLogLevel=INFO
# 按包配置日志打印級(jí)別
org.slf4j.simpleLogger.log.com.sucl.blog.log.slf4j=WARN
# 顯示時(shí)間
org.slf4j.simpleLogger.showDateTime=true
# 時(shí)間格式
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss
# 線程名
org.slf4j.simpleLogger.showThreadName=true
# 包路徑
org.slf4j.simpleLogger.showLogName=true
# 短的包路徑
#org.slf4j.simpleLogger.showShortLogName=false
SLF4J的配置
slf4j-simple只是一個(gè)簡(jiǎn)單的實(shí)現(xiàn),其提供的功能有一定的局限性,并且無(wú)法對(duì)日志進(jìn)行持久化,一般生成環(huán)境我們可以選擇logback,只需要將上面的配置進(jìn)行調(diào)整
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
SLF4J本身不需要配置,因?yàn)樗皇且粋€(gè)抽象層。配置是針對(duì)所選的日志實(shí)現(xiàn)框架進(jìn)行的。例如,如果使用logback作為實(shí)現(xiàn)框架,那么需要?jiǎng)?chuàng)建一個(gè)logback.xml配置文件,并放置在項(xiàng)目的類路徑下。
logback.xml配置文件的示例:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Logback是一種Java日志框架,它提供了多種Appender用于將日志輸出到不同的目的地。以下是Logback中常見的幾種Appender及其說(shuō)明:
- ch.qos.logback.core.ConsoleAppender:
- 說(shuō)明:將日志輸出到控制臺(tái)。
- 用途:通常用于開發(fā)和調(diào)試階段,因?yàn)榭梢灾苯釉诳刂婆_(tái)上看到日志輸出。
- ch.qos.logback.core.FileAppender:
- 說(shuō)明:將日志輸出到指定的文件。
- 用途:適用于將日志持久化到磁盤,方便后續(xù)查看和分析。
- ch.qos.logback.core.rolling.RollingFileAppender:
- 說(shuō)明:將日志輸出到文件,當(dāng)文件達(dá)到一定大小時(shí),會(huì)自動(dòng)滾動(dòng)(即創(chuàng)建新的日志文件)。
- 用途:適用于需要長(zhǎng)時(shí)間運(yùn)行的應(yīng)用,可以避免單一日志文件過(guò)大導(dǎo)致的問題。
- ch.qos.logback.classic.db.DBAppender:
- 說(shuō)明:將日志寫入到數(shù)據(jù)庫(kù)中。
- 用途:適用于需要將日志長(zhǎng)期保存或進(jìn)行復(fù)雜查詢的場(chǎng)景。
- ch.qos.logback.classic.net.SocketAppender:
- 說(shuō)明:將日志發(fā)送到指定的socket服務(wù)器。
- 用途:適用于需要將日志發(fā)送到遠(yuǎn)程服務(wù)器或進(jìn)行集中管理的場(chǎng)景。
- ch.qos.logback.classic.net.SMTPAppender:
- 說(shuō)明:將日志以電子郵件的形式發(fā)送。
- 用途:適用于需要將重要日志或錯(cuò)誤日志及時(shí)通知給相關(guān)人員的場(chǎng)景。
- ch.qos.logback.classic.net.SyslogAppender:
- 說(shuō)明:將日志發(fā)送到syslog服務(wù)器。
- 用途:適用于需要將日志與系統(tǒng)的其他日志進(jìn)行統(tǒng)一管理的場(chǎng)景。
除了上述常見的Appender外,Logback還支持自定義Appender,可以根據(jù)具體需求擴(kuò)展日志輸出的目的地。每種Appender都有其特定的用途和適用場(chǎng)景,可以根據(jù)實(shí)際需求選擇合適的Appender進(jìn)行配置。
日志的統(tǒng)一
SLF4J可以與多種日志框架結(jié)合使用,如Logback、Log4j和JDK自帶的日志框架。只需將SLF4J的依賴和所選日志框架的依賴添加到項(xiàng)目中,并在配置文件中進(jìn)行相應(yīng)的配置即可。
- 與Log4j結(jié)合使用
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
- 與JDK自帶的日志框架結(jié)合使用
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.30</version>
</dependency>
實(shí)際項(xiàng)目中我們會(huì)引用很對(duì)外部jar,然而每個(gè)jar中使用的日志框架不可能完全相同,我們不可能為每一種日志提供專門的配置,而且日志的輸出形式也很難統(tǒng)一,所以需要一種方式將所有第三方框架 的日志信息統(tǒng)一管理起來(lái),前面說(shuō)到的日志橋接就起到了作用。
比如我們當(dāng)前系統(tǒng)使用的是slf4j+logback作為日志框架,現(xiàn)在引用了一個(gè)jar,它的日志框架選擇了log4j2,示例如下:
依賴的log4j2:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
日志配置classpath:log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="WARN" monitorInterval="30">
<appenders>
<console name="Console">
<!--輸出日志的格式-->
<PatternLayout pattern="log4j2-> [%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
</console>
</appenders>
<loggers>
<root level="debug">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
內(nèi)部會(huì)這樣使用
public class Log4j2Printer {
private static final Logger LOGGER = LogManager.getLogger(Log4j2Printer.class);
public void doPrint(String info){
LOGGER.debug("調(diào)試打印:{}", info);
LOGGER.info("打印:{}", info);
LOGGER.warn("警告打印:{}", info);
LOGGER.error("錯(cuò)誤打印:{}", info);
}
}
日志的輸出:
slf4j-> 22:21:44.212 [main] INFO c.s.b.l.slf4j.logback.LogbackPrinter - 打印:我是logback的日志
slf4j-> 22:21:44.216 [main] WARN c.s.b.l.slf4j.logback.LogbackPrinter - 警告打印:我是logback的日志
slf4j-> 22:21:44.216 [main] ERROR c.s.b.l.slf4j.logback.LogbackPrinter - 錯(cuò)誤打印:我是logback的日志
log4j2-> [22:21:44:605] [INFO] - com.sucl.blog.log.slf4j.log4j2.Log4j2Printer.doPrint(Log4j2Printer.java:17) - 打印:我是log4j2的日志
log4j2-> [22:21:44:606] [WARN] - com.sucl.blog.log.slf4j.log4j2.Log4j2Printer.doPrint(Log4j2Printer.java:18) - 警告打印:我是log4j2的日志
log4j2-> [22:21:44:608] [ERROR] - com.sucl.blog.log.slf4j.log4j2.Log4j2Printer.doPrint(Log4j2Printer.java:19) - 錯(cuò)誤打印:我是log4j2的日志
這時(shí)只需要引入一個(gè)橋架包,這樣我們只需要配置項(xiàng)目中的logback.xml,第三方的所有l(wèi)og4j2打印的日志則會(huì)按照我們的配置統(tǒng)一輸出
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
</dependency>
日志橋接允許應(yīng)用程序在不更改代碼的情況下,靈活地切換或同時(shí)使用不同的日志框架實(shí)現(xiàn)。具體來(lái)說(shuō),日志橋接包的意義體現(xiàn)在以下幾個(gè)方面:
- 接口標(biāo)準(zhǔn)化:日志橋接包如SLF4J提供了一套標(biāo)準(zhǔn)的日志API,這意味著無(wú)論底層使用哪種日志實(shí)現(xiàn)(如Logback、Log4j等),開發(fā)者都可以使用相同的API進(jìn)行日志記錄。
- 實(shí)現(xiàn)靈活切換:通過(guò)使用橋接包,項(xiàng)目可以輕松切換到不同的日志框架。例如,如果項(xiàng)目最初使用的是Log4j,但后來(lái)想要切換到Logback,只需添加相應(yīng)的橋接包依賴即可,無(wú)需修改代碼。
- 維護(hù)兼容性:對(duì)于遺留系統(tǒng)或者第三方庫(kù)中使用的特定日志框架,橋接包可以保證這些系統(tǒng)或庫(kù)的日志調(diào)用能夠被當(dāng)前項(xiàng)目中使用的日志框架正確處理。
- 減少依賴沖突:在使用多個(gè)第三方庫(kù)時(shí),這些庫(kù)可能依賴于不同的日志框架。橋接包可以解決這些庫(kù)之間的日志框架沖突問題,確保它們能夠和平共處。
- 提高可移植性:由于橋接包的存在,項(xiàng)目的日志系統(tǒng)不再是與特定日志框架強(qiáng)綁定的,這使得項(xiàng)目在不同的運(yùn)行環(huán)境或部署環(huán)境中更容易移植和配置。
- 簡(jiǎn)化依賴管理:使用橋接包可以減少項(xiàng)目中直接引入的日志框架實(shí)現(xiàn)數(shù)量,從而簡(jiǎn)化了依賴管理,避免了潛在的版本沖突和依賴混亂的問題。
框架實(shí)現(xiàn)原理
SLF4J的實(shí)現(xiàn)原理主要基于Java的接口和抽象類。它定義了一套統(tǒng)一的日志記錄接口,并提供了一個(gè)LoggerFactory類用于創(chuàng)建Logger對(duì)象。Logger對(duì)象負(fù)責(zé)實(shí)際的日志記錄操作,但具體的實(shí)現(xiàn)細(xì)節(jié)則委托給了所選的日志框架。
接口層:定義了日志記錄的接口,如Logger、LoggerFactory等。實(shí)現(xiàn)層:各種日志框架的具體實(shí)現(xiàn),如Logback、Log4j等。橋接層:將接口層與實(shí)現(xiàn)層連接起來(lái),使得應(yīng)用程序可以在不同的日志框架之間輕松切換。
日志打印中的問題
在使用SLF4J進(jìn)行日志記錄時(shí),可能會(huì)遇到一些問題,如日志不打印、日志級(jí)別不正確等。這些問題通常與所選日志框架的配置有關(guān),需要檢查配置文件和代碼中的日志記錄語(yǔ)句。
另外,還需要注意以下幾點(diǎn):
- 確保項(xiàng)目中只包含一個(gè)SLF4J的實(shí)現(xiàn)框架,避免產(chǎn)生沖突。
- 檢查日志級(jí)別設(shè)置,確保所需的日志級(jí)別沒有被禁用。
- 檢查L(zhǎng)ogger對(duì)象的創(chuàng)建方式是否正確,確保Logger對(duì)象與當(dāng)前類關(guān)聯(lián)。
- 檢查日志記錄語(yǔ)句是否正確,確保日志消息和參數(shù)都正確傳遞給了Logger對(duì)象。
- 日志級(jí)別不清晰:不同級(jí)別的日志應(yīng)該有明顯的區(qū)分,以便于查找和定位問題。
- 日志信息不足:日志應(yīng)該包含足夠的信息,以便于分析問題。
- 日志輸出格式不統(tǒng)一:為了方便查看和管理,日志的輸出格式應(yīng)該統(tǒng)一。
- 日志性能問題:在高并發(fā)場(chǎng)景下,日志的性能可能會(huì)受到影響,需要選擇合適的日志框架和配置。
總結(jié)
SLF4J是一個(gè)強(qiáng)大的日志框架,它簡(jiǎn)化了日志記錄的過(guò)程,并提供了與多種日志框架的兼容性。通過(guò)深入了解SLF4J的使用和配置, 我們可以更好地利用它進(jìn)行Java應(yīng)用的日志記錄和管理。希望本文能夠幫助讀者更好地理解和使用SLF4J日志框架。