logback的FileAppender文件追加模式和沖突檢測(cè)解讀
序
本文主要研究一下logback的FileAppender
FileAppender
ch/qos/logback/core/FileAppender.java
public class FileAppender<E> extends OutputStreamAppender<E> { public static final long DEFAULT_BUFFER_SIZE = 8192; static protected String COLLISION_WITH_EARLIER_APPENDER_URL = CODES_URL + "#earlier_fa_collision"; /** * Append to or truncate the file? The default value for this variable is * <code>true</code>, meaning that by default a <code>FileAppender</code> will * append to an existing file and not truncate it. */ protected boolean append = true; /** * The name of the active log file. */ protected String fileName = null; private boolean prudent = false; private FileSize bufferSize = new FileSize(DEFAULT_BUFFER_SIZE); //...... }
FileAppender繼承了OutputStreamAppender,它定義了append、prudent、bufferSize屬性
start
public void start() { int errors = 0; if (getFile() != null) { addInfo("File property is set to [" + fileName + "]"); if (prudent) { if (!isAppend()) { setAppend(true); addWarn("Setting \"Append\" property to true on account of \"Prudent\" mode"); } } if (checkForFileCollisionInPreviousFileAppenders()) { addError("Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting."); addError(MORE_INFO_PREFIX + COLLISION_WITH_EARLIER_APPENDER_URL); errors++; } else { // file should be opened only if collision free try { openFile(getFile()); } catch (java.io.IOException e) { errors++; addError("openFile(" + fileName + "," + append + ") call failed.", e); } } } else { errors++; addError("\"File\" property not set for appender named [" + name + "]."); } if (errors == 0) { super.start(); } }
start方法要求fileName必須有值,在prudent模式下會(huì)強(qiáng)制開(kāi)啟append;另外start的時(shí)候會(huì)執(zhí)行checkForFileCollisionInPreviousFileAppenders判斷是否有沖突,沒(méi)有沖突則執(zhí)行openFile方法
checkForFileCollisionInPreviousFileAppenders
protected boolean checkForFileCollisionInPreviousFileAppenders() { boolean collisionsDetected = false; if (fileName == null) { return false; } @SuppressWarnings("unchecked") Map<String, String> map = (Map<String, String>) context.getObject(CoreConstants.FA_FILENAME_COLLISION_MAP); if (map == null) { return collisionsDetected; } for (Entry<String, String> entry : map.entrySet()) { if (fileName.equals(entry.getValue())) { addErrorForCollision("File", entry.getValue(), entry.getKey()); collisionsDetected = true; } } if (name != null) { map.put(getName(), fileName); } return collisionsDetected; }
checkForFileCollisionInPreviousFileAppenders方法從上下文讀取FA_FILENAME_COLLISION_MAP,判斷有沒(méi)有文件名重復(fù)的,有則返回true
openFile
public void openFile(String file_name) throws IOException { lock.lock(); try { File file = new File(file_name); boolean result = FileUtil.createMissingParentDirectories(file); if (!result) { addError("Failed to create parent directories for [" + file.getAbsolutePath() + "]"); } ResilientFileOutputStream resilientFos = new ResilientFileOutputStream(file, append, bufferSize.getSize()); resilientFos.setContext(context); setOutputStream(resilientFos); } finally { lock.unlock(); } }
openFile方法加鎖創(chuàng)建file,然后通過(guò)createMissingParentDirectories來(lái)創(chuàng)建不存在的父目錄,最后創(chuàng)建根據(jù)file、append參數(shù)、bufferSize來(lái)創(chuàng)建ResilientFileOutputStream
stop
public void stop() { super.stop(); Map<String, String> map = ContextUtil.getFilenameCollisionMap(context); if (map == null || getName() == null) return; map.remove(getName()); }
stop方法會(huì)獲取FA_FILENAME_COLLISION_MAP,移除當(dāng)前文件名
writeOut
protected void writeOut(E event) throws IOException { if (prudent) { safeWrite(event); } else { super.writeOut(event); } }
FileAppender覆蓋了OutputStreamAppender的writeOut方法,在prudent為true時(shí)執(zhí)行safeWrite
safeWrite
private void safeWrite(E event) throws IOException { ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) getOutputStream(); FileChannel fileChannel = resilientFOS.getChannel(); if (fileChannel == null) { return; } // Clear any current interrupt (see LOGBACK-875) boolean interrupted = Thread.interrupted(); FileLock fileLock = null; try { fileLock = fileChannel.lock(); long position = fileChannel.position(); long size = fileChannel.size(); if (size != position) { fileChannel.position(size); } super.writeOut(event); } catch (IOException e) { // Mainly to catch FileLockInterruptionExceptions (see LOGBACK-875) resilientFOS.postIOFailure(e); } finally { if (fileLock != null && fileLock.isValid()) { fileLock.release(); } // Re-interrupt if we started in an interrupted state (see LOGBACK-875) if (interrupted) { Thread.currentThread().interrupt(); } } }
safeWrite會(huì)通過(guò)fileChannel.lock()進(jìn)行加鎖,然后執(zhí)行fileChannel.position(size),最后通過(guò)父類writeOut進(jìn)行寫入;對(duì)于IOException會(huì)執(zhí)行resilientFOS.postIOFailure(e)
小結(jié)
logback的FileAppender繼承了OutputStreamAppender,它定義了append、prudent、bufferSize屬性,它使用的是ResilientFileOutputStream,其writeOut方法主要是新增了對(duì)prudent模式的支持,在prudent為true時(shí)采用的是safeWrite。
以上就是logback的FileAppender文件追加模式和沖突檢測(cè)解讀的詳細(xì)內(nèi)容,更多關(guān)于logback FileAppender沖突檢測(cè)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
http中g(shù)et請(qǐng)求與post請(qǐng)求區(qū)別及如何選擇
這篇文章主要介紹了http中g(shù)et請(qǐng)求與post請(qǐng)求在應(yīng)用中應(yīng)該如何選擇,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2021-09-09Java實(shí)現(xiàn)大文件的分片上傳與下載(springboot+vue3)
這篇文章主要為大家詳細(xì)介紹了java基于springboot+vue3如何大文件的分片上傳與下載,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2023-06-06FluentMybatis實(shí)現(xiàn)mybatis動(dòng)態(tài)sql拼裝和fluent api語(yǔ)法
本文主要介紹了FluentMybatis實(shí)現(xiàn)mybatis動(dòng)態(tài)sql拼裝和fluent api語(yǔ)法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08springboot?加載本地jar到maven的實(shí)現(xiàn)方法
如何在SpringBoot項(xiàng)目中加載本地jar到Maven本地倉(cāng)庫(kù),使用Maven的install-file目標(biāo)來(lái)實(shí)現(xiàn),本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-01-01Object.wait()與Object.notify()的用法詳細(xì)解析
以下是對(duì)java中Object.wait()與Object.notify()的用法進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下2013-09-09