// common/logger/logger.go package logger import ( "os" "sync" "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" ) // 全局单例日志实例 var ( _logger *zap.Logger once sync.Once ) // LoggerOptions 日志配置项 type LoggerOptions struct { Level string // 日志级别:debug/info/warn/error/panic/fatal Format string // 输出格式:console(控制台)/json(JSON) OutputPath string // 文件输出路径(如./logs/app.log) MaxSize int // 单个日志文件最大大小(MB) MaxBackups int // 最大保留日志文件数 MaxAge int // 最大保留天数 Compress bool // 是否压缩日志文件 ShowLine bool // 是否显示代码行号 ConsoleColor bool // 控制台是否显示彩色(开发环境用) ServiceName string // 服务名(多业务时区分日志归属,如user/order) StacktraceKey string // 堆栈信息键名 CallerSkip int // 调用栈跳过数(适配封装层,正确显示业务代码行号) FlushInterval time.Duration // 日志刷盘间隔 } // LoggerOption 选项模式函数类型 type LoggerOption func(*LoggerOptions) // defaultLoggerOptions 初始化默认配置 // 开发环境默认:控制台彩色日志、info级别、显示行号 func defaultLoggerOptions() *LoggerOptions { return &LoggerOptions{ Level: "info", Format: "console", OutputPath: "./logs/app.log", MaxSize: 100, // 单个文件100MB MaxBackups: 10, // 保留10个备份 MaxAge: 7, // 保留7天 Compress: true, // 压缩备份文件 ShowLine: true, // 显示行号 ConsoleColor: true, // 控制台彩色 ServiceName: "trading_assistant", // 默认服务名 StacktraceKey: "stacktrace", CallerSkip: 1, // 跳过当前封装层,正确显示业务代码行号 FlushInterval: 3 * time.Second, } } // 以下为配置项设置函数,支持链式调用 func WithLevel(level string) LoggerOption { return func(o *LoggerOptions) { o.Level = level } } func WithFormat(format string) LoggerOption { return func(o *LoggerOptions) { o.Format = format } } func WithOutputPath(path string) LoggerOption { return func(o *LoggerOptions) { o.OutputPath = path } } func WithServiceName(name string) LoggerOption { return func(o *LoggerOptions) { o.ServiceName = name } } func WithShowLine(show bool) LoggerOption { return func(o *LoggerOptions) { o.ShowLine = show } } func WithConsoleColor(color bool) LoggerOption { return func(o *LoggerOptions) { o.ConsoleColor = color } } // getZapLevel 转换日志级别为zapcore.Level func getZapLevel(level string) zapcore.Level { switch level { case "debug": return zapcore.DebugLevel case "warn": return zapcore.WarnLevel case "error": return zapcore.ErrorLevel case "panic": return zapcore.PanicLevel case "fatal": return zapcore.FatalLevel default: return zapcore.InfoLevel // 默认info级别 } } // getEncoder 获取日志编码器(console/json) func getEncoder(opts *LoggerOptions) zapcore.Encoder { // 日志基础配置:时间格式、服务名、调用栈等 encoderConfig := zapcore.EncoderConfig{ TimeKey: "time", LevelKey: "level", NameKey: "service", CallerKey: "caller", MessageKey: "msg", StacktraceKey: opts.StacktraceKey, LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.CapitalLevelEncoder, // 级别大写(INFO/ERROR) EncodeTime: zapcore.RFC3339TimeEncoder, // 时间格式RFC3339 EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, // 调用者格式:文件:行号 } // 开发环境:控制台彩色编码器 if opts.Format == "console" && opts.ConsoleColor { return zapcore.NewConsoleEncoder(encoderConfig) } // 生产环境:JSON编码器(便于日志收集分析,如ELK) return zapcore.NewJSONEncoder(encoderConfig) } // getWriteSyncer 获取日志写入器(文件+控制台) // 同时输出到文件和控制台,文件自动切割 func getWriteSyncer(opts *LoggerOptions) zapcore.WriteSyncer { // 日志文件切割配置(基于lumberjack) lumberjackLogger := &lumberjack.Logger{ Filename: opts.OutputPath, MaxSize: opts.MaxSize, MaxBackups: opts.MaxBackups, MaxAge: opts.MaxAge, Compress: opts.Compress, } // 同时输出到文件和控制台 return zapcore.NewMultiWriteSyncer( zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberjackLogger), ) } // InitLogger 初始化全局单例日志实例 func InitLogger(opts ...LoggerOption) *zap.Logger { once.Do(func() { // 加载默认配置 + 覆盖用户自定义配置 options := defaultLoggerOptions() for _, opt := range opts { opt(options) } // 1. 设置日志级别 level := getZapLevel(options.Level) core := zapcore.NewCore( getEncoder(options), // 编码器 getWriteSyncer(options), // 写入器 level, // 日志级别 ) // 2. 构建日志实例配置:是否显示调用者、堆栈信息 zapOpts := []zap.Option{zap.AddCallerSkip(options.CallerSkip)} if options.ShowLine { zapOpts = append(zapOpts, zap.AddCaller()) // 显示调用者(文件:行号) } // 错误级别及以上显示堆栈信息 zapOpts = append(zapOpts, zap.AddStacktrace(zapcore.ErrorLevel)) // 设置服务名 zapOpts = append(zapOpts, zap.Fields(zap.String("service", options.ServiceName))) // 3. 创建日志实例 _logger = zap.New(core, zapOpts...) // 4. 定时刷盘(避免日志驻留内存) go func() { ticker := time.NewTicker(options.FlushInterval) defer ticker.Stop() for range ticker.C { _ = _logger.Sync() } }() // 5. 应用退出时刷盘 os.Setenv("ZAP_FLUSH_ON_EXIT", "true") }) return _logger } // GetLogger 获取全局单例日志实例 func GetLogger() *zap.Logger { if _logger == nil { // 未初始化时,返回默认控制台日志(兜底) return InitLogger() } return _logger } // --------------- 轻量封装日志方法 --------------- // 简化业务层调用,无需每次获取logger实例 // 复杂日志(如带字段)可直接使用GetLogger()获取原生实例 // Debug 调试日志 func Debug(msg string, fields ...zap.Field) { GetLogger().Debug(msg, fields...) } // Info 信息日志 func Info(msg string, fields ...zap.Field) { GetLogger().Info(msg, fields...) } // Warn 警告日志 func Warn(msg string, fields ...zap.Field) { GetLogger().Warn(msg, fields...) } // Error 错误日志(带堆栈) func Error(msg string, fields ...zap.Field) { GetLogger().Error(msg, fields...) } // Panic 恐慌日志(打印后触发panic) func Panic(msg string, fields ...zap.Field) { GetLogger().Panic(msg, fields...) } // Fatal 致命日志(打印后退出程序) func Fatal(msg string, fields ...zap.Field) { GetLogger().Fatal(msg, fields...) } // Sync 手动刷盘(如重要操作后) func Sync() error { return GetLogger().Sync() }