package logging import ( "fmt" "io" "os" "path/filepath" "time" "github.com/rs/zerolog" ) // Logger 封装 zerolog,提供全局日志能力 type Logger struct { zl zerolog.Logger // 底层 zerolog 实例 level zerolog.Level // 当前日志级别 logFile *os.File // 日志文件句柄(可选) quiet bool // 静默模式:不输出到控制台 verbose bool // 详细模式:启用 Debug 级别 } // Options 创建 Logger 时的配置选项 type Options struct { Level string // 日志级别: trace|debug|info|warn|error|fatal (默认 info) LogFile string // 日志文件路径(空则不写文件) Verbose bool // 详细模式,覆盖 Level 为 debug Quiet bool // 静默模式,禁用控制台输出 NoColor bool // 禁用彩色输出 } // 全局 Logger 实例,初始化前使用零值安全地丢弃日志 var global = &Logger{zl: zerolog.Nop()} // G 返回全局 Logger 实例 func G() *Logger { return global } // Init 根据选项初始化全局 Logger,应在程序启动时调用一次 func Init(opts Options) (*Logger, error) { logger, err := New(opts) if err != nil { return nil, err } global = logger return logger, nil } // New 创建一个新的 Logger 实例 func New(opts Options) (*Logger, error) { // 解析日志级别 level := zerolog.InfoLevel if opts.Verbose { level = zerolog.DebugLevel } else if opts.Level != "" { parsed, err := zerolog.ParseLevel(opts.Level) if err != nil { return nil, fmt.Errorf("无效的日志级别 %q: %w", opts.Level, err) } level = parsed } // 构建输出目标(Writers) var writers []io.Writer // 控制台输出(非静默模式时启用) if !opts.Quiet { console := zerolog.ConsoleWriter{ Out: os.Stdout, TimeFormat: "2006-01-02 15:04:05", NoColor: opts.NoColor, } writers = append(writers, console) } // 文件输出(指定了日志文件时启用) var logFile *os.File if opts.LogFile != "" { dir := filepath.Dir(opts.LogFile) if err := os.MkdirAll(dir, 0o755); err != nil { return nil, fmt.Errorf("无法创建日志目录 %s: %w", dir, err) } f, err := os.OpenFile(opts.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644) if err != nil { return nil, fmt.Errorf("无法打开日志文件 %s: %w", opts.LogFile, err) } logFile = f // 文件始终使用 JSON 格式,不带颜色 writers = append(writers, f) } // 若无任何输出目标,丢弃所有日志 var writer io.Writer switch len(writers) { case 0: writer = io.Discard case 1: writer = writers[0] default: writer = zerolog.MultiLevelWriter(writers...) } zl := zerolog.New(writer). Level(level). With(). Timestamp(). Logger() return &Logger{ zl: zl, level: level, logFile: logFile, quiet: opts.Quiet, verbose: opts.Verbose, }, nil } // Close 关闭日志文件句柄(如果有的话),应在程序退出时调用 func (l *Logger) Close() { if l.logFile != nil { _ = l.logFile.Close() } } // ---- 各级别日志方法 ---- // Trace 输出 Trace 级别日志 func (l *Logger) Trace(msg string) { l.zl.Trace().Msg(msg) } // Tracef 输出格式化 Trace 级别日志 func (l *Logger) Tracef(format string, args ...any) { l.zl.Trace().Msgf(format, args...) } // Debug 输出 Debug 级别日志 func (l *Logger) Debug(msg string) { l.zl.Debug().Msg(msg) } // Debugf 输出格式化 Debug 级别日志 func (l *Logger) Debugf(format string, args ...any) { l.zl.Debug().Msgf(format, args...) } // Info 输出 Info 级别日志 func (l *Logger) Info(msg string) { l.zl.Info().Msg(msg) } // Infof 输出格式化 Info 级别日志 func (l *Logger) Infof(format string, args ...any) { l.zl.Info().Msgf(format, args...) } // Warn 输出 Warn 级别日志 func (l *Logger) Warn(msg string) { l.zl.Warn().Msg(msg) } // Warnf 输出格式化 Warn 级别日志 func (l *Logger) Warnf(format string, args ...any) { l.zl.Warn().Msgf(format, args...) } // Error 输出 Error 级别日志 func (l *Logger) Error(msg string) { l.zl.Error().Msg(msg) } // Errorf 输出格式化 Error 级别日志 func (l *Logger) Errorf(format string, args ...any) { l.zl.Error().Msgf(format, args...) } // Fatal 输出 Fatal 级别日志并终止程序 func (l *Logger) Fatal(msg string) { l.zl.Fatal().Msg(msg) } // Fatalf 输出格式化 Fatal 级别日志并终止程序 func (l *Logger) Fatalf(format string, args ...any) { l.zl.Fatal().Msgf(format, args...) } // ---- 带字段的结构化日志 ---- // WithField 附加一个键值对字段,返回可链式调用的事件 func (l *Logger) WithField(key string, value any) *zerolog.Event { return l.zl.Info().Any(key, value) } // WithError 附加一个错误对象,返回 Error 级别事件 func (l *Logger) WithError(err error) *zerolog.Event { return l.zl.Error().Err(err) } // WithDuration 附加一个耗时字段,常用于性能日志 func (l *Logger) WithDuration(d time.Duration) *zerolog.Event { return l.zl.Info().Dur("duration", d) } // ---- 工具方法 ---- // IsDebug 返回当前是否启用了 Debug 级别 func (l *Logger) IsDebug() bool { return l.level <= zerolog.DebugLevel } // IsQuiet 返回当前是否处于静默模式 func (l *Logger) IsQuiet() bool { return l.quiet } // IsVerbose 返回当前是否处于详细模式 func (l *Logger) IsVerbose() bool { return l.verbose }