package logger import ( "context" "os" "path/filepath" "strings" "github.com/gofiber/fiber/v3" "go.uber.org/zap" "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" ) var Log *zap.Logger const TraceIDKey = "request_id" // ====================== // 配置结构 // ====================== type Config struct { Dir string // 日志目录 Level string // debug info warn error MaxSize int // MB MaxBackups int MaxAge int // 天 Compress bool // 是否压缩 } // ====================== // 初始化 // ====================== func Init(cfg Config) { if cfg.Dir == "" { cfg.Dir = "logs" } if cfg.Level == "" { cfg.Level = "info" } level := parseLevel(cfg.Level) encoderConfig := zapcore.EncoderConfig{ TimeKey: "time", LevelKey: "level", MessageKey: "msg", CallerKey: "caller", EncodeLevel: zapcore.CapitalLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } encoder := zapcore.NewJSONEncoder(encoderConfig) // 控制台 consoleWriter := zapcore.AddSync(os.Stdout) // 文件 writer(info) infoWriter := getWriter(cfg, "info") // 文件 writer(error) errorWriter := getWriter(cfg, "error") // info core infoCore := zapcore.NewCore( encoder, zapcore.NewMultiWriteSyncer(consoleWriter, infoWriter), zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl < zapcore.ErrorLevel && lvl >= level }), ) // error core errorCore := zapcore.NewCore( encoder, zapcore.NewMultiWriteSyncer(consoleWriter, errorWriter), zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl >= zapcore.ErrorLevel }), ) core := zapcore.NewTee(infoCore, errorCore) Log = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) } // ====================== // lumberjack writer // ====================== func getWriter(cfg Config, level string) zapcore.WriteSyncer { dir := filepath.Join(cfg.Dir, level) _ = os.MkdirAll(dir, os.ModePerm) return zapcore.AddSync(&lumberjack.Logger{ Filename: filepath.Join(dir, level+".log"), MaxSize: cfg.MaxSize, MaxBackups: cfg.MaxBackups, MaxAge: cfg.MaxAge, Compress: cfg.Compress, }) } // ====================== // level 解析 // ====================== func parseLevel(lvl string) zapcore.Level { switch strings.ToLower(lvl) { case "debug": return zap.DebugLevel case "warn": return zap.WarnLevel case "error": return zap.ErrorLevel default: return zap.InfoLevel } } // ====================== // trace 支持(核心) // ====================== // Fiber 中使用 func Ctx(c fiber.Ctx) *zap.Logger { if c == nil { return Log } traceID, _ := c.Locals(TraceIDKey).(string) if traceID == "" { return Log } return Log.With(zap.String("trace_id", traceID)) } // Service / Repo 使用 func FromCtx(ctx context.Context) *zap.Logger { if ctx == nil { return Log } traceID, _ := ctx.Value(TraceIDKey).(string) if traceID == "" { return Log } return Log.With(zap.String("trace_id", traceID)) } // 注入 trace func WithTrace(ctx context.Context, traceID string) context.Context { return context.WithValue(ctx, TraceIDKey, traceID) } // ====================== // 优雅退出 // ====================== func Sync() { if Log != nil { _ = Log.Sync() } }