摘要:前言一般都會(huì)對(duì)應(yīng)用程序日志做回滾處理,本文簡(jiǎn)要分析日志回滾實(shí)現(xiàn)觸發(fā)策略使用接口來抽象日志回滾觸發(fā)策略,使用了設(shè)計(jì)模式方法用于初始化策略,方法用于判斷是否需要回滾,接口的不同實(shí)現(xiàn)類對(duì)應(yīng)不同的策略組合模式,聚合不同的策略類基于時(shí)間的回滾策略基于
前言
一般都會(huì)對(duì)應(yīng)用程序日志做回滾處理,本文簡(jiǎn)要分析 log4j2 日志回滾實(shí)現(xiàn)
觸發(fā)策略log4j2 使用 TriggeringPolity 接口來抽象日志回滾觸發(fā)策略,使用了 Strategy + Compose 設(shè)計(jì)模式
public interface TriggeringPolicy { void initialize(final RollingFileManager manager); boolean isTriggeringEvent(final LogEvent event); }
initialize 方法用于初始化策略,isTriggeringEvent 方法用于判斷是否需要回滾,TriggeringPolicy 接口的不同實(shí)現(xiàn)類對(duì)應(yīng)不同的策略
// 組合模式,聚合不同的策略類 public final class CompositeTriggeringPolicy implements TriggeringPolicy { ... } // 基于時(shí)間的回滾策略 public final class TimeBasedTriggeringPolicy implements TriggeringPolicy { ... } // 基于文件大小的回滾策略 public final class SizeBasedTriggeringPolicy implements TriggeringPolicy { ... }基于時(shí)間的觸發(fā)策略 回滾策略
log4j2 使用 RolloverStrategy 接口抽象日志回滾策略
public interface RolloverStrategy { RolloverDescription rollover(final RollingFileManager manager) throws SecurityException; }
rollover 方法并不直接執(zhí)行回滾操作,而是返回一個(gè) RolloverDescription 接口,該接口用于獲取日志回滾需要進(jìn)行的操作: Action
public interface RolloverDescription { String getActiveFileName(); boolean getAppend(); Action getSynchronous(); Action getAsynchronous(); }回滾動(dòng)作
log4j2 使用 Action 接口抽象日志回滾過程中的一系列動(dòng)作,使用了 Command + Compose 設(shè)計(jì)模式
public interface Action extends Runnable { boolean execute() throws IOException; void close(); boolean isComplete(); }
AbstractAction 類是 Action 接口的抽象實(shí)現(xiàn),使用了 Method template 設(shè)計(jì)模式,子類通過 override execute 方法執(zhí)行不同的動(dòng)作
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 每個(gè) Appender 都有一個(gè) Manager 與之對(duì)應(yīng)(多對(duì)一), RollingFileAppender 對(duì)應(yīng)的 Manager 為RollingFileManager, 它管理著日志的寫入,回滾 .etc,類層次結(jié)構(gòu)
AbstractManager OutputStreamManager FileManager RollingFileManager
非常經(jīng)典的 面向?qū)ο?設(shè)計(jì),單一職責(zé). AbstractManager 保存 Manager 基本信息,例如 name(名字),count(引用計(jì)數(shù)),并提供靜態(tài)工廠方法根據(jù)名字獲取 Manager,這個(gè)方法同樣值得學(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); } // 增加引用計(jì)數(shù) manager.count++; return manager; } finally { // 釋放鎖 LOCK.unlock(); } }
RollingFileAppender 在 append LogEvent 時(shí)會(huì)先調(diào)用 RollingFileManager 的 checkRollover 方法嘗試進(jìn)行日志回滾,然后再調(diào)用父類的 append 方法,這種子類通過 override 方法 "攔截" 父類默認(rèn)實(shí)現(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ù)的版本,為了代碼顯示更加緊湊特意省略掉了日志輸出和異常處理邏輯,有幾個(gè)地方值得品味
使用信號(hào)量進(jìn)行同步,所以不要太頻繁打 log 觸發(fā)回滾,會(huì) block 線程
同步 Action 在當(dāng)前線程立即執(zhí)行,異步 Action 則啟動(dòng)一個(gè)線程執(zhí)行
如果異步 Action 很可執(zhí)行完畢(某些極端情況),finally 語句塊會(huì)釋放 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)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/66823.html
摘要:前言使用插件機(jī)制加載各種組件,本文簡(jiǎn)要分析插件機(jī)制實(shí)現(xiàn)注解注解提供了一種便捷的方法將一個(gè)類聲明成的插件,比如,單例類用來保存插件信息,暴露了一些方法從配置文件中加載內(nèi)置插件,使用了單例設(shè)計(jì)模式線程安全的數(shù)據(jù)結(jié)構(gòu)使用了一些多線程編程的最佳實(shí)踐 前言 log4j2 使用插件機(jī)制加載各種組件:appender, logger .etc,本文簡(jiǎn)要分析 log4j2 插件機(jī)制實(shí)現(xiàn) Plugin ...
摘要:如上圖所示,的實(shí)際上是已中間件的形式放在應(yīng)用層,不用依賴數(shù)據(jù)庫對(duì)協(xié)議的支持,完全剝離了分布式事務(wù)方案對(duì)數(shù)據(jù)庫在協(xié)議支持上的要求。 微信公眾號(hào)「后端進(jìn)階」,專注后端技術(shù)分享:Java、Golang、WEB框架、分布式中間件、服務(wù)治理等等。 在微服務(wù)架構(gòu)體系下,我們可以按照業(yè)務(wù)模塊分層設(shè)計(jì),單獨(dú)部署,減輕了服務(wù)部署壓力,也解耦了業(yè)務(wù)的耦合,避免了應(yīng)用逐漸變成一個(gè)龐然怪物,從而可以輕松擴(kuò)展,...
摘要:上一篇文章模塊分析第節(jié)模塊一日志記錄的級(jí)別優(yōu)先級(jí),記錄調(diào)試的詳細(xì)信息,只在調(diào)試時(shí)開啟優(yōu)先級(jí),記錄普通的消息,報(bào)告錯(cuò)誤和警告等待。監(jiān)聽端口號(hào)上一篇文章模塊分析第節(jié)模塊 上一篇文章:Python模塊分析:第3節(jié)-typing模塊 一、日志記錄的級(jí)別 debug:優(yōu)先級(jí)10,記錄調(diào)試的詳細(xì)信息,只在調(diào)試時(shí)開啟 info:優(yōu)先級(jí)20,記錄普通的消息,報(bào)告錯(cuò)誤和警告等待。 warning:優(yōu)...
閱讀 3045·2023-04-25 19:08
閱讀 1499·2021-11-16 11:45
閱讀 2141·2021-10-13 09:40
閱讀 4321·2021-09-30 09:47
閱讀 2495·2019-08-30 15:44
閱讀 2452·2019-08-30 13:03
閱讀 1450·2019-08-30 12:56
閱讀 1953·2019-08-26 14:04