Go 日志封装实战示例详解
作者:小雄Ya
背景
日志是框架中至关重要的一部分。在 Golang 只也提供了基本的日志组件,但其没有提供给比较多的功能。当然现在也有很多第三方已经封装好的库使用起来也很方便。作为成熟的开发者也应该学会封装自己的类库,以便后续在自己的个人项目中使用它。
Logger 封装的理解
Logger 库主要是一系列方法或接口,提供给应用程序代码调用然后将相关的数据记录到日志记录库中。就是在代码和底层日志库中间的一层。需要实现的功能是能够记录日志,且支持不同日志记录库的切换或添加新的记录库。
封装包,首先需要先定义一些能够描述日志方法的接口,基本的都是你的应用程序所需要的方法。比如,基本的日志接口基本的方法有 Info、Warn、 Error。然后就可以创建此接口的实现,将数据记录到记录库中。定义了接口和实现,就可以创建一个 logger 封装,并将相关的接口方法公开。
简单包封装
以下定义了五个方法:Info, Warn, Error 和 Fatal. 这些方法接口接收 2个参数,一个字符串和一个字段映射map.
type Logger interface { Debug(msg string, field map[string]interface{}) Info(msg string, field map[string]interface{}) Warn(msg string, field map[string]interface{}) Error(msg string, field map[string]interface{}) Fatal(msg string, field map[string]interface{}) }
接下来就是实现啦 Logger 方法,底层的实现主要是依赖使用 Logrus。首先需要创建一个 LLogger 结构体,该结构体包含 Logrus 实例。当然还需要一个 NewLLogger 方法来实例化一个 LLogger。
type LLogger struct { logger *logrus.Logger ctx context.Context } func NewLLogger(ctx context.Context) *LLogger { logger := logrus.New() logger.Out = os.Stdout return &LLogger{logger: logger, ctx: ctx} } func (l *LLogger) Info(msg string, fields map[string]interface{}) { l.logger.WithFields(fields).Info(msg) } func (l *LLogger) Warn(msg string, fields map[string]interface{}) { l.logger.WithFields(fields).Warn(msg) } func (l *LLogger) Error(msg string, fields map[string]interface{}) { l.logger.WithFields(fields).Error(msg) } func (l *LLogger) Fatal(msg string, fields map[string]interface{}) { l.logger.WithFields(fields).Fatal(msg) }
这样一个简单日志封装就完成了,使用时就可以直接使用。
func main() { ctx := ... // context 信息 lg := NewLLogger(ctx) fields := map[string]interface{}{ "userId": "xiaoxiong", "ipAddress": "127.0.0.1", } lg.Info("这是测试信息", fields) }
多日志库切换
当底层需要使用的是不同的日志库进行记录日志时,那就需要封装另外的日志,但是在实例化方法时可以增加一种日志类型参数,然后根据不同的类型实例化不同的日志。
比如使用了zap 包,那童养媳与奥实现接口的四个方法 :Info, Warn, Error and Fatal.
type ZapLog struct { logger *zap.Logger ctx context.Context } func NewZapLog(ctx context.Context) *ZapLog { logger, _ := zap.NewProduction() return &ZapLog{logger: logger, ctx: ctx} } func (l *ZapLog) Info(msg string, fields map[string]interface{}) { l.logger.Info(msg, zap.Any("args", fields)) } func (l *ZapLog) Warn(msg string, fields map[string]interface{}) { l.logger.Warn(msg, zap.Any("args", fields)) } func (l *ZapLog) Error(msg string, fields map[string]interface{}) { l.logger.Error(msg, zap.Any("args", fields)) } func (l *ZapLog) Fatal(msg string, fields map[string]interface{}) { l.logger.Fatal(msg, zap.Any("args", fields)) }
定义其他日志库的方法后就只需对这个两个日志库进行封装,根据不同的类型初始化出不同的日志,然后使用日志时候就使用相应的包进行处理。
type LoggerWrapper struct { logger Logger } func NewLoggerWrapper(loggerType string, ctx context.Context) *LoggerWrapper { var logger Logger switch loggerType { case "logrus": logger = NewLLogger(ctx) case "zap": logger = NewZapLog(ctx) default: logger = NewLLogger(ctx) } return &LoggerWrapper{logger: logger} }
使用中间件记录公共信息
封装中增加了 Context 上下文的内容,主要是为了记录一些公共属性。就像经常在链路追踪时可需要请求ID ,请求IP 等相关的信息。但是日志实例都是在程序启动时进行初始化的,在每次的请求时可能请求ID或者IP 都是不一样,所以要记录这部分信息,可以使用中间件将上下文的信息添加到日志消息中。
以上就是Go 日志封装实战示例详解的详细内容,更多关于Go 日志封装的资料请关注脚本之家其它相关文章!