導航:首頁 > 文件教程 > log4j日誌文件丟失

log4j日誌文件丟失

發布時間:2025-03-20 17:22:41

『壹』 如何使用log4j,讓生成的日誌文件以當天日期為名稱

在使用log4j生成日誌文件時,若希望文件名依據當前日期進行命名,需在初始化日誌配置後,添加相應代碼。以下為示例代碼:

首先,定義Appender對象,獲取根日誌記錄器的Appender,並確保其類型為FileAppender。代碼如下:

java代碼

Appender appender = LogManager.getLoggerRepository().getRootLogger().getAppender("A2");

if(appender instanceof FileAppender) {

FileAppender fileAppender = (FileAppender) appender;

String currentDateTime = new SimpleDateFormat("yyyy-MM-dd").format(new Date());

fileAppender.setFile("logs/" + currentDateTime + ".log");

}

這段代碼的作用是在FileAppender實例化時,動態設置文件名為當前日期。

需要注意的是,這僅適用於已存在且已配置好的FileAppender。若需要為其他Appender設置日期作為文件名,需按照上述方式修改相應代碼。

為了確保代碼能夠正確運行,還需確保引入了相應的log4j依賴,並在項目配置文件中正確配置了相關Appender的信息。

通過這種方式,我們可以確保生成的日誌文件按照日期進行分類,便於後續的管理和查詢。

此外,可以根據需要調整日期格式,以滿足不同的需求。例如,使用"yyyyMMdd"格式,可以得到如"20231001.log"這樣的文件名。

在實際應用中,這種方式對於日誌管理非常重要,可以有效避免日誌文件名沖突,提高日誌文件的可讀性和可維護性。

總結來說,通過在初始化日誌配置後加入上述代碼,我們能夠輕松實現日誌文件名按日期命名的功能,從而更好地管理日誌文件。

『貳』 大廠都是如何解決Java日誌級別,重復記錄、丟日誌問題

1SLF4J日誌行業的現狀

框架繁不同類庫可能使用不同日誌框架,兼容難,無法接入統一日誌,讓運維很頭疼!

配置復雜由於配置文件一般是xml文件,內容繁雜!很多人喜歡從其他項目或網上閉眼!

隨意度高因為不會直接導致代碼bug,測試人員也難發現問題,開發就沒仔細考慮日誌內容獲取的性能開銷,隨意選用日誌級別!

Logback、Log4j、Log4j2、commons-logging及java.util.logging等,都是Java體系的日誌框架。不同的類庫,還可能選擇使用不同的日誌框架,導致日誌統一管理困難。

SLF4J(SimpleLoggingFacadeForJava)就為解決該問題而生

提供統一的日誌門面API圖中紫色部分,實現中立的日誌記錄API

橋接功能藍色部分,把各種日誌框架API橋接到SLF4JAPI。這樣即使你的程序使用了各種日誌API記錄日誌,最終都可橋接到SLF4J門面API

適配功能紅色部分,綁定SLF4JAPI和實際的日誌框架(灰色部分)

SLF4J只是日誌標准,還是需要實際日誌框架。日誌框架本身未實現SLF4JAPI,所以需要有個前置轉換。Logback本身就按SLF4JAPI標准實現,所以無需綁定模塊做轉換。

雖然可用log4j-over-slf4j實現Log4j橋接到SLF4J,也可使用slf4j-log4j12實現SLF4J適配到Log4j,也把它們畫到了一列,但是它不能同時使用它們,否則就會產生死循環。jcl和jul同理。

雖然圖中有4個灰色的日誌實現框架,但業務開發使用最多的還是Logback和Log4j,都是同一人開發的。Logback可認為是Log4j改進版,更推薦使用,已是社會主流。

SpringBoot的日誌框架也是Logback。那為什麼我們沒有手動引入Logback包,就可直接使用Logback?

spring-boot-starter模塊依賴spring-boot-starter-logging模塊,而spring-boot-starter-logging自動引入logback-classic(包含SLF4J和Logback日誌框架)和SLF4J的一些適配器。

2非同步日誌就肯定能提高性能?

如何避免日誌記錄成為系統性能瓶頸呢?這關繫到磁碟(比如機械磁碟)IO性能較差、日誌量又很大的情況下,如何記錄日誌。

2.1案例

定義如下的日誌配置,一共有兩個Appender:

FILE是一個FileAppender,用於記錄所有的日誌

CONSOLE是一個ConsoleAppender,用於記錄帶有time標記的日誌

把大量日誌輸出到文件中,日誌文件會非常大,若性能測試結果也混在其中,就很難找到那條日誌了。所以,這里使用EvaluatorFilter對日誌按照標記進行過濾,並將過濾出的日誌單獨輸出到控制台。該案例中給輸出測試結果的那條日誌上做了time標記。

配合使用標記和EvaluatorFilter,可實現日誌的按標簽過濾。

測試代碼:實現記錄指定次數的大日誌,每條日誌包含1MB位元組的模擬數據,最後記錄一條以time為標記的方法執行耗時日誌:

執行程序後發現,記錄1000次日誌和10000次日誌的調用耗時,分別是5.1s和39s

對只記錄文件日誌的代碼,這耗時過長了。

2.2源碼解析

FileAppender繼承自OutputStreamAppender

在追加日誌時,是直接把日誌寫入OutputStream中,屬同步記錄日誌

所以日誌大量寫入才會曠日持久。如何才能實現大量日誌寫入時,不會過多影響業務邏輯執行耗時而影響吞吐量呢?

2.3AsyncAppender

使用Logback的AsyncAppender,即可實現非同步日誌記錄。

AsyncAppender類似裝飾模式,在不改變類原有基本功能情況下,為其增添新功能。這便可把AsyncAppender附加在其他Appender,將其變為非同步。

定義一個非同步AppenderASYNCFILE,包裝之前的同步文件日誌記錄的FileAppender,即可實現非同步記錄日誌到文件

記錄1000次日誌和10000次日誌的調用耗時,分別是537ms和1019ms

非同步日誌真的如此高性能?並不,因為它並沒有記錄下所有日誌。

3AsyncAppender非同步日誌的天坑

記錄非同步日誌撐爆內存

記錄非同步日誌出現日誌丟失

記錄非同步日誌出現阻塞。

3.1案例

模擬個慢日誌記錄場景:首先,自定義一個繼承自ConsoleAppender的MySlowAppender,作為記錄到控制台的輸出器,寫入日誌時睡1s。

配置文件中使用AsyncAppender,將MySlowAppender包裝為非同步日誌記錄

測試代碼

耗時很短但出現日誌丟失:要記錄1000條日誌,最終控制台只能搜索到215條日誌,而且日誌行號變問號。

原因分析AsyncAppender提供了一些配置參數,而當前沒用對。

源碼解析

includeCallerData默認false:方法行號、方法名等信息不顯示

queueSize控制阻塞隊列大小,使用的ArrayBlockingQueue阻塞隊列,默認容量256:內存中最多保存256條日誌

discardingThreshold丟棄日誌的閾值,為防止隊列滿後發生阻塞。默認隊列剩餘容量<隊列長度的20%,就會丟棄TRACE、DEBUG和INFO級日誌

neverBlock控制隊列滿時,加入的數據是否直接丟棄,不會阻塞等待,默認是false

//阻塞隊列:實現非同步日誌的核心BlockingQueueblockingQueue;//默認隊列大小publicstaticfinalintDEFAULT_QUEUE_SIZE=256;intqueueSize=DEFAULT_QUEUE_SIZE;staticfinalintUNDEFINED=-1;intdiscardingThreshold=UNDEFINED;//當隊列滿時:加入數據時是否直接丟棄,不會阻塞等待booleanneverBlock=false;

@Overridepublicvoidstart(){...blockingQueue=newArrayBlockingQueue(queueSize);if(discardingThreshold==UNDEFINED)//默認丟棄閾值是隊列剩餘量低於隊列長度的20%,參見方法discardingThreshold=queueSize/5;...}

@Overrideprotectedvoidappend(EeventObject){if(()&&isDiscardable(eventObject)){//判斷是否可以丟數據return;}preprocess(eventObject);put(eventObject);}

privateboolean(){return(blockingQueue.remainingCapacity()<discardingThreshold);}

privatevoidput(EeventObject){if(neverBlock){//根據neverBlock決定使用不阻塞的offer還是阻塞的put方法blockingQueue.offer(eventObject);}else{putUninterruptibly(eventObject);}}//以阻塞方式添加數據到隊列privatevoidputUninterruptibly(EeventObject){booleaninterrupted=false;try{while(true){try{blockingQueue.put(eventObject);break;}catch(InterruptedExceptione){interrupted=true;}}}finally{if(interrupted){Thread.currentThread().interrupt();}}}}

隊列滿時:offer不阻塞,而put會阻塞

neverBlock為true時,使用offer

<ILoggingEvent>{//是否收集調用方數據booleanincludeCallerData=false;protectedbooleanisDiscardable(ILoggingEventevent){Levellevel=event.getLevel();//丟棄≤INFO級日誌returnlevel.toInt()<=Level.INFO_INT;}protectedvoidpreprocess(ILoggingEventeventObject){eventObject.prepareForDeferredProcessing();if(includeCallerData)eventObject.getCallerData();}}publicclassAsyncAppenderBase<E><E>implementsAppenderAttachable<E>{

默認隊列大小256,達到80%後開始丟棄<=INFO級日誌後,即可理解日誌中為什麼只有兩百多條INFO日誌了。

queueSize過大

可能導致OOM

queueSize較小

默認值256就已經算很小了,且discardingThreshold設置為大於0(或為默認值),隊列剩餘容量少於discardingThreshold的配置就會丟棄<=INFO日誌。這里的坑點有兩個:

因為discardingThreshold,所以設置queueSize時容易踩坑。比如本案例最大日誌並發1000,即便置queueSize為1000,同樣會導致日誌丟失

discardingThreshold參數容易有歧義,它不是百分比,而是日誌條數。對於總容量10000隊列,若希望隊列剩餘容量少於1000時丟棄,需配置為1000

neverBlock默認false

意味總可能會出現阻塞。

若discardingThreshold=0,那麼隊列滿時再有日誌寫入就會阻塞

若discardingThreshold!=0,也只丟棄≤INFO級日誌,出現大量錯誤日誌時,還是會阻塞

queueSize、discardingThreshold和neverBlock三參密不可分,務必按業務需求設置:

若優先絕對性能,設置neverBlock=true,永不阻塞

若優先絕不丟數據,設置discardingThreshold=0,即使≤INFO級日誌也不會丟。但最好把queueSize設置大一點,畢竟默認的queueSize顯然太小,太容易阻塞。

若兼顧,可丟棄不重要日誌,把queueSize設置大點,再設置合理的discardingThreshold

以上日誌配置最常見兩個誤區

再看日誌記錄本身的誤區。

4如何選擇日誌級別?

使用{}佔位符,就不用判斷loglevel了嗎?

據不知名網友說道:SLF4J的{}佔位符語法,到真正記錄日誌時才會獲取實際參數,因此解決了日誌數據獲取的性能問題。是真的嗎?

驗證代碼:返回結果耗時1s

若記錄DEBUG日誌,並設置只記錄>=INFO級日誌,程序是否也會耗時1s?三種方法測試:

拼接字元串方式記錄slowString

使用佔位符方式記錄slowString

先判斷日誌級別是否啟用DEBUG。

前倆方式都調用slowString,所以都耗時1s。且方式二就是使用佔位符記錄slowString,這種方式雖允許傳Object,不顯式拼接String,但也只是延遲(若日誌不記錄那就是省去)日誌參數對象.toString()和字元串拼接的耗時。

本案例除非事先判斷日誌級別,否則必調用slowString。所以使用{}佔位符不能通過延遲參數值獲取,來解決日誌數據獲取的性能問題。

除事先判斷日誌級別,還可通過lambda表達式延遲參數內容獲取。但SLF4J的API還不支持lambda,因此需使用Log4j2日誌API,把Lombok的@Slf4j註解替換為@Log4j2註解,即可提供lambda表達式參數的方法:

這樣調用debug,簽名Supplier<?>,參數就會延遲到真正需要記錄日誌時再獲取:

所以debug4並不會調用slowString方法

只是換成Log4j2API,真正的日誌記錄還是走的Logback,這就是SLF4J適配的好處。

總結

SLF4J統一了Java日誌框架。在使用SLF4J時,要理清楚其橋接API和綁定。若程序啟動時出現SLF4J錯誤提示,可能是配置問題,可使用Maven的dependency:tree命令梳理依賴關系。

非同步日誌解決性能問題,是用空間換時間。但空間畢竟有限,當空間滿,要考慮阻塞等待or丟棄日誌。若更希望不丟棄重要日誌,那麼選擇阻塞等待;如果更希望程序不要因為日誌記錄而阻塞,那麼就需要丟棄日誌。

日誌框架提供的參數化記錄方式不能完全取代日誌級別的判斷。若日誌量很大,獲取日誌參數代價也很大,就要判斷日誌級別,避免不記錄日誌也要耗時獲取日誌參數!

閱讀全文

與log4j日誌文件丟失相關的資料

熱點內容
銀行的app信用貸怎麼辦 瀏覽:867
如何刪除自動連接的無線網路 瀏覽:316
iphone4升級到ios7太卡了 瀏覽:674
線上銷售成本數據來自哪裡 瀏覽:284
昂科威APP在什麼地方 瀏覽:259
文件為什麼不見了呢 瀏覽:883
什麼情況下聲音文件最大 瀏覽:202
兒童編程培訓班哪個最好 瀏覽:252
電腦投影儀如何播放word文件 瀏覽:614
win10電腦怎麼強制關機不了 瀏覽:724
linuxh3cinode 瀏覽:480
王者榮耀數據建模損壞怎麼辦 瀏覽:333
諾言app怎麼群發 瀏覽:372
win10安裝protue8 瀏覽:137
微信沒綁卡搶不了紅包嗎 瀏覽:49
qq傳輸文件原理 瀏覽:117
ipad怎樣創建蘋果id賬號和密碼 瀏覽:469
手機app怎麼裝到u盤啟動 瀏覽:230
u8數據怎麼弄到釘釘上 瀏覽:556
做win10系統u盤安裝系統教程視頻教程 瀏覽:34

友情鏈接