self4j日志框架设计之从无到有(结合logback的实现)


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

业务建模

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

self4j日志框架设计之从无到有(结合logback的实现)

使用者只要关心调用日志记录动作,传递数据以及业务标识。一个系统要划分为许多业务块,每条日志输出要严格地属于某一个业务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、logger的构造者以及执行者。
对于使用者来说,一个对象载体及其方法足矣,但是我们需要根据某种维度对logger进行划分,这样使用者不用重复传入基础的标示信息;因为根据配置的不同、加载的成功与否,logger的行为也会不同,这里通过工厂的方式来获取logger;最后logger动作的执行,这一块涉及到日志内容的解析、目的地的选择以及写入的实现,需要一个执行器组件。日志的动作不能对主流程造成干扰,所以很多时候日志动作需要异步执行,这就需要在独立于业务线程的单独线程来执行真正的写入动作。

组件加载可以放到logger的builer类里吗?从责任角度说,这是builder在初始化前要做的工作。我们上文说到在配置没有加载完成的时候还需要返回一种特殊的logger,看来这个应该是一个抽象工厂模式了,根据不同的加载情况来返回不同的logger获取工厂,最后再获取对应的logger。

2、self4j的实现

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

log操作

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

self4j日志框架设计之从无到有(结合logback的实现)
self4j日志框架设计之从无到有(结合logback的实现)

现有官方实现的流程

1、通过静态代码块加载logger的配置

self4j日志框架设计之从无到有(结合logback的实现)

这个过程包括配置的解析以及相应动作的执行
self4j日志框架设计之从无到有(结合logback的实现)
这个分为三种saxevent
self4j日志框架设计之从无到有(结合logback的实现)

将SaxEvent转化为具体的动作是重点所在:
self4j日志框架设计之从无到有(结合logback的实现)

2、在加载配置的过程中,如果要写日志怎么办,官方的做法是将日志动作记录下来,在加载完成后统一执行

2.1 如果还没加载好,就返回这个
self4j日志框架设计之从无到有(结合logback的实现)

2.2 获取logger时实际返回的是
self4j日志框架设计之从无到有(结合logback的实现)
它会将日志动作加到队列里
self4j日志框架设计之从无到有(结合logback的实现)

2.3 在加载完成后执行队列里的动作
self4j日志框架设计之从无到有(结合logback的实现)