日志库的设计,一般而言要抓住最核心的一条,就是日志从产生到到达最终目的地期间的处理流程。一般而言,为了设计一个灵活可扩展,可配置的日志库,主要将日志库分为4个部分去设计,分别是:记录器、过滤器、格式化器、输出器四部分。
- 记录器:负责产生日志记录的原始信息,比如(原始信息,日志等级,时间,记录的位置)等等信息。
- 过滤器:负责按指定的过滤条件过滤掉我们不需要的日志(比如按日志等级过滤)。
- 格式化器:负责对原始日志信息按照我们想要的格式去格式化。
- 输出器:负责将将要进行记录的日志(一般经过过滤器及格式化器的处理后)记录到日志目的地(例如:输出到文件中)
下面以一条日志的生命周为例说明日志库是怎么工作的。
一条日志的生命周期:
1. 产生。info!(“log information.”);
2. 经过记录器。记录器去获取日志发生的时间,位置,线程信息等等信息,会有一个数据结构去存储你需要的信息(例如:msg:”log information.”,time:2018-3-20 10:00:00,level:info,location:main.rs:3 lines)
3. 经过过滤器。决定是否记录(例如,过滤条件设为info级一下的过滤掉,这里条日志信息等级是info,满足条件,继续。)
4. 经过格式化器。假设我们想输出为“2018-3-22 10:00:00 [info] log information.”
5. 到输出器。例如输出到文件中,我们就将这条信息写到文件上(File::write(….);).
6. 这条日志信息生命结束了。
根据这四部分,去组成日志库的核功能。在具体实现上,可能要要设计一个核心处理器,负责读取日志配置文件,根据配置文件中的配置条件去构建相应的代码等等工作。
伪代码实现:
- 从配置文件中读取配置(可通过序列化或其他方式),生成Config。
- LoggerBuilder根据Config去构造Logger。
- 由Logger实现日志库的核心功能。
//配置
struct Config{
level:Level,
...
}
//Logger建造者
struct LoggerBuilder{
...
}
//Logger
struct Logger{
record:Recorder,
filter:Filter,
formater:Formater,
output:Output,
}
fern,rust实现的一个日志库,基本就是这种思路。log4rs,rust实现的一个复杂可配置的日志库,也基本是上面这种模式,只不过每一部分的实现更加复杂,灵活,多样。