摘要:引言用過(guò)的同學(xué)們大多都知道日志框架可以自動(dòng)按照某個(gè)時(shí)間點(diǎn)切割日志的功能。而跟日志切割相關(guān)的邏輯就在這里面。第一步判斷是否需要切割日志,需要就執(zhí)行滾動(dòng)操作。中實(shí)現(xiàn)了按照時(shí)間切割日志的策略。如果到了應(yīng)該切割日志的時(shí)間則會(huì)調(diào)用方法。
引言
用過(guò)Logback的同學(xué)們大多都知道Logback日志框架可以自動(dòng)按照某個(gè)時(shí)間點(diǎn)切割日志的功能。但了解其中工作原理的同學(xué)可能并不是很多。樓主今天就帶領(lǐng)各位了解一下其中的核心源碼。本文的示例引用了Logback 1.1.7版的源碼。
舉個(gè)實(shí)際的例子,如果希望某一個(gè)Appender按天切割日志,那么我們需要類(lèi)似如下的配置:
logs/service-log.log %d{yyyy-MM-dd HH:mm:ss} %level [%class:%line] - %m%n logs/service-log.%d{yyyy-MM-dd}.log.zip
如果需要日志切割功能,首先要選用RollingFileAppender這種Appender,之后要配置TimeBasedRollingPolicy作為該Appender的滾動(dòng)策略。
源碼解讀業(yè)務(wù)代碼在調(diào)用Logback的記錄日志的方法時(shí),Logger類(lèi)會(huì)調(diào)用ch.qos.logback.core.Appender#doAppend方法。Appender的doAppend就是Appender記錄日志功能的入口。
我們先來(lái)看一下RollingFileAppender的繼承關(guān)系
看起來(lái)貌似有些暈。沒(méi)關(guān)系,doAppend方法經(jīng)過(guò)幾層之后會(huì)調(diào)到ch.qos.logback.core.rolling.RollingFileAppender#subAppend 這個(gè)方法。而跟日志切割相關(guān)的邏輯就在這里面。因此從doAppend到subAppend之間的調(diào)用鏈路我們?cè)诖寺赃^(guò)不提,而是從subAppend這個(gè)方法切入。
/** * This method differentiates RollingFileAppender from its super class. */ @Override protected void subAppend(E event) { // The roll-over check must precede actual writing. This is the // only correct behavior for time driven triggers. // We need to synchronize on triggeringPolicy so that only one rollover // occurs at a time synchronized (triggeringPolicy) { if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) { rollover(); } } super.subAppend(event); }
第一步判斷是否需要切割日志,需要就執(zhí)行滾動(dòng)操作。第二步執(zhí)行父類(lèi)中的寫(xiě)日志操作。
TimeBasedRollingPolicy中實(shí)現(xiàn)了按照時(shí)間切割日志的策略。
ch.qos.logback.core.rolling.TimeBasedRollingPolicy#isTriggeringEvent方法
public boolean isTriggeringEvent(File activeFile, final E event) { return timeBasedFileNamingAndTriggeringPolicy.isTriggeringEvent(activeFile, event); }
這個(gè)timeBasedFileNamingAndTriggeringPolicy是DefaultTimeBasedFileNamingAndTriggeringPolicy類(lèi)的實(shí)例。
ch.qos.logback.core.rolling.DefaultTimeBasedFileNamingAndTriggeringPolicy#isTriggeringEvent方法
public boolean isTriggeringEvent(File activeFile, final E event) { long time = getCurrentTime();//獲得當(dāng)前時(shí)間 if (time >= nextCheck) {//如果當(dāng)前時(shí)間大于下一次滾動(dòng)時(shí)間點(diǎn),則執(zhí)行如下邏輯 Date dateOfElapsedPeriod = dateInCurrentPeriod;//之前時(shí)間段的開(kāi)始時(shí)間 addInfo("Elapsed period: " + dateOfElapsedPeriod); elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convert(dateOfElapsedPeriod);//算出之前一個(gè)時(shí)間段的文件名字(用于將當(dāng)前文件重命名) setDateInCurrentPeriod(time);//將當(dāng)前時(shí)間保存起來(lái),作為下次滾動(dòng)時(shí)的上一次滾動(dòng)的時(shí)間點(diǎn) computeNextCheck();//計(jì)算下次滾動(dòng)時(shí)間 return true; } else { return false; } }
ch.qos.logback.core.rolling.helper.FileNamePattern類(lèi)的convert方法根據(jù)上次滾動(dòng)執(zhí)行的開(kāi)始時(shí)間計(jì)算出了本次操作的文件名。
計(jì)算下次滾動(dòng)執(zhí)行的時(shí)間。
protected void computeNextCheck() { nextCheck = rc.getNextTriggeringDate(dateInCurrentPeriod).getTime(); }
上述方法調(diào)用了ch.qos.logback.core.rolling.helper.RollingCalendar#getNextTriggeringDate
RollingCalendar這個(gè)工具類(lèi)是用來(lái)計(jì)算每一次觸發(fā)滾動(dòng)操作的時(shí)間的。RollingCalendar的periodicityType成員變量就是滾動(dòng)時(shí)間類(lèi)型枚舉,可以是天、小時(shí)、分鐘等等時(shí)間單位。這個(gè)值并不是在logback.xml文件中配置的,而是拿到日期格式“%d{yyyy-MM-dd}”之后自動(dòng)計(jì)算出來(lái)的。計(jì)算原理就是用日期格式創(chuàng)建一個(gè)SimpleDateFormat然后按照時(shí)間單位從小到大(必須是從小到大)的順序依次試驗(yàn)。每次試驗(yàn)的方法就是用SimpleDateFormat生成兩個(gè)時(shí)間,這兩個(gè)時(shí)間相差1倍的該時(shí)間單位的時(shí)間,然后將兩個(gè)時(shí)間轉(zhuǎn)換成字符串進(jìn)行對(duì)比,如果這兩個(gè)字符串不相等就說(shuō)明該日期格式對(duì)應(yīng)的時(shí)間單位是當(dāng)前試驗(yàn)的時(shí)間單位。
看完判斷是否觸發(fā)滾動(dòng)的邏輯之后我們把視線轉(zhuǎn)回ch.qos.logback.core.rolling.RollingFileAppender#subAppend方法。如果到了應(yīng)該切割日志的時(shí)間則會(huì)調(diào)用rollover方法。
/** * Implemented by delegating most of the rollover work to a rolling policy. */ public void rollover() { lock.lock(); try { // Note: This method needs to be synchronized because it needs exclusive // access while it closes and then re-opens the target file. // // make sure to close the hereto active log file! Renaming under windows // does not work for open files. this.closeOutputStream(); attemptRollover(); attemptOpenFile(); } finally { lock.unlock(); } }
滾動(dòng)方法里面自己控制了線程同步邏輯,保證多個(gè)線程只有一個(gè)會(huì)執(zhí)行滾動(dòng)操作。attemptRollover方法調(diào)用了ch.qos.logback.core.rolling.TimeBasedRollingPolicy#rollover方法
public void rollover() throws RolloverFailure { // when rollover is called the elapsed period"s file has // been already closed. This is a working assumption of this method. String elapsedPeriodsFileName = timeBasedFileNamingAndTriggeringPolicy.getElapsedPeriodsFileName();//獲得上個(gè)周期的文件名字 String elapsedPeriodStem = FileFilterUtil.afterLastSlash(elapsedPeriodsFileName); if (compressionMode == CompressionMode.NONE) { if (getParentsRawFileProperty() != null) { renameUtil.rename(getParentsRawFileProperty(), elapsedPeriodsFileName);//將當(dāng)前文件重命名 } // else { nothing to do if CompressionMode == NONE and parentsRawFileProperty == null } } else { if (getParentsRawFileProperty() == null) { compressionFuture = compressor.asyncCompress(elapsedPeriodsFileName, elapsedPeriodsFileName, elapsedPeriodStem);//執(zhí)行異步壓縮,里面用到了java語(yǔ)言的Future } else { compressionFuture = renameRawAndAsyncCompress(elapsedPeriodsFileName, elapsedPeriodStem); } } if (archiveRemover != null) { Date now = new Date(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime()); cleanUpFuture = archiveRemover.cleanAsynchronously(now);//執(zhí)行清理,清楚過(guò)期日志 } }
其中有一些工具類(lèi)可以供我們?cè)跇I(yè)務(wù)開(kāi)發(fā)過(guò)程中使用,比如ch.qos.logback.core.rolling.helper.RenameUtil#rename可以將文件重命名,拿來(lái)用就可以不用重復(fù)造輪子了。
private void attemptOpenFile() { try { // update the currentlyActiveFile LOGBACK-64 currentlyActiveFile = new File(rollingPolicy.getActiveFileName());//創(chuàng)建一個(gè)當(dāng)前的文件,新的日志輸出到這個(gè)文件。因此日志的滾動(dòng)操作就完成了 // This will also close the file. This is OK since multiple close operations are safe. this.openFile(rollingPolicy.getActiveFileName()); } catch (IOException e) { addError("setFile(" + fileName + ", false) call failed.", e); } }結(jié)束語(yǔ)
由于篇幅關(guān)系,一些細(xì)節(jié)沒(méi)有展開(kāi)。歡迎大家拍磚。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/66542.html
摘要:防抖函數(shù)防抖和節(jié)流是一對(duì)常常被放在一起的場(chǎng)景。同時(shí),這里會(huì)設(shè)置一個(gè)定時(shí)器,在等待后會(huì)執(zhí)行,的主要作用就是觸發(fā)。最后,如果不再有函數(shù)調(diào)用,就會(huì)在定時(shí)器結(jié)束時(shí)執(zhí)行。 函數(shù)節(jié)流和去抖的出現(xiàn)場(chǎng)景,一般都伴隨著客戶(hù)端 DOM 的事件監(jiān)聽(tīng)。比如scroll resize等事件,這些事件在某些場(chǎng)景觸發(fā)非常頻繁。 比如,實(shí)現(xiàn)一個(gè)原生的拖拽功能(不能用 H5 Drag&Drop API),需要一路監(jiān)聽(tīng)...
摘要:配置本文講的文件的配置,其中主要是在介紹各類(lèi)。注意,使用必須聲明節(jié)點(diǎn)子節(jié)點(diǎn)指定命名模式注意,必須包含,為窗口索引。并且為單獨(dú)拿出來(lái)。 Logback 配置 本文講logback的xml文件的配置,其中主要是在介紹各類(lèi)appender。 logback 的簡(jiǎn)單例子 xml文件大概結(jié)構(gòu) 導(dǎo)圖鏈接showImg(https://segmentfault.com/img/bV8gtH?w=27...
摘要:定制日志文件簡(jiǎn)單的日志配置不能滿(mǎn)足實(shí)際項(xiàng)目需求,那可以通過(guò)引用定制日志文件的形式達(dá)到目的。能根據(jù)類(lèi)路徑下的類(lèi)庫(kù)和配置文件自動(dòng)配置對(duì)應(yīng)的日志框架。看到這里,相信你對(duì)的日志應(yīng)該有了一個(gè)全面的了解。 本節(jié)內(nèi)容基于 Spring Boot 2.0. 你所需具備的基礎(chǔ) 什么是 Spring Boot? Spring Boot 核心配置文件詳解 Spring Boot 開(kāi)啟的 2 種方式 Spr...
摘要:的測(cè)試完全不同級(jí)別的。記錄請(qǐng)求的級(jí)別在高于或等于其的有效級(jí)別時(shí)被稱(chēng)為被啟用,否則,稱(chēng)為被禁用。該規(guī)則是的核心。指定名稱(chēng),指定的全限定名。對(duì)記錄事件進(jìn)行格式化。查看當(dāng)前活動(dòng)文件的大小,如果超過(guò)指定大小會(huì)告知觸發(fā)當(dāng)前活動(dòng)文件滾動(dòng)。 一、logback的介紹 Logback是由log4j創(chuàng)始人設(shè)計(jì)的又一個(gè)開(kāi)源日志組件,官方網(wǎng)站: http://logback.qos.ch。 logback當(dāng)...
閱讀 1441·2021-11-17 09:33
閱讀 3030·2021-10-13 09:39
閱讀 2707·2021-10-09 10:01
閱讀 2454·2021-09-29 09:35
閱讀 3902·2021-09-26 10:01
閱讀 3524·2019-08-26 18:37
閱讀 3155·2019-08-26 13:46
閱讀 1918·2019-08-26 13:39