Java中的日志框架

Java中的日志框架

常見日志框架

JUL|Log4j1|Log4j2|Logback|JCL|Slf4j

日志框架需要解決的問題

  • 穩(wěn)定性高:不可影響主進程的正常運行且自身的日志內容準確無歧義
  • 擴展性高:對于不同的日志輸出需求,有便捷的方式進行自定義擴展
  • 開銷低:不可占用服務器的過多資源舶担,影響主進程的執(zhí)行速度
  • 延遲低:對應的日志內容輸出不可延遲太久羞迷,否則失去觀察的意義

框架對比

框架 版本 優(yōu)點 缺點 作者 備注
JUL @since JDK 1.4 JDK自帶日志工具類莱坎,無需額外依賴 功能單一 Oracle java.util.logging拇厢;提供了基礎的Handler(Appender)椭岩,F(xiàn)ormatter(Pattern)等組件化功能
Logback 2006-2018 優(yōu)化了Log4j1中的缺陷及不足竟趾;配合Slf4j使用時不需要引入適配層 重載配置文件時可能丟失日志 QOS.ch 出現(xiàn)時間介于Log4j1與Log4j2冯事,是基于Slf4j標準的原生實現(xiàn),相比其他框架不需要引入適配層
Log4j1 1999-2012 標準的日志接口;模塊化的設計理念辕录; 多線程下可能存在死鎖 Apache 2015年被Apache聲明不再維護睦霎,最后版本為2012年發(fā)布的log4j 1.2.17
Log4j2 2012-2019 更少的內存占用;更高的并發(fā)性能走诞;更完善的使用手冊 特性繁多副女,完全掌握需要一定學習成本 Apache 在1的版本上完全重寫,基于LMAX Disruptor庫使得并發(fā)性能大幅提升
JCL 2005-2014 Apache 官方項目 使用不當易存在內存泄漏 Apache Apache Commons Logging蚣旱;日志抽象接口層碑幅,最新版截止2014年;因設計理念及使用方式導致在某些情況下存在內存泄漏的問題
Slf4j 2009-2019 >=1.6.0 易用塞绿,單jar包沟涨,使用范圍廣 QOS.ch Simple Logging Facade for Java日志抽象接口層

Slf4j

  • 保證了項目內日志框架升級的便捷性,項目間日志框架的一致性
  • 利用Bridging legacy logging APIs實現(xiàn)已有JCL异吻、JUL裹赴、Log4j多項目的歸并統(tǒng)一
  • 參數(shù)化日志打印
  • 無綁定、多綁定涧黄、版本異常等可以在加載期進行檢測提示

解決的問題

without slf4j
with slf4j

調用關系鏈

slf4j application

Log4j2

性能對比

log4j2 benchmark 1
log4j2 benchmark 2

基礎概念

Log Level

OFF|FATAL|ERROR|WARN|INFO|DEBUG|TRACE|ALL

Log Event

Event Level LoggerConfig Level
TRACE DEBUG INFO WARN ERROR FATAL OFF
ALL YES YES YES YES YES YES NO
TRACE YES NO NO NO NO NO NO
DEBUG YES YES NO NO NO NO NO
INFO YES YES YES NO NO NO NO
WARN YES YES YES YES NO NO NO
ERROR YES YES YES YES YES NO NO
FATAL YES YES YES YES YES YES NO
OFF NO NO NO NO NO NO NO

Appender

真正執(zhí)行日志輸出的類篮昧,log4j2預定義了多種用途的Appender如Console Appender,F(xiàn)ile Appender笋妥,Http Appender等,其中Appender按執(zhí)行層級又可以分為二種:普通Appender與引用Appender窄潭,引用Appender即自身并不實現(xiàn)具體的輸出而是對普通Appender進行了一層包裝來實現(xiàn)異步春宣、過濾、轉發(fā)等目的

Logger

具體的日志對象嫉你,一個Logger對象可以包含[0, n)個Appender來同時輸出到不同流月帝;同時Logger對象還包含一些管理信息如Log Level及Log Filter等

結構圖解

log4j 2

常用配置項

<?xml version="1.0" encoding="UTF-8"?>;
<Configuration name="my configuration file" status="WARN" monitorInterval="30" desc="log4jdebug.log">
  <Properties>
    <Property name="name1">value</property>
    <Property name="name2" value="value2"/>
  </Properties>
  <filters>
      <MarkerFilter marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
      <MarkerFilter marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/>
  </filters>
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
                 filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <TimeBasedTriggeringPolicy />
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
    </RollingFile>
    ...
  </Appenders>
  <Loggers>
    <Logger name="name1">
      <filter  ... />
      <AppenderRef ref="name1"/>
      <AppenderRef ref="name2"/>
    </Logger>
    ...
    <Root level="level">
      <AppenderRef ref="name"/>
    </Root>
  </Loggers>
</Configuration>

Configuration

  • status:LogLevel;log4j2內部代碼日志級別幽污,調試時可設置為trace
  • monitorInterval:配置變更檢測間隔嚷辅,單位秒;注意只有當monitorInterval之后并有新的logEvent時才會真正觸發(fā)reconfiguration
  • dest:err|out|file|URL距误;log4j2內部代碼輸出流簸搞,對于不方便直接查看的環(huán)境可以導出調試信息至文件

Properties

類似POM文件中的Properties,定義通過kvp准潭,引用通過{name}趁俊;除此之外在引用時還可以通過特定的前綴來指定引用的值的格式`{prefix:name},如{base64:SGVsbG8gV29ybGQhCg==}`等價于`Hello World!`刑然,`{sys:some.property:-default_value}表示取名為some.property的系統(tǒng)參數(shù)寺擂,當不存在時使用default_value`進行替換

Filter

Log Event過濾器,每個過濾器有三種返回結果Accept|Deny|Neutral,分別表示直接接受怔软,直接拒絕垦细,向下傳遞;根據(jù)Filter的作用域又可以分為以下三種

  • 全局Filter挡逼,配置節(jié)點與Properties\Appenders\Loggers同級
  • Logger Filter括改,位于Logger中,針對某個具體的Logger進行過濾
  • Appender Filter挚瘟,位于Appender中叹谁,針對某個具體的Appender進行過濾

Appenders

Rolling File Appender
Parameter Name Type Values Default Description
append boolean true|false true 新日志附加至文件末尾或全量覆蓋
bufferedIO boolean true|false true 是否開啟文件寫入緩存
bufferSize int 8192 配合bufferedIO使用,單位字節(jié)
createOnDemand boolean true|false false 是否開啟延遲創(chuàng)建文件
filter Filter 過濾器乘盖,多個filter應使用filters標簽
fileName String 日志路徑焰檩,若不存在則自動創(chuàng)建
filePattern String 滾動文件名,支持占位符及自動壓縮
immediateFlush boolean true|false true 是否立即寫入磁盤
layout Layout %m%n 日志內容格式订框,參考Pattern Layout
name String 同配置集中析苫,Appender的name必須唯一
policy TriggeringPolicy 滾動觸發(fā)策略,決定何時進行文件滾動
policy.OnStartupTriggeringPolicy.minSize long 1 滾動文件大小最小值
policy.SizeBasedTriggeringPolicy.size String 20KB|MB|GB filePattern中必須包含%i項穿扳,否則會導致文件直接被覆蓋
policy.TimeBasedTriggeringPolicy.interval int 1 基于filePattern中的日期精度單位觸發(fā)滾動
policy.TimeBasedTriggeringPolicy.modulate boolean true|false 是否使用絕對時間
policy.TimeBasedTriggeringPolicy.maxRandomDelay int 0 觸發(fā)滾動時隨機延遲N秒衩侥,避免多觸發(fā)下造成CPU波峰
policy.CronTriggeringPolicy.schedule String cron表達式
policy.CronTriggeringPolicy.evaluateOnStartup boolean 是否啟動時候立即執(zhí)行
strategy RolloverStrategy 滾動執(zhí)行策略,決定怎么進行文件滾動
strategy.DefaultRolloverStrategy.fileIndex String min|max max 備份的文件按時間降序或升序排號矛物,默認升序即編號最大的時間最近
strategy.DefaultRolloverStrategy.min int 1 備份文件排號起點
strategy.DefaultRolloverStrategy.max int 7 備份文件排號最大值茫死,超出最大值時候將刪除時間最遠的文件
strategy.DefaultRolloverStrategy.compressionLevel int [0-9] 0 只有當filePattern配置后綴為壓縮時生效,0-不壓縮履羞,1-9表示壓縮率
strategy.DefaultRolloverStrategy.tempCompressedFilePattern String 壓縮期間使用的臨時文件名
strategy.DefaultRolloverStrategy.delete Delete 執(zhí)行滾動時自定義的刪除行為
strategy.DefaultRolloverStrategy.posixViewAttribute posixViewAttribute 執(zhí)行滾動時自定義的文件權限
ignoreExceptions boolean true|false true 是否忽略appender的內部異常
filePermissions String 創(chuàng)建文件時賦予的權限峦萎,POSIX格式
fileOwner String 創(chuàng)建文件時賦予的用戶
fileGroup String 創(chuàng)建文件時賦予的用戶組

觸發(fā)策略Policy配置時類似Filter,可以使用Policies進行多項配置忆首,只要任一項Policy滿足條件則觸發(fā)

<Policies>
  <OnStartupTriggeringPolicy />
  <SizeBasedTriggeringPolicy size="20 MB" />
  <TimeBasedTriggeringPolicy />
</Policies>
AsyncAppender
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <File name="MyFile" fileName="logs/app.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
    </File>
    <Async name="Async">
      <AppenderRef ref="MyFile"/>
    </Async>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="Async"/>
    </Root>
  </Loggers>
</Configuration>
Parameter Name Type Default Description
AppenderRef String 關聯(lián)的appender.name
blocking boolean true 內存隊列滿時是等待還是寫入errorRef
shutdownTimeout integer 0
bufferSize integer 1024 緩沖大小爱榔,單位字節(jié);
errorRef String 執(zhí)行異常時候輸出的appender.name
filter Filter 同樣可以使用filters進行多項組合
name String 唯一標識
ignoreExceptions boolean true 是否忽略內部異常
includeLocation boolean false 是否記錄caller location即調用堆棧
BlockingQueueFactory BlockingQueueFactory ArrayBlockingQueue This element overrides what type of BlockingQueue to use. Seebelow documentation for more details.
RewriteAppender

重寫log event糙及,主要用于數(shù)據(jù)過濾或脫敏

RoutingAppender

appender重定向详幽,需要注意的是routing必須定義在所有關聯(lián)appender之后

Logger

  • additivity:true|false;是否繼承父類logger浸锨,默認繼承
  • name:string唇聘;唯一標識,除root logger外都必須配置
  • level:log level揣钦;日志輸出級別雳灾,默認為error
  • appenderRef:string;關聯(lián)appender的name

合并配置項

  • 使用XInclude<xi:include href="log4j-xinclude-appenders.xml" />進行文件內合并
  • 使用log4j.configurationFile參數(shù)進行跨文件合并:file1,file2

注意事項

  • 當未提供log4j.configurationFile啟動參數(shù)時冯凹,將按內置優(yōu)先級依次查找配置文件谎亩,都未找到的情況下使用默認ConsoleAppender且Level設置為Error
  • 調試log4j2的內部日志有二種常用方式:設置配置文件的status屬性為trace炒嘲;在啟動參數(shù)中加入log4j2.debug(僅支持debug級別);更多l(xiāng)og4j2支持的啟動參數(shù)請查閱這里

FAQ

為什么我使用了日志配置文件確依然沒有日志輸出匈庭?

答:

  • 確認是否引入了slf4j的實現(xiàn)包夫凸,比如slf4j-log4j-impl;若沒有阱持,slf4j會提示無法找到對應實現(xiàn)類夭拌,若提供了多個slf4j實現(xiàn)包,則同樣會提示綁定沖突
  • 確認是否正確提供了日志配置文件衷咽;若沒有鸽扁,log4j會提示找不到配置文件并啟動默認配置集(Console + Level.Error)
  • 確認是否配置了bufferIO及緩沖區(qū);只有緩沖區(qū)滿才會提交到磁盤IO進行寫入操作
  • 確認是否有磁盤文件創(chuàng)建權限镶骗;可以使用sudo啟動或預先以運行用戶的角色建立好日志文件路徑

我添加了依賴slf4j-log4j2-impl桶现,那么我還是否需要額外引入slf4j-api?

答:不需要鼎姊,slf4j的實現(xiàn)包具體依賴項以POM文件為準


分析如下三種函數(shù)使用方式骡和,哪種最優(yōu),好在哪里相寇?

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
if(logger.isDebugEnabled()) {
  logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}
logger.debug("Entry number: {} is {}.", i, String.valueOf(entry[i]);

答:第三種慰于,減少字符串的合并操作


在log4j的參數(shù)化日志方法中,分析如下幾種情況的輸出

logger.debug("param-1: {}, param-2: {}, param-3: {}", "1", "2", "3")
logger.info("param-1: \\{}, param-2: {}, param-3: {}", "1", "2", "3")
logger.info("param-1: {}, param-2: {{}}, param-3: {}", "1", "2", "3")

答:

  • param-1: 1, param-2: 2, param-3: 3
  • param-1: {}, param-2: 1, param-3: 2
  • param-1: 1, param-2: {2}, param-3: 3

Logger對象定義為static或variable有什么區(qū)別唤衫,適用于哪些場景婆赠?JCL及Slf4j是如何解決這個問題的?

static 可能產生的混亂

答:static在同容器多應用的場景下可能存在引用沖突佳励;JCL默認使用MAP來存儲每個Logger的引用页藻,需要手動釋放可能存在使用不當導致內存泄漏;Slf4j沒有這樣的機制植兰,是否static完全交由使用者控制


Slf4j為什么沒有FATAL以及TRACE級別?

答:Slf4j的作者設計理念璃吧,認為FATAL類似ERROR楣导,TRACE類似DEBUG,存在概念上的混淆畜挨;如果確實需要標記為FATAL或TRACE可以使用Marker + Pattern來實現(xiàn)


分析以下配置文件最終生成的日志文件將會是什么樣的筒繁?

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" filePattern="logs/app-%d{yyyy-MM-dd-HH}-%i.log.gz">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <CronTriggeringPolicy schedule="0 0 * * * ?"/>
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

答:

資源鏈接

JUL - java.util.logging - ORACLE

JUL Tutorials - vogella.com

StackOverflow - Why NOT JUL?

JCL - Apache Commons Logging

Slf4j - Simple Logging Facade for Java

Slf4j - 如何正確的排除依賴包中的日志框架

Slf4j - 如何提高日志的性能

Logger - Static or Not?

Log4j 1.x to Log4j 2.x

Log4j 2 Benchmarks

Log4j 2 Pattern Layout

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市巴元,隨后出現(xiàn)的幾起案子毡咏,更是在濱河造成了極大的恐慌,老刑警劉巖逮刨,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呕缭,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機恢总,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門迎罗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人片仿,你說我怎么就攤上這事纹安。” “怎么了砂豌?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵厢岂,是天一觀的道長。 經常有香客問我阳距,道長塔粒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任娄涩,我火速辦了婚禮窗怒,結果婚禮上,老公的妹妹穿的比我還像新娘蓄拣。我一直安慰自己扬虚,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布球恤。 她就那樣靜靜地躺著辜昵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪咽斧。 梳的紋絲不亂的頭發(fā)上堪置,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音张惹,去河邊找鬼舀锨。 笑死,一個胖子當著我的面吹牛宛逗,可吹牛的內容都是我干的坎匿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼雷激,長吁一口氣:“原來是場噩夢啊……” “哼替蔬!你這毒婦竟也來了?” 一聲冷哼從身側響起屎暇,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤承桥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后根悼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凶异,經...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡蜀撑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了唠帝。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屯掖。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖襟衰,靈堂內的尸體忽然破棺而出贴铜,到底是詐尸還是另有隱情,我是刑警寧澤瀑晒,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布绍坝,位于F島的核電站,受9級特大地震影響苔悦,放射性物質發(fā)生泄漏轩褐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一玖详、第九天 我趴在偏房一處隱蔽的房頂上張望把介。 院中可真熱鬧,春花似錦蟋座、人聲如沸拗踢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巢墅。三九已至,卻和暖如春券膀,著一層夾襖步出監(jiān)牢的瞬間君纫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工芹彬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蓄髓,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓舒帮,卻偏偏與公主長得像双吆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子会前,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355