新年趁有集中的时间对自己的 Golang 也进行了一些修修补补,这个是一个 modules 的系列
Go 的基础 logger 是 log 这个库提供的,要做成日志比较集全的,还是欠缺比较多内容的。
包含以下功能:
1.log 等级和格式化能力。基础的只有 Print
2.文件 size 进行分文件
3.log 大小,log 是否压缩
4.是否清除旧日志。
5.滚动输出日志 等等。
如果通过 log 这个库重新开发一套,不如去对现有的库进行组合,Log 这块在 Golang 工程中安放在 middleware 模块。
日志配置读取,可以采取 github.com/spf13/viper。
日志如果不涉及到中间修改参数,只读的话,只学习 Get 数据类型 () 就足够用。
日志不必使用 struct 结构包一层,原因是只是读设置可以和其他库结合。
Level := viper.GetString(`settings.log.level`) //日志等级
LogName := viper.GetString(`settings.log.path`) // 日志文件名
LogSize := viper.GetInt(`settings.log.maxsize`) // 日志文件大小
MaxSave := viper.GetInt(`settings.log.maxsave`) // 最长保存天数
MaxBackups :=viper.GetInt(`settings.log.maxbackups`) // 最多备份几个
Compress:= viper.GetBool(`settings.log.compress`) // 是否为压缩文件 布尔类型
viper 读取 yaml 文件里面。因为扩展其他功能,所以 options 和 config 选择其他方案,下面是 Yaml 部分参数的推荐填法。
settings:
log:
level:debug
path:./logs/
maxbackups:10
maxsave:3
compress:1
zap 为 Uber 开源的高性能 log 包,通过 go get -u go.uber.org/zap 安装,使用后需要导入
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
大概衔接库的开发步骤如下:
步骤 1:做法上是用 zap 去设置 log 的等级和添加一些逻辑,做 EncoderConfig。
步骤 2:zapcore 装配参数功能后-->core
步骤 3:zap.New(core,zap.AddCaller(), zap.AddCallerSkip(1))
先去定义日志等级的数据结构,日志等级也是基础 log 不存在的,定义一个全局的 levelMap。
var levelMap = map[string]zapcore.Level{
"debug": zapcore.DebugLevel,
"info": zapcore.InfoLevel,
"warn": zapcore.WarnLevel, //
"error": zapcore.ErrorLevel,
"dpanic": zapcore.DPanicLevel,
"panic": zapcore.PanicLevel,
"fatal": zapcore.FatalLevel,
}
然后 Level := viper.GetString(settings.log.level
) ,把这个 level 参数传入到一个读取日志等级函数里面,下面是把这 2 个结合一起。
/**
获取日志等级
*/
func getLogLevel(lv string) zapcore.Level {
if level, ok := levelMap[lv]; ok {
return level
}
//没有找到就选择默认的等级
return zapcore.InfoLevel
}
// 初始化新建Log,下面就会结合一起
func NewLog(){
level := getLogLevel(viper.GetString(`settings.log.level`))
//省略代码 Config部分
encoder := zap.NewProductionEncoderConfig()
}
encoder,返回的就是 zapcore.EncoderConfig 的结构体(结构体通过实例然后装配对象),源码部分 NewProductionEncoderConfig 看看里面内容
func NewProductionEncoderConfig() zapcore.EncoderConfig {
return zapcore.EncoderConfig{
TimeKey: "ts",//时间对应的key名
LevelKey: "level",//日志级别对应的key名
NameKey: "logger",//logger名对应的key名
CallerKey: "caller",//时间对应的key名
MessageKey: "msg",//日志内容对应的key名,此参数必须不为空,否则日志主体不处理
StacktraceKey: "stacktrace",//栈追踪的key名
LineEnding: zapcore.DefaultLineEnding,//默认换行,即使不设置
EncodeLevel: zapcore.LowercaseLevelEncoder,//小写
EncodeTime: zapcore.EpochTimeEncoder,//时间转为s
EncodeDuration: zapcore.SecondsDurationEncoder,//日期转为s
EncodeCaller: zapcore.ShortCallerEncoder,//记录调用路径格式为package/file:line
}
EncodeTime,StacktraceKey(满足错误日志输出堆栈),EncodeDuration,EncodeCaller 都是比较推荐做修改的,下面是一个通过 zapcore.EncoderConfig 去链接设置的。
//时间转为设置s
encoder.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02 15:04:05.000000"))
}
encoder.EncodeDuration = func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendInt64(int64(d) / 1000000)
}
encoder.EncodeCaller =func (caller EntryCaller, enc PrimitiveArrayEncoder) {
enc.AppendString(caller.TrimmedPath())
}
enc.AppendString(caller.TrimmedPath())
需要定义一个同步 IO 写入变量,viper 读取配置和 zap 做结合。
var writers []zapcore.WriteSyncer
if viper.GetBool("settings.log.consoleStdout") {
//追加到zapcore的写入缓存和zapcore同步输出日志到控制台
writers = append(writers, zapcore.AddSync(os.Stdout))
}
if viper.GetBool("settings.log.fileStdout") {
//同步输出到output文件,fileConfig后面会讲到,因为zap没有包含这部分功能。
writers = append(writers, zapcore.AddSync(fileConfig))
}
zapcore.NewCore 是一个适配器,然后可以用下面的方式去创建后装配。
var sugaredLogger *zap.SugaredLogger //需要先定义一个全局,全局变量不要用:=赋值就行
func NewLog(){
//省略上面的部分代码
core := zapcore.NewCore(
zapcore.NewJSONEncoder(encoder), //enc Encoder
zapcore.NewMultiWriteSyncer(syncWriters...), //ws WriteSyncer
zap.NewAtomicLevelAt(level)) //enab LevelEnabler
logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
sugaredLogger = logger.Sugar() //printf格式记录语句
}
enc Encoder 是用于编码写入日志的,装配了 NewJSONEncoder(),这块和 Netty 那边大量的装配很像。
1.config 日志使用 lumberjack.v2 与现有的结合。
2.包装使用自定义拼接的日志 middleware。