摘要:前言一般都會對應(yīng)用程序日志做回滾處理,本文簡要分析日志回滾實現(xiàn)觸發(fā)策略使用接口來抽象日志回滾觸發(fā)策略,使用了設(shè)計模式方法用于初始化策略,方法用于判斷是否需要回滾,接口的不同實現(xiàn)類對應(yīng)不同的策略組合模式,聚合不同的策略類基于時間的回滾策略基于
前言
一般都會對應(yīng)用程序日志做回滾處理,本文簡要分析 log4j2 日志回滾實現(xiàn)
觸發(fā)策略log4j2 使用 TriggeringPolity 接口來抽象日志回滾觸發(fā)策略,使用了 Strategy + Compose 設(shè)計模式
public interface TriggeringPolicy { void initialize(final RollingFileManager manager); boolean isTriggeringEvent(final LogEvent event); }
initialize 方法用于初始化策略,isTriggeringEvent 方法用于判斷是否需要回滾,TriggeringPolicy 接口的不同實現(xiàn)類對應(yīng)不同的策略
// 組合模式,聚合不同的策略類 public final class CompositeTriggeringPolicy implements TriggeringPolicy { ... } // 基于時間的回滾策略 public final class TimeBasedTriggeringPolicy implements TriggeringPolicy { ... } // 基于文件大小的回滾策略 public final class SizeBasedTriggeringPolicy implements TriggeringPolicy { ... }基于時間的觸發(fā)策略 回滾策略
log4j2 使用 RolloverStrategy 接口抽象日志回滾策略
public interface RolloverStrategy { RolloverDescription rollover(final RollingFileManager manager) throws SecurityException; }
rollover 方法并不直接執(zhí)行回滾操作,而是返回一個 RolloverDescription 接口,該接口用于獲取日志回滾需要進(jìn)行的操作: Action
public interface RolloverDescription { String getActiveFileName(); boolean getAppend(); Action getSynchronous(); Action getAsynchronous(); }回滾動作
log4j2 使用 Action 接口抽象日志回滾過程中的一系列動作,使用了 Command + Compose 設(shè)計模式
public interface Action extends Runnable { boolean execute() throws IOException; void close(); boolean isComplete(); }
AbstractAction 類是 Action 接口的抽象實現(xiàn),使用了 Method template 設(shè)計模式,子類通過 override execute 方法執(zhí)行不同的動作
public synchronized void run() { if (!interrupted) { try { execute(); } catch (final IOException ex) { reportException(ex); } complete = true; interrupted = true; } } public abstract boolean execute() throws IOException;
文件重命名,F(xiàn)ileRenameAction
文件刪除,DeleteAction
文件壓縮,GzCompressAction, ZipCompressAction
聚合,CompositeAction
回滾管理log4j2 每個 Appender 都有一個 Manager 與之對應(yīng)(多對一), RollingFileAppender 對應(yīng)的 Manager 為RollingFileManager, 它管理著日志的寫入,回滾 .etc,類層次結(jié)構(gòu)
AbstractManager OutputStreamManager FileManager RollingFileManager
非常經(jīng)典的 面向?qū)ο?設(shè)計,單一職責(zé). AbstractManager 保存 Manager 基本信息,例如 name(名字),count(引用計數(shù)),并提供靜態(tài)工廠方法根據(jù)名字獲取 Manager,這個方法同樣值得學(xué)習(xí)和借鑒
public staticM getManager(final String name, final ManagerFactory factory, final T data) { // 獲取鎖 LOCK.lock(); try { @SuppressWarnings("unchecked") M manager = (M) MAP.get(name); if (manager == null) { // 使用工廠類創(chuàng)建具體的 Manager manager = factory.createManager(name, data); if (manager == null) { throw new IllegalStateException("ManagerFactory [" + factory + "] unable to create manager for [" + name + "] with data [" + data + "]"); } MAP.put(name, manager); } else { manager.updateData(data); } // 增加引用計數(shù) manager.count++; return manager; } finally { // 釋放鎖 LOCK.unlock(); } }
RollingFileAppender 在 append LogEvent 時會先調(diào)用 RollingFileManager 的 checkRollover 方法嘗試進(jìn)行日志回滾,然后再調(diào)用父類的 append 方法,這種子類通過 override 方法 "攔截" 父類默認(rèn)實現(xiàn)增加自己的處理邏輯的方法很常見
// RollingFileAppender.java @Override public void append(final LogEvent event) { getManager().checkRollover(event); super.append(event); }
RollingFileManager 的 checkRollover 方法使用上文提到的 觸發(fā)策略類 TriggeringPolicy 判斷是否符合觸發(fā)條件,如果符合調(diào)用 rollover 方法
public synchronized void checkRollover(final LogEvent event) { if (triggeringPolicy.isTriggeringEvent(event)) { rollover(); } }
不帶參數(shù)的 rollover 方法最終調(diào)用帶 RolloverStrategy(回滾策略)類型參數(shù)的版本,為了代碼顯示更加緊湊特意省略掉了日志輸出和異常處理邏輯,有幾個地方值得品味
使用信號量進(jìn)行同步,所以不要太頻繁打 log 觸發(fā)回滾,會 block 線程
同步 Action 在當(dāng)前線程立即執(zhí)行,異步 Action 則啟動一個線程執(zhí)行
如果異步 Action 很可執(zhí)行完畢(某些極端情況),finally 語句塊會釋放 semaphore
private boolean rollover(final RolloverStrategy strategy) { semaphore.acquire(); boolean success = false; Thread thread = null; try { final RolloverDescription descriptor = strategy.rollover(this); if (descriptor != null) { writeFooter(); close(); if (descriptor.getSynchronous() != null) { success = descriptor.getSynchronous().execute(); } if (success && descriptor.getAsynchronous() != null) { thread = new Log4jThread(new AsyncAction( descriptor.getAsynchronous(), this)); thread.start(); } return true; } return false; } finally { if (thread == null || !thread.isAlive()) { semaphore.release(); } } }總結(jié)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/66823.html
摘要:前言使用插件機(jī)制加載各種組件,本文簡要分析插件機(jī)制實現(xiàn)注解注解提供了一種便捷的方法將一個類聲明成的插件,比如,單例類用來保存插件信息,暴露了一些方法從配置文件中加載內(nèi)置插件,使用了單例設(shè)計模式線程安全的數(shù)據(jù)結(jié)構(gòu)使用了一些多線程編程的最佳實踐 前言 log4j2 使用插件機(jī)制加載各種組件:appender, logger .etc,本文簡要分析 log4j2 插件機(jī)制實現(xiàn) Plugin ...
摘要:如上圖所示,的實際上是已中間件的形式放在應(yīng)用層,不用依賴數(shù)據(jù)庫對協(xié)議的支持,完全剝離了分布式事務(wù)方案對數(shù)據(jù)庫在協(xié)議支持上的要求。 微信公眾號「后端進(jìn)階」,專注后端技術(shù)分享:Java、Golang、WEB框架、分布式中間件、服務(wù)治理等等。 在微服務(wù)架構(gòu)體系下,我們可以按照業(yè)務(wù)模塊分層設(shè)計,單獨部署,減輕了服務(wù)部署壓力,也解耦了業(yè)務(wù)的耦合,避免了應(yīng)用逐漸變成一個龐然怪物,從而可以輕松擴(kuò)展,...
摘要:上一篇文章模塊分析第節(jié)模塊一日志記錄的級別優(yōu)先級,記錄調(diào)試的詳細(xì)信息,只在調(diào)試時開啟優(yōu)先級,記錄普通的消息,報告錯誤和警告等待。監(jiān)聽端口號上一篇文章模塊分析第節(jié)模塊 上一篇文章:Python模塊分析:第3節(jié)-typing模塊 一、日志記錄的級別 debug:優(yōu)先級10,記錄調(diào)試的詳細(xì)信息,只在調(diào)試時開啟 info:優(yōu)先級20,記錄普通的消息,報告錯誤和警告等待。 warning:優(yōu)...
閱讀 2947·2023-04-25 19:08
閱讀 1431·2021-11-16 11:45
閱讀 1994·2021-10-13 09:40
閱讀 4163·2021-09-30 09:47
閱讀 2427·2019-08-30 15:44
閱讀 2309·2019-08-30 13:03
閱讀 1401·2019-08-30 12:56
閱讀 1903·2019-08-26 14:04