封装zap log打印日志

封装 go.uber.org/zap log打印日志:

支持输出到标准输出、文件;

输出到文件支持按时间或者大小切分文件;

支持多个日志输出级别;

package main

import (
    "context"
    "github.com/opentracing/opentracing-go"
    "github.com/uber/jaeger-client-go"
    "go.uber.org/zap/zapcore"
    "go.uber.org/zap"
    rotatelogs "github.com/lestrrat-go/file-rotatelogs"
    "io"
    "os"
    "time"
)

type LogIface interface {
    // 包含链路跟踪信息的日志方法
    InfoWithCtx(ctx context.Context, format string, v ...interface{})
    ErrorWithCtx(ctx context.Context, format string, v ...interface{})
    // DebugWithCtx、WarnWithCtx、Info...
}

var sugarLogger *zap.SugaredLogger

/**
 * 日志配置
 */
type Config struct {
    Path        string //日志打印路径
    Level       string //需要打印日志的最低级别日志级别有debug,info,warn,error,panic,fatal
    OutputMode  int    // 0:file 1:stdout 2:file+stdout
    ArchiveTime int    //存档时间(单位天,默认20天)
    AddCaller   bool   //是否打印文件名行号,默认不打印debug会打印
}
var (
    OutputFile          int = 0 //file
    OutputStdout        int = 1 //stdout
    OutputFileAndStdout int = 2 //file+stdout
)

/**
 * 日志级别
 */
var (
    DebugLevel = "debug" //日志级别
    InfoLevel  = "info"
    WarnLevel  = "warn"
    ErrorLevel = "error"
    PanicLevel = "panic"
    FatalLevel = "fatal"
)

var levelMap = map[string]zapcore.Level{
    DebugLevel: zapcore.DebugLevel,
    InfoLevel:  zapcore.InfoLevel,
    WarnLevel:  zapcore.WarnLevel,
    ErrorLevel: zapcore.ErrorLevel,
    PanicLevel: zapcore.PanicLevel,
    FatalLevel: zapcore.FatalLevel,
}

func getLoggerLevel(lvl string) zapcore.Level {
    if level, ok := levelMap[lvl]; ok {
        return level
    }
    return zapcore.InfoLevel
}

/**
 * 打印日志方法
 */
func InfoWithCtx(ctx context.Context, format string, v ...interface{}) {
    traceID := TraceID(ctx)
    if traceID == "" {
        sugarLogger.Infof(format, v...)
        return
    }
    sugarLogger.With("trace_id", traceID).Infof(format, v...)
}

func ErrorWithCtx(ctx context.Context, format string, v ...interface{}) {
    traceID := TraceID(ctx)
    if traceID == "" {
        sugarLogger.Errorf(format, v...)
        return
    }
    sugarLogger.With("trace_id", traceID).Errorf(format, v...)
}

func TraceID(ctx context.Context) string {
    sp := opentracing.SpanFromContext(ctx)
    if sp == nil {
        return ""
    }
    sctx, ok := sp.Context().(jaeger.SpanContext)
    if !ok {
        return ""
    }
    return sctx.TraceID().String()
}

func getWriter(filename string, archiveTime int) io.Writer {
    if archiveTime < 5 { //日志存档时间小于5天时,默认更新为30天
        archiveTime = 30
    }
    var withMaxAge = time.Duration(archiveTime) * 24 * time.Hour
    hook, err := rotatelogs.New(
        filename+".%Y-%m-%d", //info.log.2021-10-22
        //rotatelogs.WithLinkName(filename),         //为最新的日志建立软连接
        rotatelogs.WithMaxAge(withMaxAge),         //默认保存30天
        rotatelogs.WithRotationTime(time.Hour*24), //切割频率 24小时
    )
    if err != nil {
        panic(err)
    }
    return hook
}

/**
 * conf.Path: 日志目录
 * conf.Level: 日志级别
 * serviceName: 服务名
 * addCaller: 是否开启打印文件名以及行号信息
 * outputModel: 日志打印方式 0:file 1:stdout 2:file+stdout
 * archiveTime: 存档时间(单位天,默认20天)
 */
func LoadConfig(conf *Config, serviceName string) *zap.Logger {
    // 1.设置级别
    logLevel := getLoggerLevel(conf.Level)
    infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
        return lvl >= logLevel
    })

    // 2.设置文件存储路径
    logPath := conf.Path
    if logPath == "" {
        logPath = "./log/info.log"
    }
    infoWriter := getWriter(logPath, conf.ArchiveTime)

    // 3.设置config
    config := zapcore.EncoderConfig{
        MessageKey:    "message",
        LevelKey:      "level",
        TimeKey:       "time",
        CallerKey:     "caller",
        StacktraceKey: "stack",
        EncodeLevel:   zapcore.CapitalLevelEncoder,
        EncodeCaller:  zapcore.ShortCallerEncoder,
        EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
            enc.AppendString(t.Format("2006-01-02T15:04:05-07:00"))
        },
        EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
            enc.AppendInt64(int64(d) / 1000000)
        },
    }
    encoder := zapcore.NewJSONEncoder(config)

    // 4.初始化日志操作句柄
    var core zapcore.Core
    if conf.OutputMode == OutputFileAndStdout {
        core = zapcore.NewTee(
            zapcore.NewCore(encoder, zapcore.AddSync(infoWriter), infoLevel),
            zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout)), logLevel),
        )
    } else if conf.OutputMode == OutputStdout {
        core = zapcore.NewTee(
            zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), logLevel),
        )
    } else {
        core = zapcore.NewTee(
            zapcore.NewCore(encoder, zapcore.AddSync(infoWriter), infoLevel),
        )
    }
    var option []zap.Option
    option = []zap.Option{zap.AddStacktrace(zap.ErrorLevel), zap.Fields(zap.String("service", serviceName))} //error级别会打印栈
    if conf.AddCaller {
        option = append(option, zap.AddCaller(), zap.AddCallerSkip(1))
    }
    return zap.New(core, option...)
}

//释放资源
func DeferLog() {
    _ = sugarLogger.Sync()
}

func main() {
    conf := &Config{
        Path:        "",
        Level:       InfoLevel,
        OutputMode:  2,
        ArchiveTime: 30,
        AddCaller:   false,
    }
    sugarLogger = LoadConfig(conf, "test-srv").Sugar()
    InfoWithCtx(context.Background(), "current time: %v", time.Now().Unix())
}

 

posted @ 2025-08-07 20:12  划水的猫  阅读(17)  评论(0)    收藏  举报