好长时间没有写点干货出来了,偷懒太久了23333.
前段时间隔壁组小姐姐又陷入了日志组件的水深火热中,emmm,毕竟还是我当年学go语言时写的第一个项目,留的坑太深,设计也太差。(默默心疼下小姐姐)
言归正传,ServiceComb中的日志组件并不是自研的,从配套go的1.12版本开始,使用了uber开源出来的zap日志组件。虽然不属于ServiceComb,但是不知道为什么还是强行放在了这个系列里,强行从ServiceComb学zap吧,学习下zap的设计模式以及代码实现的细节,也算是对zap代码走读的一个指导。
先转一张网上流传很广泛的图
这张图大致描述了zap中有哪些模块,但是还体现不出来zap的精髓,我们就按照上图,从对外接口向下分析,学习zap中的实现。
既然是打印日志,一定需要一个接口或者结构体来实现日志打印的功能,在zap中,对外呈现的实现日志打印方法的是Logger结构体而不是接口,这个其实与常见的用接口定义API的模式有点不同。在zap的设计理念中,Logger虽然是结构体,但Logger中的core却是接口,这个接口可以有不同的实现,core中的方法才是真正实现日志的编码和输出的地方。而zap.core和Logger几乎是完全解耦的,也为我们按模块学习zap的实现提供了便利。
// A Logger provides fast, leveled, structured logging. All methods are safe
// for concurrent use.
//
// The Logger is designed for contexts in which every microsecond and every
// allocation matters, so its API intentionally favors performance and type
// safety over brevity. For most applications, the SugaredLogger strikes a
// better balance between performance and ergonomics.
type Logger struct {
core zapcore.Core
development bool
name string
errorOutput zapcore.WriteSyncer
addCaller bool
addStack zapcore.LevelEnabler
callerSkip int
}
稍后我们再回过头看Logger中的方法,先探究下如何构造一个Logger,zap提供了两类构造Logger的方式,一类是使用了建造者模式的Build方法,一类是接收Option参数的New方法,这两类方法提供的能力完全相同,只是给用户提供了不同的选择。先看看建造者模式的实现。
在config.go文件中,有以下结构体和方法:
Config这个结构体是创建一个Logger的基础。
// Config offers a declarative way to construct a logger. It doesn't do
// anything that can't be done with New, Options, and the various
// zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to
// toggle common options.
//
// Note that Config intentionally supports only the most common options. More
// unusual logging setups (logging to network connections or message queues,
// splitting output between multiple files, etc.) are possible, but require
// direct use of the zapcore package. For sample code, see the package-level
// BasicConfiguration and AdvancedConfiguration examples.
//
// For an example showing runtime log level changes, see the documentation for
// AtomicLevel.
type Config struct {
// Level is the minimum enabled logging level. Note that this is a dynamic
// level, so calling Config.Level.SetLevel will atomically change the log
// level of all loggers descended from this config.
Level AtomicLevel `json:"level" yaml:"level"`
// Development puts the logger in development mode, which changes the
// behavior of DPanicLevel and takes stacktraces more liberally.
Development bool `json:"development" yaml:"development"`
// DisableCaller stops annotating logs with the calling function's file
// name and line number. By default, all logs are annotated.
DisableCaller bool `json:"disableCaller" yaml:"disableC