摘要:前言一般都会对应用程序日志做回滚处理,本文简要分析日志回滚实现触发策略使用接口来抽象日志回滚触发策略,使用了设计模式方法用于初始化策略,方法用于判断是否需要回滚,接口的不同实现类对应不同的策略组合模式,聚合不同的策略类基于时间的回滚策略基于
前言
一般都会对应用程序日志做回滚处理,本文简要分析 log4j2 日志回滚实现
触发策略log4j2 使用 TriggeringPolity 接口来抽象日志回滚触发策略,使用了 Strategy + Compose 设计模式
public interface TriggeringPolicy { void initialize(final RollingFileManager manager); boolean isTriggeringEvent(final LogEvent event); }
initialize 方法用于初始化策略,isTriggeringEvent 方法用于判断是否需要回滚,TriggeringPolicy 接口的不同实现类对应不同的策略
// 组合模式,聚合不同的策略类 public final class CompositeTriggeringPolicy implements TriggeringPolicy { ... } // 基于时间的回滚策略 public final class TimeBasedTriggeringPolicy implements TriggeringPolicy { ... } // 基于文件大小的回滚策略 public final class SizeBasedTriggeringPolicy implements TriggeringPolicy { ... }基于时间的触发策略 回滚策略
log4j2 使用 RolloverStrategy 接口抽象日志回滚策略
public interface RolloverStrategy { RolloverDescription rollover(final RollingFileManager manager) throws SecurityException; }
rollover 方法并不直接执行回滚操作,而是返回一个 RolloverDescription 接口,该接口用于获取日志回滚需要进行的操作: Action
public interface RolloverDescription { String getActiveFileName(); boolean getAppend(); Action getSynchronous(); Action getAsynchronous(); }回滚动作
log4j2 使用 Action 接口抽象日志回滚过程中的一系列动作,使用了 Command + Compose 设计模式
public interface Action extends Runnable { boolean execute() throws IOException; void close(); boolean isComplete(); }
AbstractAction 类是 Action 接口的抽象实现,使用了 Method template 设计模式,子类通过 override execute 方法执行不同的动作
public synchronized void run() { if (!interrupted) { try { execute(); } catch (final IOException ex) { reportException(ex); } complete = true; interrupted = true; } } public abstract boolean execute() throws IOException;
文件重命名,FileRenameAction
文件删除,DeleteAction
文件压缩,GzCompressAction, ZipCompressAction
聚合,CompositeAction
回滚管理log4j2 每个 Appender 都有一个 Manager 与之对应(多对一), RollingFileAppender 对应的 Manager 为RollingFileManager, 它管理着日志的写入,回滚 .etc,类层次结构
AbstractManager OutputStreamManager FileManager RollingFileManager
非常经典的 面向对象 设计,单一职责. AbstractManager 保存 Manager 基本信息,例如 name(名字),count(引用计数),并提供静态工厂方法根据名字获取 Manager,这个方法同样值得学习和借鉴
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) { // 使用工厂类创建具体的 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); } // 增加引用计数 manager.count++; return manager; } finally { // 释放锁 LOCK.unlock(); } }
RollingFileAppender 在 append LogEvent 时会先调用 RollingFileManager 的 checkRollover 方法尝试进行日志回滚,然后再调用父类的 append 方法,这种子类通过 override 方法 "拦截" 父类默认实现增加自己的处理逻辑的方法很常见
// RollingFileAppender.java @Override public void append(final LogEvent event) { getManager().checkRollover(event); super.append(event); }
RollingFileManager 的 checkRollover 方法使用上文提到的 触发策略类 TriggeringPolicy 判断是否符合触发条件,如果符合调用 rollover 方法
public synchronized void checkRollover(final LogEvent event) { if (triggeringPolicy.isTriggeringEvent(event)) { rollover(); } }
不带参数的 rollover 方法最终调用带 RolloverStrategy(回滚策略)类型参数的版本,为了代码显示更加紧凑特意省略掉了日志输出和异常处理逻辑,有几个地方值得品味
使用信号量进行同步,所以不要太频繁打 log 触发回滚,会 block 线程
同步 Action 在当前线程立即执行,异步 Action 则启动一个线程执行
如果异步 Action 很可执行完毕(某些极端情况),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(); } } }总结
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/66823.html
摘要:前言使用插件机制加载各种组件,本文简要分析插件机制实现注解注解提供了一种便捷的方法将一个类声明成的插件,比如,单例类用来保存插件信息,暴露了一些方法从配置文件中加载内置插件,使用了单例设计模式线程安全的数据结构使用了一些多线程编程的最佳实践 前言 log4j2 使用插件机制加载各种组件:appender, logger .etc,本文简要分析 log4j2 插件机制实现 Plugin ...
摘要:如上图所示,的实际上是已中间件的形式放在应用层,不用依赖数据库对协议的支持,完全剥离了分布式事务方案对数据库在协议支持上的要求。 微信公众号「后端进阶」,专注后端技术分享:Java、Golang、WEB框架、分布式中间件、服务治理等等。 在微服务架构体系下,我们可以按照业务模块分层设计,单独部署,减轻了服务部署压力,也解耦了业务的耦合,避免了应用逐渐变成一个庞然怪物,从而可以轻松扩展,...
摘要:上一篇文章模块分析第节模块一日志记录的级别优先级,记录调试的详细信息,只在调试时开启优先级,记录普通的消息,报告错误和警告等待。监听端口号上一篇文章模块分析第节模块 上一篇文章:Python模块分析:第3节-typing模块 一、日志记录的级别 debug:优先级10,记录调试的详细信息,只在调试时开启 info:优先级20,记录普通的消息,报告错误和警告等待。 warning:优...
阅读 2822·2023-04-25 19:08
阅读 1390·2021-11-16 11:45
阅读 1878·2021-10-13 09:40
阅读 3872·2021-09-30 09:47
阅读 2379·2019-08-30 15:44
阅读 2169·2019-08-30 13:03
阅读 1332·2019-08-30 12:56
阅读 1854·2019-08-26 14:04