Java日志系统概述SLF4J、log4j、JCL、Logback
?
java日志系統(tǒng)經(jīng)常遇到SLF4j,JCL,logback,log4j2等等。一些人可能要暈了怎么選擇,這里簡單說下。
發(fā)展
這些都要從Java日志框架的元老log4j說起。java1.3之前打日志都是采用System.out.println(), System.err.println()等做法。
此時log4j誕生,定義了Logger、Appender、Level等概念,至今還在使用。可以說是一下子脫離了刀耕火種時代。
畢竟還不是java的標(biāo)準(zhǔn),因此隨著IT發(fā)展,開始有多種設(shè)計和實現(xiàn)
從上圖可以看出,這個過程出了兩個統(tǒng)一的接口,并有不同實現(xiàn)
SLF4j和JCL
JCL,Jakarta Commons Logging。
SLF4j和JCL對使用者封裝了統(tǒng)一接口,進行解耦。兩者定位一樣,在不更改代碼的基礎(chǔ)上,隨意切換日志系統(tǒng)。也各自擁有忠實的原生實現(xiàn)logback和log4j2。
SLF4j
SLF4j采用靜態(tài)綁定機制
1.SLF4j為各個日志輸出系統(tǒng)提供了適配庫
2.SLF4j會加載org.slf4j.impl.StaticLoggerBinder作為輸出日志的實現(xiàn)類(老版本)。新版本通過java SPI實現(xiàn)。自然而然地就能與具體的日志輸出實現(xiàn)綁定起來。
JCL
JCL采用動態(tài)綁定機制
1.啟動時,嘗試尋找當(dāng)前factory中名叫org.apache.commons.logging.Log配置屬性的值
2.找不到,則尋找系統(tǒng)中屬性中名叫org.apache.commons.logging.Log的值
3.依舊找不到,如果應(yīng)用程序的classpath中有l(wèi)og4j,則使用相關(guān)的包裝(wrapper)類(Log4JLogger)
4.還沒找到的話,使用相關(guān)的包裝類(Jdk14Logger)
log4j2與logback
log4j2比較新,比logback晚出來幾年。也吸收了logback的有點,也有大量改進。在多線程環(huán)境下和使用異步日志時性能比較好
log4j2和logback各有優(yōu)缺點
?
橋接與適配
因為理念和實現(xiàn)的差異性,不可避免的存在一些不兼容。另外不同的java庫也依賴不同的日志輸出服務(wù),也有兼容性問題。因此兩個門面在與實現(xiàn)進行組合時就需要一些橋接器和適配器。
完美情況下,應(yīng)該是
這個是最理想的實現(xiàn)方案,比如:
dependencies {compile "org.slf4j:slf4j-api:1.7.30"compile "ch.qos.logback:logback-classic:1.2.3"compile "ch.qos.logback:logback-core:1.2.3" }然而現(xiàn)實是多樣的。因為理念、設(shè)計思想、實現(xiàn)思路等各不相同。一定會出現(xiàn)兼容性問題。
適配器
系統(tǒng)jar包之間肯定存在統(tǒng)一接口與日志輸出系統(tǒng)不兼容的
此時就需要在接口和日志輸出系統(tǒng)之間,做一層適配。
示例代碼
適配到log4j依賴:
repositories {mavenCentral()maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } }dependencies {compile "org.slf4j:slf4j-api:1.7.30"compile "org.slf4j:slf4j-log4j12:1.7.30" // 自帶了 log4jcompile "log4j:log4j:1.2.17" } import org.slf4j.Logger; import org.slf4j.LoggerFactory;public class MyMain {private static Logger logger = LoggerFactory.getLogger(MyMain.class);public static void main(String[] args) {logger.info("ka---------ka");} }log4j.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE log4j:configuration PUBLIC "-//log4j/log4j Configuration//EN" "log4j.dtd"> <log4j:configuration><!-- 日志輸出到控制臺 --><appender name="console" class="org.apache.log4j.ConsoleAppender"><!-- 日志輸出格式 --><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="[%p][%d{yyyy-MM-dd HH:mm:ss SSS}][%c]-[%m]%n"/></layout></appender><!--1. 指定logger的設(shè)置,additivity是否遵循缺省的繼承機制2. 當(dāng)additivity="false"時,root中的配置就失靈了,不遵循缺省的繼承機制3. 代碼中使用Logger.getLogger("logTest")獲得此輸出器,且不會使用根輸出器--><logger name="com.hust" additivity="false"><level value ="INFO"/><appender-ref ref="console"/></logger><!-- 根logger的設(shè)置,若代碼中未找到指定的logger,則會根據(jù)繼承機制,使用根logger--><root><appender-ref ref="console"/></root> </log4j:configuration>或者適配到log4j2:
dependencies {compile "org.slf4j:slf4j-api:1.7.30"compile "org.apache.logging.log4j:log4j-slf4j-impl:2.13.1"// 適配到 log4jcompile "org.apache.logging.log4j:log4j-core:2.13.1"compile "org.apache.logging.log4j:log4j-api:2.13.1" }此時log4j2.xml 配置反而更簡單:
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="INFO"><AppenderRef ref="Console"/></Root></Loggers> </Configuration>?
簡單來說就是把slf4j收到的日志請求轉(zhuǎn)向到合適的日志系統(tǒng)中,目前主要有
slf4j-jdk14:slf4j適配到j(luò)dk-logging
slf4j-log4j12:slf4j適配到 log4j1
log4j-slf4j-impl:slf4j 適配到 log4j2
logback-classic:slf4j 適配到 logback
slf4j-jcl:slf4j 適配到 commons-logging
?
橋接器
由于歷史遺留的jar包、日志輸出實現(xiàn)選擇等問題,肯定存在java服務(wù)直接使用日志輸出系統(tǒng)。
比如當(dāng)前系統(tǒng)要使用slf4j,但是依賴的某個jar使用了log4j。此時slf4j就收集不到這個jar的日志,怎么辦?此時就需要橋接,簡單說就是把日志重定向輸出
示例,注意這里L(fēng)og是 common-logging里的類
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;public class MyMain {// commons.logging 的類private static Log logger = LogFactory.getLog(MyMain.class);public static void main(String[] args) {logger.info("ka---------ka");} }改造:
dependencies {compile "commons-logging:commons-logging:1.2"compile "org.slf4j:jcl-over-slf4j:1.7.30"// commons-logging到slf4j的橋梁compile "org.slf4j:slf4j-api:1.7.30" // slf4j 可以直接輸出到logbackcompile "ch.qos.logback:logback-classic:1.2.3"compile "ch.qos.logback:logback-core:1.2.3" }橋接器自動把已經(jīng)存在的日志輸出系統(tǒng)實現(xiàn)類似屏蔽,對此java服務(wù)是無感知的.
同樣的現(xiàn)有l(wèi)og4j的也可以無感知的橋接到slf4j,然后轉(zhuǎn)到日志輸出系統(tǒng)
import org.apache.log4j.Logger;public class MyMain {// apache 的 log4j 包的類private static Logger logger = Logger.getLogger(MyMain.class);public static void main(String[] args) {logger.info("ka---------ka");} }對應(yīng)依賴
dependencies {compile "log4j:log4j:1.2.17"compile "org.slf4j:log4j-over-slf4j:1.7.30" // log4j1到slf4j的橋梁compile "org.slf4j:slf4j-api:1.7.30"compile "ch.qos.logback:logback-classic:1.2.3"compile "ch.qos.logback:logback-core:1.2.3" }對于jul來說也是一樣,稍微不同的是要提前注冊一個類。另外日志 兩邊都輸出
import org.slf4j.bridge.SLF4JBridgeHandler; import java.util.logging.Logger;public class MyMain {static {// 在使用之前,必須提前注冊這個處理器,SLF4JBridgeHandler.install();// 該類集成了jdk-logging里面的java.util.logging.Handler,這個是jdk-logging處理日志中的一個處理器}// jdk 自帶的 Logger 類private static Logger logger = Logger.getLogger(MyMain.class.getName());public static void main(String[] args) {logger.info("ka---------ka");} }依賴如下:
dependencies {compile "org.slf4j:jul-to-slf4j:1.7.30" // jdk-logging到slf4j的橋梁compile "org.slf4j:slf4j-api:1.7.30"compile "ch.qos.logback:logback-classic:1.2.3"compile "ch.qos.logback:logback-core:1.2.3" }?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的Java日志系统概述SLF4J、log4j、JCL、Logback的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: javaweb多媒体素材管理系统
- 下一篇: 二值图像--基础概念