Go语言Zap日志库使用封装,日志分割

1. 日志目录结果

└──logger
        ├── log
        │   ├── test-2021-05-31T04-45-58.137.log
        │   ├── test-2021-05-31T04-46-03.650.log
        │   ├── test-2021-05-31T04-46-09.013.log
        │   └── test.log
        ├── logger.go
        └── logger_test.go

logger.go 文件

安装依赖

  1. go get -u go.uber.org/zap
  2. go get -u github.com/natefinch/lumberjack
package logger

// 安装以下依赖库
// go get -u go.uber.org/zap
// go get -u github.com/natefinch/lumberjack
import (
    "github.com/natefinch/lumberjack"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "os"
    "path/filepath"
)

const DefaultLogPath = "/var/log/test" // 默认输出日志文件路径

type LogConfigs struct {
    LogLevel          string // 日志打印级别 debug  info  warning  error
    LogFormat         string // 输出日志格式  logfmt, json
    LogPath           string // 输出日志文件路径
    LogFileName       string // 输出日志文件名称
    LogFileMaxSize    int    // 【日志分割】单个日志文件最多存储量 单位(mb)
    LogFileMaxBackups int    // 【日志分割】日志备份文件最多数量
    LogMaxAge         int    // 日志保留时间,单位: 天 (day)
    LogCompress       bool   // 是否压缩日志
    LogStdout         bool   // 是否输出到控制台
}

// InitLogger 初始化 log
func InitLogger(conf LogConfigs) error {
    logLevel := map[string]zapcore.Level{
        "debug": zapcore.DebugLevel,
        "info":  zapcore.InfoLevel,
        "warn":  zapcore.WarnLevel,
        "error": zapcore.ErrorLevel,
    }
    writeSyncer, err := getLogWriter(conf) // 日志文件配置 文件位置和切割
    if err != nil {
        return err
    }
    encoder := getEncoder(conf)          // 获取日志输出编码
    level, ok := logLevel[conf.LogLevel] // 日志打印级别
    if !ok {
        level = logLevel["info"]
    }
    core := zapcore.NewCore(encoder, writeSyncer, level)
    logger := zap.New(core, zap.AddCaller()) // zap.Addcaller() 输出日志打印文件和行数如: logger/logger_test.go:33
    // 1. zap.ReplaceGlobals 函数将当前初始化的 logger 替换到全局的 logger,
    // 2. 使用 logger 的时候 直接通过 zap.S().Debugf("xxx") or zap.L().Debug("xxx")
    // 3. 使用 zap.S() 和 zap.L() 提供全局锁,保证一个全局的安全访问logger的方式
    zap.ReplaceGlobals(logger)
    //zap.L().Debug("")
    //zap.S().Debugf("")
    return nil
}

// getEncoder 编码器(如何写入日志)
func getEncoder(conf LogConfigs) zapcore.Encoder {
    encoderConfig := zap.NewProductionEncoderConfig()
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder   // log 时间格式 例如: 2021-09-11t20:05:54.852+0800
    encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder // 输出level序列化为全大写字符串,如 INFO DEBUG ERROR
    //encoderConfig.EncodeCaller = zapcore.FullCallerEncoder
    //encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
    if conf.LogFormat == "json" {
        return zapcore.NewJSONEncoder(encoderConfig) // 以json格式写入
    }
    return zapcore.NewConsoleEncoder(encoderConfig) // 以logfmt格式写入
}

// getLogWriter 获取日志输出方式  日志文件 控制台
func getLogWriter(conf LogConfigs) (zapcore.WriteSyncer, error) {

    // 判断日志路径是否存在,如果不存在就创建
    if exist := IsExist(conf.LogPath); !exist {
        if conf.LogPath == "" {
            conf.LogPath = DefaultLogPath
        }
        if err := os.MkdirAll(conf.LogPath, os.ModePerm); err != nil {
            conf.LogPath = DefaultLogPath
            if err := os.MkdirAll(conf.LogPath, os.ModePerm); err != nil {
                return nil, err
            }
        }
    }

    // 日志文件 与 日志切割 配置
    lumberJackLogger := &lumberjack.Logger{
        Filename:   filepath.Join(conf.LogPath, conf.LogFileName), // 日志文件路径
        MaxSize:    conf.LogFileMaxSize,                           // 单个日志文件最大多少 mb
        MaxBackups: conf.LogFileMaxBackups,                        // 日志备份数量
        MaxAge:     conf.LogMaxAge,                                // 日志最长保留时间
        Compress:   conf.LogCompress,                              // 是否压缩日志
    }
    if conf.LogStdout {
        // 日志同时输出到控制台和日志文件中
        return zapcore.NewMultiWriteSyncer(zapcore.AddSync(lumberJackLogger), zapcore.AddSync(os.Stdout)), nil
    } else {
        // 日志只输出到日志文件
        return zapcore.AddSync(lumberJackLogger), nil
    }
}

// IsExist 判断文件或者目录是否存在
func IsExist(path string) bool {
    _, err := os.Stat(path)
    return err == nil || os.IsExist(err)
}

3. logger_test.go 文件

package logger

import (
    "errors"
    "go.uber.org/zap"
    "testing"
    "time"
)

func TestInitLogger(t *testing.T) {
    // 1. 配置log参数
    conf := LogConfigs{
        LogLevel:          "debug",    // 输出日志级别 "debug" "info" "warn" "error"
        LogFormat:         "logfmt",   // 输出日志格式 logfmt, json
        LogPath:           "./log",    // 输出日志文件位置
        LogFileName:       "test.log", // 输出日志文件名称
        LogFileMaxSize:    1,          // 输出单个日志文件大小,单位MB
        LogFileMaxBackups: 10,         // 输出最大日志备份个数
        LogMaxAge:         1000,       // 日志保留时间,单位: 天 (day)
        LogCompress:       false,      // 是否压缩日志
        LogStdout:         true,       // 是否输出到控制台
    }
    // 2. 初始化log
    if err := InitLogger(conf); err != nil {
        t.Fatal(err)
    }

    // 3. 调用 Logger 打印日志测试
    zap.S().Infof("测试 Infof 用法:%s", "111")   // logger Infof 用法
    zap.S().Debugf("测试 Debugf 用法:%s", "111") // logger Debugf 用法
    go func() {
        for i := 0; i < 100000; i++ {
            zap.S().Infof("(1)协程内部调用测试 Infof 用法:%s", "111")
            time.Sleep(time.Millisecond)
        }
    }()
    zap.S().Errorf("测试 Errorf 用法:%s", "111") // logger Errorf 用法
    zap.S().Warnf("测试 Warnf 用法:%s", "111")   // logger Warnf 用法
    zap.S().Infof("测试 Infof 用法:%s, %d, %v, %f", "111", 1111, errors.New("collector returned no data"), 3333.33)
    // logger With 用法
    logger := zap.S().With("collector", "cpu", "name", "主机")
    logger.Infof("测试 (With + Infof) 用法:%s", "测试")
    zap.S().Errorf("测试 Errorf 用法:%s", "111")
    go func() {
        for i := 0; i < 100000; i++ {
            zap.S().Infof("(2)协程内部调用测试 Infof 用法:%s", "111")
            time.Sleep(time.Millisecond)
        }
    }()
    time.Sleep(time.Minute)
}

4. 输出日志文件 test.log

{"level":"INFO","ts":"2021-03-12T16:43:49.023+0800","caller":"logger/logger_test.go:28","msg":"测试 Infof 用法:111"}
{"level":"ERROR","ts":"2021-03-12T16:43:49.023+0800","caller":"logger/logger_test.go:30","msg":"测试 Errorf 用法:111"}
{"level":"WARN","ts":"2021-03-12T16:43:49.023+0800","caller":"logger/logger_test.go:31","msg":"测试 Warnf 用法:111"}
{"level":"INFO","ts":"2021-03-12T16:43:49.024+0800","caller":"logger/logger_test.go:32","msg":"测试 Infof 用法:111, 1111, collector returned no data, 3333.330000"}
{"level":"INFO","ts":"2021-03-12T16:43:49.024+0800","caller":"logger/logger_test.go:35","msg":"测试 (With + Infof) 用法:测试","collector":"cpu","name":"主机"}


2021-03-12T16:51:39.740+0800    INFO    logger/logger_test.go:28        测试 Infof 用法:111
2021-03-12T16:51:39.741+0800    ERROR   logger/logger_test.go:30        测试 Errorf 用法:111
2021-03-12T16:51:39.741+0800    WARN    logger/logger_test.go:31        测试 Warnf 用法:111
2021-03-12T16:51:39.741+0800    INFO    logger/logger_test.go:32        测试 Infof 用法:111, 1111, collector returned no data, 3333.330000
2022-03-12T16:51:39.741+0800    INFO    logger/logger_test.go:35        测试 (With + Infof) 用法:测试 {"collector": "cpu", "name": "主机"}