Files
tyapi-server/internal/shared/logger/level_logger.go
2025-07-28 15:21:37 +08:00

314 lines
8.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.

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 ""
}