314 lines
8.0 KiB
Go
314 lines
8.0 KiB
Go
|
|
package logger
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"fmt"
|
|||
|
|
"os"
|
|||
|
|
"path/filepath"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"go.uber.org/zap"
|
|||
|
|
"go.uber.org/zap/zapcore"
|
|||
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// LevelLoggerConfig 按级别分文件的日志配置
|
|||
|
|
type LevelLoggerConfig struct {
|
|||
|
|
BaseConfig Config
|
|||
|
|
// 是否启用按级别分文件
|
|||
|
|
EnableLevelSeparation bool
|
|||
|
|
// 各级别日志文件配置
|
|||
|
|
LevelConfigs map[zapcore.Level]LevelFileConfig
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// LevelFileConfig 单个级别文件配置
|
|||
|
|
type LevelFileConfig struct {
|
|||
|
|
MaxSize int // 单个文件最大大小(MB)
|
|||
|
|
MaxBackups int // 最大备份文件数
|
|||
|
|
MaxAge int // 最大保留天数
|
|||
|
|
Compress bool // 是否压缩
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// LevelLogger 按级别分文件的日志器
|
|||
|
|
type LevelLogger struct {
|
|||
|
|
loggers map[zapcore.Level]*zap.Logger
|
|||
|
|
config LevelLoggerConfig
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewLevelLogger 创建按级别分文件的日志器
|
|||
|
|
func NewLevelLogger(config LevelLoggerConfig) (*LevelLogger, error) {
|
|||
|
|
if !config.EnableLevelSeparation {
|
|||
|
|
// 如果不启用按级别分文件,使用普通日志器
|
|||
|
|
normalLogger, err := NewLogger(config.BaseConfig)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 转换为LevelLogger格式
|
|||
|
|
zapLogger := normalLogger.(*ZapLogger).GetZapLogger()
|
|||
|
|
return &LevelLogger{
|
|||
|
|
loggers: map[zapcore.Level]*zap.Logger{
|
|||
|
|
zapcore.DebugLevel: zapLogger,
|
|||
|
|
zapcore.InfoLevel: zapLogger,
|
|||
|
|
zapcore.WarnLevel: zapLogger,
|
|||
|
|
zapcore.ErrorLevel: zapLogger,
|
|||
|
|
zapcore.FatalLevel: zapLogger,
|
|||
|
|
zapcore.PanicLevel: zapLogger,
|
|||
|
|
},
|
|||
|
|
config: config,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置默认级别配置
|
|||
|
|
if config.LevelConfigs == nil {
|
|||
|
|
config.LevelConfigs = getDefaultLevelConfigs()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确保日志目录存在
|
|||
|
|
if err := os.MkdirAll(config.BaseConfig.LogDir, 0755); err != nil {
|
|||
|
|
return nil, fmt.Errorf("创建日志目录失败: %w", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 为每个级别创建独立的日志器
|
|||
|
|
loggers := make(map[zapcore.Level]*zap.Logger)
|
|||
|
|
|
|||
|
|
for level := range config.LevelConfigs {
|
|||
|
|
logger, err := createLevelLogger(level, config)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, fmt.Errorf("创建级别日志器失败 [%s]: %w", level.String(), err)
|
|||
|
|
}
|
|||
|
|
loggers[level] = logger
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return &LevelLogger{
|
|||
|
|
loggers: loggers,
|
|||
|
|
config: config,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// getDefaultLevelConfigs 获取默认级别配置
|
|||
|
|
func getDefaultLevelConfigs() map[zapcore.Level]LevelFileConfig {
|
|||
|
|
return map[zapcore.Level]LevelFileConfig{
|
|||
|
|
zapcore.DebugLevel: {
|
|||
|
|
MaxSize: 50, // 50MB
|
|||
|
|
MaxBackups: 3,
|
|||
|
|
MaxAge: 7, // 7天
|
|||
|
|
Compress: true,
|
|||
|
|
},
|
|||
|
|
zapcore.InfoLevel: {
|
|||
|
|
MaxSize: 100, // 100MB
|
|||
|
|
MaxBackups: 5,
|
|||
|
|
MaxAge: 30, // 30天
|
|||
|
|
Compress: true,
|
|||
|
|
},
|
|||
|
|
zapcore.WarnLevel: {
|
|||
|
|
MaxSize: 100, // 100MB
|
|||
|
|
MaxBackups: 5,
|
|||
|
|
MaxAge: 30, // 30天
|
|||
|
|
Compress: true,
|
|||
|
|
},
|
|||
|
|
zapcore.ErrorLevel: {
|
|||
|
|
MaxSize: 200, // 200MB
|
|||
|
|
MaxBackups: 10,
|
|||
|
|
MaxAge: 90, // 90天
|
|||
|
|
Compress: true,
|
|||
|
|
},
|
|||
|
|
zapcore.FatalLevel: {
|
|||
|
|
MaxSize: 100, // 100MB
|
|||
|
|
MaxBackups: 10,
|
|||
|
|
MaxAge: 365, // 1年
|
|||
|
|
Compress: true,
|
|||
|
|
},
|
|||
|
|
zapcore.PanicLevel: {
|
|||
|
|
MaxSize: 100, // 100MB
|
|||
|
|
MaxBackups: 10,
|
|||
|
|
MaxAge: 365, // 1年
|
|||
|
|
Compress: true,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// createLevelLogger 为单个级别创建日志器
|
|||
|
|
func createLevelLogger(level zapcore.Level, config LevelLoggerConfig) (*zap.Logger, error) {
|
|||
|
|
levelConfig := config.LevelConfigs[level]
|
|||
|
|
|
|||
|
|
// 创建编码器
|
|||
|
|
encoderConfig := getEncoderConfig()
|
|||
|
|
var encoder zapcore.Encoder
|
|||
|
|
if config.BaseConfig.Format == "json" {
|
|||
|
|
encoder = zapcore.NewJSONEncoder(encoderConfig)
|
|||
|
|
} else {
|
|||
|
|
encoder = zapcore.NewConsoleEncoder(encoderConfig)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建文件输出
|
|||
|
|
writeSyncer, err := createLevelFileWriteSyncer(level, config.BaseConfig, levelConfig)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建核心
|
|||
|
|
core := zapcore.NewCore(encoder, writeSyncer, level)
|
|||
|
|
|
|||
|
|
// 创建日志器
|
|||
|
|
logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
|
|||
|
|
|
|||
|
|
return logger, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// createLevelFileWriteSyncer 创建级别文件输出同步器
|
|||
|
|
func createLevelFileWriteSyncer(level zapcore.Level, baseConfig Config, levelConfig LevelFileConfig) (zapcore.WriteSyncer, error) {
|
|||
|
|
// 构建日志文件路径
|
|||
|
|
var logFilePath string
|
|||
|
|
if baseConfig.UseDaily {
|
|||
|
|
// 按日分包:logs/2024-01-01/error.log
|
|||
|
|
today := time.Now().Format("2006-01-02")
|
|||
|
|
dailyDir := filepath.Join(baseConfig.LogDir, today)
|
|||
|
|
if err := os.MkdirAll(dailyDir, 0755); err != nil {
|
|||
|
|
return nil, fmt.Errorf("创建日期目录失败: %w", err)
|
|||
|
|
}
|
|||
|
|
logFilePath = filepath.Join(dailyDir, fmt.Sprintf("%s.log", level.String()))
|
|||
|
|
} else {
|
|||
|
|
// 传统方式:logs/error.log
|
|||
|
|
logFilePath = filepath.Join(baseConfig.LogDir, fmt.Sprintf("%s.log", level.String()))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建lumberjack日志轮转器
|
|||
|
|
lumberJackLogger := &lumberjack.Logger{
|
|||
|
|
Filename: logFilePath,
|
|||
|
|
MaxSize: levelConfig.MaxSize,
|
|||
|
|
MaxBackups: levelConfig.MaxBackups,
|
|||
|
|
MaxAge: levelConfig.MaxAge,
|
|||
|
|
Compress: levelConfig.Compress,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return zapcore.AddSync(lumberJackLogger), nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Debug 调试日志
|
|||
|
|
func (l *LevelLogger) Debug(msg string, fields ...zapcore.Field) {
|
|||
|
|
if logger, exists := l.loggers[zapcore.DebugLevel]; exists {
|
|||
|
|
logger.Debug(msg, fields...)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Info 信息日志
|
|||
|
|
func (l *LevelLogger) Info(msg string, fields ...zapcore.Field) {
|
|||
|
|
if logger, exists := l.loggers[zapcore.InfoLevel]; exists {
|
|||
|
|
logger.Info(msg, fields...)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Warn 警告日志
|
|||
|
|
func (l *LevelLogger) Warn(msg string, fields ...zapcore.Field) {
|
|||
|
|
if logger, exists := l.loggers[zapcore.WarnLevel]; exists {
|
|||
|
|
logger.Warn(msg, fields...)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Error 错误日志
|
|||
|
|
func (l *LevelLogger) Error(msg string, fields ...zapcore.Field) {
|
|||
|
|
if logger, exists := l.loggers[zapcore.ErrorLevel]; exists {
|
|||
|
|
logger.Error(msg, fields...)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Fatal 致命错误日志
|
|||
|
|
func (l *LevelLogger) Fatal(msg string, fields ...zapcore.Field) {
|
|||
|
|
if logger, exists := l.loggers[zapcore.FatalLevel]; exists {
|
|||
|
|
logger.Fatal(msg, fields...)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Panic 恐慌日志
|
|||
|
|
func (l *LevelLogger) Panic(msg string, fields ...zapcore.Field) {
|
|||
|
|
if logger, exists := l.loggers[zapcore.PanicLevel]; exists {
|
|||
|
|
logger.Panic(msg, fields...)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// With 添加字段
|
|||
|
|
func (l *LevelLogger) With(fields ...zapcore.Field) Logger {
|
|||
|
|
// 为每个级别创建带字段的日志器
|
|||
|
|
newLoggers := make(map[zapcore.Level]*zap.Logger)
|
|||
|
|
for level, logger := range l.loggers {
|
|||
|
|
newLoggers[level] = logger.With(fields...)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return &LevelLogger{
|
|||
|
|
loggers: newLoggers,
|
|||
|
|
config: l.config,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// WithContext 从上下文添加字段
|
|||
|
|
func (l *LevelLogger) WithContext(ctx context.Context) Logger {
|
|||
|
|
// 从上下文中提取常用字段
|
|||
|
|
fields := []zapcore.Field{}
|
|||
|
|
|
|||
|
|
if traceID := getTraceIDFromContextLevel(ctx); traceID != "" {
|
|||
|
|
fields = append(fields, zap.String("trace_id", traceID))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if userID := getUserIDFromContextLevel(ctx); userID != "" {
|
|||
|
|
fields = append(fields, zap.String("user_id", userID))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if requestID := getRequestIDFromContextLevel(ctx); requestID != "" {
|
|||
|
|
fields = append(fields, zap.String("request_id", requestID))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return l.With(fields...)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Sync 同步日志
|
|||
|
|
func (l *LevelLogger) Sync() error {
|
|||
|
|
var lastErr error
|
|||
|
|
for _, logger := range l.loggers {
|
|||
|
|
if err := logger.Sync(); err != nil {
|
|||
|
|
lastErr = err
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return lastErr
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetLevelLogger 获取指定级别的日志器
|
|||
|
|
func (l *LevelLogger) GetLevelLogger(level zapcore.Level) *zap.Logger {
|
|||
|
|
if logger, exists := l.loggers[level]; exists {
|
|||
|
|
return logger
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetLoggers 获取所有级别的日志器
|
|||
|
|
func (l *LevelLogger) GetLoggers() map[zapcore.Level]*zap.Logger {
|
|||
|
|
return l.loggers
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 辅助函数(从logger.go复制)
|
|||
|
|
func getTraceIDFromContextLevel(ctx context.Context) string {
|
|||
|
|
if traceID := ctx.Value("trace_id"); traceID != nil {
|
|||
|
|
if id, ok := traceID.(string); ok {
|
|||
|
|
return id
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func getUserIDFromContextLevel(ctx context.Context) string {
|
|||
|
|
if userID := ctx.Value("user_id"); userID != nil {
|
|||
|
|
if id, ok := userID.(string); ok {
|
|||
|
|
return id
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func getRequestIDFromContextLevel(ctx context.Context) string {
|
|||
|
|
if requestID := ctx.Value("request_id"); requestID != nil {
|
|||
|
|
if id, ok := requestID.(string); ok {
|
|||
|
|
return id
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ""
|
|||
|
|
}
|