Files
trading_assistant/trading_assistant_api/common/logger/logger.go
2026-02-06 16:48:36 +08:00

240 lines
7.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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控制台/jsonJSON
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()
}