Golang 选型

新年趁有集中的时间对自己的 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

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。


↙↙↙阅读原文可查看相关链接,并与作者交流