zaplog 将日志上报到飞书 企业微信 等im工具中

zaplog 将日志上报到飞书 lark 等im工具中

对zap的简单封装 完整地址github.com/luxun9527/zaplog 如果您觉得对您有帮助您的star就是我更新的动力

在开发中可以开箱即用,通过该库你能了解到zap各个常用配置的用法,支持将指定级别以上的日志通过机器人上报到im工具中如 飞书,企业微信,tg中。

Name: test-project #可选 填的话在增加一个 {"project": "Name"}的filed
Level: info  #日志等级 debug info warn error
Stacktrace: true #默认为true 在error级别及以上显示堆栈
AddCaller: true #默认为true  增加调用者信息
CallShip: 3 # 默认为3 调用栈深度
Mode: console #默认为console 输出到控制台  console file
Json: false #默认为false  是否json格式化
FileName:  #可选 file模式参数 输出到指定文件
ErrorFileName:  #可选 file模式参数 错误日志输出到的地方
MaxSize: 0 #可选 file模式参数 文件大小限制 单位MB
MaxAge: 0 #可选 file模式参数 文件最大保存时间 单位天
MaxBackup: 0 #可选 file模式参数 最大的日志数量
Async: false #默认为false file模式参数 是否异步落盘。
Compress: false #默认为false file模式参数 是否压缩
Console: false #默认为false file模式参数 是否同时输出到控制台
Color: true #默认为false  输出是否彩色 在开发的时候推荐使用。
IsReport: true  #默认为false 是否上报到im工具,开启上报的话,需要在程序结束执行sync
ReportConfig: # 上报配置 warn级别以上报到im工具
  Type: lark # 可选 lark(飞书也是这个) wx tg
  Token: https://open.feiashu.dcn/opesasan-apis/bot/v2/hook/71f86easadf6asdfas1212-ab9a23-464assd512-basdfss40b-1be001212fsadffe910a # lark 飞书填群机器人webhook tg填token wx填key   这个示例地址无效。
  ChatID: 0 # tg填chatID 其他不用填
  FlushSec: 3 # 刷新间隔单位为秒 开发测试调小一点,生产环境调大一点
  MaxCount: 20 #  最大缓存数量 达到刷新间隔或最大记录数 触发发送  开发测试调小一点,生产环境调大一点
  Level: warn # 指定上报级别
package zaplog

import (
    "github.com/luxun9527/zaplog/report"
    "github.com/mitchellh/mapstructure"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "gopkg.in/natefinch/lumberjack.v2"
    "log"
    "os"
    "reflect"
    "time"
)

const (
    // _defaultBufferSize specifies the default size used by Buffer.
    _defaultBufferSize = 256 * 1024 // 256 kB

    // _defaultFlushInterval specifies the default flush interval for
    // Buffer.
    _defaultFlushInterval = 30 * time.Second
)
const (
    _file    = "file"
    _console = "console"
)

type Config struct {
    Name string `json:",optional" mapstructure:"name"`
    //日志级别 debug info warn panic
    Level zap.AtomicLevel `json:"Level" mapstructure:"level"`
    //在error级别的时候 是否显示堆栈
    Stacktrace bool `json:",default=true" mapstructure:"stacktrace"`
    //添加调用者信息
    AddCaller bool `json:",default=true" mapstructure:"addCaller"`
    //调用链,往上多少级 ,在一些中间件,对日志有包装,可以通过这个选项指定。
    CallerShip int `json:",default=3" mapstructure:"callerShip"`
    //输出到哪里标准输出console,还是文件file
    Mode string `json:",default=console" mapstructure:"mode"`
    //文件名称加路径
    FileName string `json:",optional" mapstructure:"filename"`
    //error级别的日志输入到不同的地方,默认console 输出到标准错误输出,file可以指定文件
    ErrorFileName string `json:",optional" mapstructure:"errorFileName"`
    // 日志文件大小 单位MB 默认500MB
    MaxSize int `json:",optional" mapstructure:"maxSize"`
    //日志保留天数
    MaxAge int `json:",optional" mapstructure:"maxAge"`
    //日志最大保留的个数
    MaxBackup int `json:",optional" mapstructure:"maxBackUp"`
    //异步日志 日志将先输入到内存到,定时批量落盘。如果设置这个值,要保证在程序退出的时候调用Sync(),在开发阶段不用设置为true。
    Async bool `json:",optional" mapstructure:"async"`
    //是否输出json格式
    Json bool `json:",optional" mapstructure:"json"`
    //是否日志压缩
    Compress bool `json:",optional" mapstructure:"compress"`
    // file 模式是否输出到控制台
    Console bool `json:"console" mapstructure:"console"`
    // 非json格式,是否加上颜色。
    Color bool `json:",default=true" mapstructure:"color"`
    //是否report
    IsReport bool `json:",optional" mapstructure:"isReport"`
    //report配置
    ReportConfig report.ReportConfig `json:",optional" mapstructure:"reportConfig"`
    options      []zap.Option
}

func (lc *Config) UpdateLevel(level zapcore.Level) {
    lc.Level.SetLevel(level)
}

func (lc *Config) Build() *zap.Logger {
    if lc.Mode != _file && lc.Mode != _console {
        log.Panicln("mode must be console or file")
    }

    if lc.Mode == _file && lc.FileName == "" {
        log.Panicln("file mode, but file name is empty")
    }
    var (
        ws      zapcore.WriteSyncer
        errorWs zapcore.WriteSyncer
        encoder zapcore.Encoder
    )
    encoderConfig := zapcore.EncoderConfig{
        //当存储的格式为JSON的时候这些作为可以key
        MessageKey:    "message",
        LevelKey:      "level",
        TimeKey:       "time",
        NameKey:       "logger",
        CallerKey:     "caller",
        StacktraceKey: "stacktrace",
        LineEnding:    zapcore.DefaultLineEnding,
        //以上字段输出的格式
        EncodeLevel:    zapcore.LowercaseLevelEncoder,
        EncodeTime:     CustomTimeEncoder,
        EncodeDuration: zapcore.SecondsDurationEncoder,
        EncodeCaller:   zapcore.FullCallerEncoder,
    }
    if lc.Mode == _console {
        ws = zapcore.Lock(os.Stdout)
    } else {
        normalConfig := &lumberjack.Logger{
            Filename:   lc.FileName,
            MaxSize:    lc.MaxSize,
            MaxAge:     lc.MaxAge,
            MaxBackups: lc.MaxBackup,
            LocalTime:  true,
            Compress:   lc.Compress,
        }
        if lc.ErrorFileName != "" {
            errorConfig := &lumberjack.Logger{
                Filename:   lc.ErrorFileName,
                MaxSize:    lc.MaxSize,
                MaxAge:     lc.MaxAge,
                MaxBackups: lc.MaxBackup,
                LocalTime:  true,
                Compress:   lc.Compress,
            }
            errorWs = zapcore.Lock(zapcore.AddSync(errorConfig))
        }
        ws = zapcore.Lock(zapcore.AddSync(normalConfig))
    }
    //是否加上颜色。
    if lc.Color && !lc.Json {
        encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
    }
    encoder = zapcore.NewConsoleEncoder(encoderConfig)
    if lc.Json {
        encoder = zapcore.NewJSONEncoder(encoderConfig)
    }

    if lc.Async {
        ws = &zapcore.BufferedWriteSyncer{
            WS:            ws,
            Size:          _defaultBufferSize,
            FlushInterval: _defaultFlushInterval,
        }
        if errorWs != nil {
            errorWs = &zapcore.BufferedWriteSyncer{
                WS:            errorWs,
                Size:          _defaultBufferSize,
                FlushInterval: _defaultFlushInterval,
            }
        }
    }

    var c = []zapcore.Core{zapcore.NewCore(encoder, ws, lc.Level)}
    if errorWs != nil {
        highCore := zapcore.NewCore(encoder, errorWs, zapcore.ErrorLevel)
        c = append(c, highCore)
    }
    //文件模式同时输出到控制台
    if lc.Mode == _file && lc.Console {
        consoleWs := zapcore.NewCore(encoder, zapcore.Lock(os.Stdout), zapcore.ErrorLevel)
        c = append(c, consoleWs)
    }
    if lc.IsReport {
        //上报的格式一律json
        if !lc.Json {
            encoderConfig.EncodeLevel = zapcore.LowercaseLevelEncoder
            encoder = zapcore.NewJSONEncoder(encoderConfig)
        }
        //指定级别的日志上报。
        highCore := zapcore.NewCore(encoder, report.NewReportWriterBuffer(lc.ReportConfig), lc.ReportConfig.Level)
        c = append(c, highCore)
    }

    core := zapcore.NewTee(c...)

    logger := zap.New(core)
    //是否新增调用者信息
    if lc.AddCaller {
        lc.options = append(lc.options, zap.AddCaller())
        if lc.CallerShip != 0 {
            lc.options = append(lc.options, zap.AddCallerSkip(lc.CallerShip))
        }
    }
    //当错误时是否添加堆栈信息
    if lc.Stacktrace {
        //在error级别以上添加堆栈
        lc.options = append(lc.options, zap.AddStacktrace(zap.ErrorLevel))
    }
    if lc.Name != "" {
        logger = logger.With(zap.String("project", lc.Name))
    }

    return logger.WithOptions(lc.options...)

}

func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
    enc.AppendString(t.Format("2006-01-02-15:04:05"))
}

// StringToLogLevelHookFunc viper的string转zapcore.Level
func StringToLogLevelHookFunc() mapstructure.DecodeHookFunc {
    return func(
        f reflect.Type,
        t reflect.Type,
        data interface{}) (interface{}, error) {
        if f.Kind() != reflect.String {
            return data, nil
        }
        atomicLevel, err := zap.ParseAtomicLevel(data.(string))
        if err != nil {
            return data, nil
        }
        // Convert it by parsing
        return atomicLevel, nil
    }
}
func TestViperConfig(t *testing.T) {
    v := viper.New()
    v.SetConfigFile("./config.yaml")
    if err := v.ReadInConfig(); err != nil {
       log.Panicf("read config file failed, err:%v\n", err)
    }
    var c Config
    if err := v.Unmarshal(&c, viper.DecodeHook(StringToLogLevelHookFunc())); err != nil {
       log.Panicf("Unmarshal config file failed, err:%v\n", err)
    }
    InitZapLogger(&c)
    Debug("debug level ", zap.Any("test", "test"))
    Info("info level ", zap.Any("test", "test"))
    Warn("warn level ", zap.Any("test", "test"))
    Error("error level ", zap.Any("test", "test"))
    Panic("panic level ", zap.Any("test", "test"))
    Sync()
}

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!