package logger import ( "os" "path/filepath" "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) // Config 日志配置 type Config struct { Level string // 日志级别: debug, info, warn, error Path string // 日志文件目录,为空则仅输出到 stdout MaxSize int // 单个日志文件最大尺寸 (MB) MaxBackups int // 保留的旧日志文件最大数量 MaxAge int // 保留旧日志文件的最大天数 Compress bool // 是否压缩旧日志文件 } // New 根据配置创建 zap.Logger // 如果 Path 为空,仅输出到 stdout; // 如果 Path 已设置,同时输出到 stdout 和文件(文件使用 JSON 格式,stdout 使用 console 格式) func New(cfg Config) (*zap.Logger, error) { level, err := parseLevel(cfg.Level) if err != nil { return nil, err } // stdout encoder — console 格式 stdoutEncoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{ TimeKey: "ts", LevelKey: "level", NameKey: "logger", CallerKey: "caller", FunctionKey: zapcore.OmitKey, MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.CapitalColorLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, }) stdoutCore := zapcore.NewCore( stdoutEncoder, zapcore.AddSync(os.Stdout), level, ) // 仅 stdout 模式 if cfg.Path == "" { return zap.New(stdoutCore, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)), nil } // 文件 encoder — JSON 格式 fileEncoder := zapcore.NewJSONEncoder(zapcore.EncoderConfig{ TimeKey: "ts", LevelKey: "level", NameKey: "logger", CallerKey: "caller", FunctionKey: zapcore.OmitKey, MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, }) rotateWriter := newRotateWriter(cfg) fileCore := zapcore.NewCore( fileEncoder, zapcore.AddSync(rotateWriter), level, ) core := zapcore.NewTee(stdoutCore, fileCore) return zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)), nil } // parseLevel 将字符串解析为 zapcore.Level func parseLevel(s string) (zapcore.Level, error) { switch s { case "debug": return zapcore.DebugLevel, nil case "info": return zapcore.InfoLevel, nil case "warn": return zapcore.WarnLevel, nil case "error": return zapcore.ErrorLevel, nil default: return zapcore.InfoLevel, nil } } // logFileName 生成当日日志文件名: nex-YYYY-MM-DD.log func logFileName() string { return "nex-" + time.Now().Format("2006-01-02") + ".log" } // logFilePath 拼接完整日志文件路径 func logFilePath(dir string) string { return filepath.Join(dir, logFileName()) }