self4j日志框架设计之从无到有

日志框架也是一个系统,我们下面也分别从建模以及实现架构两个角度去加以分析。

业务建模

记录日志其实是一个动作,但是它包含了许多内容,包括当前的上下文信息、日志数据(告警级别、内容)、业务标识(唯一标识符name,标签marker)、记录行为。记录时需要知道记录的动作怎么做,在哪个线程操作。

这里写图片描述

使用者只要关心调用日志记录动作,传递数据以及业务标识。一个系统要划分为许多业务块,每条日志输出要严格地属于某一个业务name;同时系统要对这些日志进行多角度的划分、关联,这就是标签的概念marker。name这个概念是严格的统一划分,好比数据库的表,正好面向对象的设计中被调用者需要一个载体,我们就设计出一个Logger的概念,与name进行挂钩。
这样使用者需要用到的场景就分为两步:
1、logger的获取
2、logger.log(level,marker,content)

总结来说分别是配置、组件的加载和日志行为的发生。前者是围绕后者的设计的,日志行为包含着许多要应对场景。

架构设计

下面介绍一下self4j这个日志框架的设计过程以及设计结果。

流程描述

写日志,需要调用者传递过来的信息以及当前的上下文信息(比如当前的类、行数、线程等),将其封装成一个类LoggingEvent。有了信息,怎么输出,引出appender的概念;io慢怎么办,不能影响业务代码的执行,要考虑异步的实现,需要维护线程池。
这是正常的流程,如果写日志的时候日志配置、组件尚未加载完成怎么办?需要先加入到队列中,引出SubstituteLogger的概念,先记录下要记录的行为,等待加载完成后再写。
如果写日志的过程中出现异常怎么办?要重试吗?重试机制怎么做?这涉及到开闭原则,这些部分的实现留给具体的日志实现去做。

理清头绪

这么多情况,怎么设计?我们先只考虑正常的流程,要获取logger,怎么获取?这么多logger,需要一个承担builder的类。在获取这个logger时可能会报错,因为logger还包含了appenders等信息,可能还没初始化完,但作为日志框架,要对这种情况加以处理而不是抛出异常。我们采取延缓执行的策略,返回一个特定的logger,这个logger不做实际的日志记录工作。这样这个流程就通了。
但是什么时候触发这些留待执行的任务,这里需要一个队列,在调用logger.log()方法的时候就把日志记录行为加入到队列中,在组件加载完成后执行。

组件加载

1、分析

组件的加载什么时候进行?日志的陪着和组件是不依赖项目的,并且加载一次就好,所以在jvm启动过程中就可以通过静态代码来进行加载,那么有哪些组件呢?TODO
组件加载可以放到logger的builer类里吗?从责任角度说,这是builder在初始化前要做的工作。我们上文说到在配置没有加载完成的时候还需要返回一种特殊的logger,看来这个应该是一个抽象工厂模式了,根据不同的加载情况来返回不同的logger获取工厂,最后再获取对应的logger。

扫描二维码关注公众号,回复: 2144528 查看本文章

2、self4j的实现

到这里为止,加载配置的任务还是可以直接放到loggerFactory那里,但self4j的实现是放到StaticLoggerBinder中的,这是什么玩意?StaticLoggerBinderLoggerFactoryBinder的一个实现,self4j允许自定义loggerFactory,将类名与loggerFactory绑定,这就是LoggerFactoryBinder的概念。StaticLoggerBinder是单例,通过静态代码块加载,在加载完成之后执行等待队列里的动作。

log操作

核心的操作部分嵌套不多,分为两步,首先构建参数,包括调用者传递过来的以及当前的上下文信息,然后将内容输出到该logger绑定的appender上。appender的维护也是一个值得深挖的地方.TODO

架构概图

架构详图

猜你喜欢

转载自blog.csdn.net/guzhangyu12345/article/details/80638722
今日推荐