Files
tyapi-server/internal/shared/logger/logger.go

323 lines
8.3 KiB
Go
Raw Normal View History

package logger
import (
"context"
2025-07-28 13:47:58 +08:00
"path/filepath"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
2025-07-28 13:47:58 +08:00
"gopkg.in/natefinch/lumberjack.v2"
)
// Logger 日志器接口 - 基于 Zap 官方推荐
type Logger interface {
// 基础日志方法
Debug(msg string, fields ...zapcore.Field)
Info(msg string, fields ...zapcore.Field)
Warn(msg string, fields ...zapcore.Field)
Error(msg string, fields ...zapcore.Field)
Fatal(msg string, fields ...zapcore.Field)
Panic(msg string, fields ...zapcore.Field)
// 结构化日志方法
With(fields ...zapcore.Field) Logger
WithContext(ctx context.Context) Logger
Named(name string) Logger
// 同步和清理
Sync() error
Core() zapcore.Core
// 获取原生 Zap Logger用于高级功能
GetZapLogger() *zap.Logger
}
// Config 日志配置 - 基于 Zap 官方配置结构
type Config struct {
// 基础配置
Level string `mapstructure:"level"` // 日志级别
Format string `mapstructure:"format"` // 输出格式 (json/console)
Output string `mapstructure:"output"` // 输出方式 (stdout/stderr/file)
LogDir string `mapstructure:"log_dir"` // 日志目录
UseDaily bool `mapstructure:"use_daily"` // 是否按日分包
UseColor bool `mapstructure:"use_color"` // 是否使用彩色输出仅console格式
// 文件配置
MaxSize int `mapstructure:"max_size"` // 单个文件最大大小(MB)
MaxBackups int `mapstructure:"max_backups"` // 最大备份文件数
MaxAge int `mapstructure:"max_age"` // 最大保留天数
Compress bool `mapstructure:"compress"` // 是否压缩
// 高级功能
EnableLevelSeparation bool `mapstructure:"enable_level_separation"` // 是否启用按级别分文件
LevelConfigs map[string]interface{} `mapstructure:"level_configs"` // 各级别配置(使用 interface{} 避免循环依赖)
EnableRequestLogging bool `mapstructure:"enable_request_logging"` // 是否启用请求日志
EnablePerformanceLog bool `mapstructure:"enable_performance_log"` // 是否启用性能日志
// 开发环境配置
Development bool `mapstructure:"development"` // 是否为开发环境
Sampling bool `mapstructure:"sampling"` // 是否启用采样
}
// ZapLogger Zap日志实现 - 基于官方推荐
type ZapLogger struct {
logger *zap.Logger
}
// NewLogger 创建新的日志实例 - 使用 Zap 官方推荐的方式
func NewLogger(config Config) (Logger, error) {
var logger *zap.Logger
var err error
// 根据环境创建合适的日志器
if config.Development {
logger, err = zap.NewDevelopment(
zap.AddCaller(),
zap.AddCallerSkip(1),
zap.AddStacktrace(zapcore.ErrorLevel),
)
} else {
logger, err = zap.NewProduction(
zap.AddCaller(),
zap.AddCallerSkip(1),
zap.AddStacktrace(zapcore.ErrorLevel),
)
}
if err != nil {
return nil, err
}
// 如果配置为文件输出,需要手动设置 Core
if config.Output == "file" {
writeSyncer, err := createFileWriteSyncer(config)
if err != nil {
return nil, err
}
// 创建新的 Core 并替换
encoder := getEncoder(config.Format, config)
level := getLogLevel(config.Level)
core := zapcore.NewCore(encoder, writeSyncer, level)
logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel))
}
return &ZapLogger{
logger: logger,
}, nil
}
// 实现 Logger 接口
func (z *ZapLogger) Debug(msg string, fields ...zapcore.Field) {
z.logger.Debug(msg, fields...)
2025-07-28 13:47:58 +08:00
}
func (z *ZapLogger) Info(msg string, fields ...zapcore.Field) {
z.logger.Info(msg, fields...)
}
func (z *ZapLogger) Warn(msg string, fields ...zapcore.Field) {
z.logger.Warn(msg, fields...)
}
func (z *ZapLogger) Error(msg string, fields ...zapcore.Field) {
z.logger.Error(msg, fields...)
}
func (z *ZapLogger) Fatal(msg string, fields ...zapcore.Field) {
z.logger.Fatal(msg, fields...)
}
func (z *ZapLogger) Panic(msg string, fields ...zapcore.Field) {
z.logger.Panic(msg, fields...)
}
func (z *ZapLogger) With(fields ...zapcore.Field) Logger {
return &ZapLogger{logger: z.logger.With(fields...)}
}
func (z *ZapLogger) WithContext(ctx context.Context) Logger {
// 从上下文提取字段
fields := extractFieldsFromContext(ctx)
return &ZapLogger{logger: z.logger.With(fields...)}
}
func (z *ZapLogger) Named(name string) Logger {
return &ZapLogger{logger: z.logger.Named(name)}
}
func (z *ZapLogger) Sync() error {
return z.logger.Sync()
}
func (z *ZapLogger) Core() zapcore.Core {
return z.logger.Core()
}
func (z *ZapLogger) GetZapLogger() *zap.Logger {
return z.logger
2025-07-13 16:36:20 +08:00
}
// 全局日志器 - 基于 Zap 官方推荐
var globalLogger *zap.Logger
// InitGlobalLogger 初始化全局日志器
func InitGlobalLogger(config Config) error {
var logger *zap.Logger
var err error
// 根据环境创建合适的日志器
if config.Development {
logger, err = zap.NewDevelopment(
zap.AddCaller(),
zap.AddCallerSkip(1),
zap.AddStacktrace(zapcore.ErrorLevel),
)
} else {
logger, err = zap.NewProduction(
zap.AddCaller(),
zap.AddCallerSkip(1),
zap.AddStacktrace(zapcore.ErrorLevel),
)
}
if err != nil {
return err
}
// 如果配置为文件输出,需要手动设置 Core
if config.Output == "file" {
writeSyncer, err := createFileWriteSyncer(config)
if err != nil {
return err
}
// 创建新的 Core 并替换
encoder := getEncoder(config.Format, config)
level := getLogLevel(config.Level)
core := zapcore.NewCore(encoder, writeSyncer, level)
logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel))
}
// 替换全局日志器
zap.ReplaceGlobals(logger)
globalLogger = logger
return nil
}
// GetGlobalLogger 获取全局日志器
func GetGlobalLogger() *zap.Logger {
if globalLogger == nil {
// 如果没有初始化,使用默认的生产环境配置
globalLogger = zap.Must(zap.NewProduction())
}
return globalLogger
}
// L 获取全局日志器Zap 官方推荐的方式)
func L() *zap.Logger {
return zap.L()
}
// 辅助函数
func getLogLevel(level string) zapcore.Level {
switch level {
case "debug":
return zapcore.DebugLevel
case "info":
return zapcore.InfoLevel
case "warn":
return zapcore.WarnLevel
case "error":
return zapcore.ErrorLevel
case "fatal":
return zapcore.FatalLevel
case "panic":
return zapcore.PanicLevel
default:
return zapcore.InfoLevel
}
}
func getEncoder(format string, config Config) zapcore.Encoder {
encoderConfig := getEncoderConfig(config)
if format == "console" {
return zapcore.NewConsoleEncoder(encoderConfig)
}
return zapcore.NewJSONEncoder(encoderConfig)
}
func getEncoderConfig(config Config) zapcore.EncoderConfig {
encoderConfig := zap.NewProductionEncoderConfig()
if config.Development {
encoderConfig = zap.NewDevelopmentEncoderConfig()
}
// 自定义时间格式
encoderConfig.TimeKey = "timestamp"
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
// 自定义级别格式
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
// 自定义调用者格式
encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
return encoderConfig
}
func createFileWriteSyncer(config Config) (zapcore.WriteSyncer, error) {
// 使用 lumberjack 进行日志轮转
rotator := &lumberjack.Logger{
Filename: getLogFilePath(config),
MaxSize: config.MaxSize,
MaxBackups: config.MaxBackups,
MaxAge: config.MaxAge,
Compress: config.Compress,
}
return zapcore.AddSync(rotator), nil
}
func getLogFilePath(config Config) string {
if config.UseDaily {
// 按日期分包
date := time.Now().Format("2006-01-02")
return filepath.Join(config.LogDir, date, "app.log")
}
return filepath.Join(config.LogDir, "app.log")
}
func extractFieldsFromContext(ctx context.Context) []zapcore.Field {
var fields []zapcore.Field
// 提取请求ID
if requestID := ctx.Value("request_id"); requestID != nil {
if id, ok := requestID.(string); ok {
fields = append(fields, zap.String("request_id", id))
}
}
// 提取用户ID
if userID := ctx.Value("user_id"); userID != nil {
if id, ok := userID.(string); ok {
fields = append(fields, zap.String("user_id", id))
}
}
// 提取跟踪ID
if traceID := ctx.Value("trace_id"); traceID != nil {
if id, ok := traceID.(string); ok {
fields = append(fields, zap.String("trace_id", id))
}
}
return fields
}