f
This commit is contained in:
214
internal/shared/logger/enhanced_logger.go
Normal file
214
internal/shared/logger/enhanced_logger.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// LogLevel 日志级别
|
||||
type LogLevel string
|
||||
|
||||
const (
|
||||
DebugLevel LogLevel = "debug"
|
||||
InfoLevel LogLevel = "info"
|
||||
WarnLevel LogLevel = "warn"
|
||||
ErrorLevel LogLevel = "error"
|
||||
)
|
||||
|
||||
// LogContext 日志上下文
|
||||
type LogContext struct {
|
||||
RequestID string
|
||||
UserID string
|
||||
TraceID string
|
||||
OperationName string
|
||||
Layer string // repository/service/handler
|
||||
Component string
|
||||
}
|
||||
|
||||
// ContextualLogger 上下文感知的日志器
|
||||
type ContextualLogger struct {
|
||||
logger *zap.Logger
|
||||
ctx LogContext
|
||||
}
|
||||
|
||||
// NewContextualLogger 创建上下文日志器
|
||||
func NewContextualLogger(logger *zap.Logger) *ContextualLogger {
|
||||
return &ContextualLogger{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// WithContext 添加上下文信息
|
||||
func (l *ContextualLogger) WithContext(ctx context.Context) *ContextualLogger {
|
||||
logCtx := LogContext{}
|
||||
|
||||
// 从context中提取常用字段
|
||||
if requestID := getStringFromContext(ctx, "request_id"); requestID != "" {
|
||||
logCtx.RequestID = requestID
|
||||
}
|
||||
if userID := getStringFromContext(ctx, "user_id"); userID != "" {
|
||||
logCtx.UserID = userID
|
||||
}
|
||||
if traceID := getStringFromContext(ctx, "trace_id"); traceID != "" {
|
||||
logCtx.TraceID = traceID
|
||||
}
|
||||
|
||||
return &ContextualLogger{
|
||||
logger: l.logger,
|
||||
ctx: logCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// WithLayer 设置层级信息
|
||||
func (l *ContextualLogger) WithLayer(layer string) *ContextualLogger {
|
||||
newCtx := l.ctx
|
||||
newCtx.Layer = layer
|
||||
return &ContextualLogger{
|
||||
logger: l.logger,
|
||||
ctx: newCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// WithComponent 设置组件信息
|
||||
func (l *ContextualLogger) WithComponent(component string) *ContextualLogger {
|
||||
newCtx := l.ctx
|
||||
newCtx.Component = component
|
||||
return &ContextualLogger{
|
||||
logger: l.logger,
|
||||
ctx: newCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// WithOperation 设置操作名称
|
||||
func (l *ContextualLogger) WithOperation(operation string) *ContextualLogger {
|
||||
newCtx := l.ctx
|
||||
newCtx.OperationName = operation
|
||||
return &ContextualLogger{
|
||||
logger: l.logger,
|
||||
ctx: newCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 构建基础字段
|
||||
func (l *ContextualLogger) buildBaseFields() []zapcore.Field {
|
||||
fields := []zapcore.Field{}
|
||||
|
||||
if l.ctx.RequestID != "" {
|
||||
fields = append(fields, zap.String("request_id", l.ctx.RequestID))
|
||||
}
|
||||
if l.ctx.UserID != "" {
|
||||
fields = append(fields, zap.String("user_id", l.ctx.UserID))
|
||||
}
|
||||
if l.ctx.TraceID != "" {
|
||||
fields = append(fields, zap.String("trace_id", l.ctx.TraceID))
|
||||
}
|
||||
if l.ctx.Layer != "" {
|
||||
fields = append(fields, zap.String("layer", l.ctx.Layer))
|
||||
}
|
||||
if l.ctx.Component != "" {
|
||||
fields = append(fields, zap.String("component", l.ctx.Component))
|
||||
}
|
||||
if l.ctx.OperationName != "" {
|
||||
fields = append(fields, zap.String("operation", l.ctx.OperationName))
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// LogTechnicalError 记录技术性错误(Repository层)
|
||||
func (l *ContextualLogger) LogTechnicalError(msg string, err error, fields ...zapcore.Field) {
|
||||
allFields := l.buildBaseFields()
|
||||
allFields = append(allFields, zap.Error(err))
|
||||
allFields = append(allFields, zap.String("error_type", "technical"))
|
||||
allFields = append(allFields, fields...)
|
||||
|
||||
l.logger.Error(msg, allFields...)
|
||||
}
|
||||
|
||||
// LogBusinessWarn 记录业务警告(Service层)
|
||||
func (l *ContextualLogger) LogBusinessWarn(msg string, fields ...zapcore.Field) {
|
||||
allFields := l.buildBaseFields()
|
||||
allFields = append(allFields, zap.String("log_type", "business"))
|
||||
allFields = append(allFields, fields...)
|
||||
|
||||
l.logger.Warn(msg, allFields...)
|
||||
}
|
||||
|
||||
// LogBusinessInfo 记录业务信息(Service层)
|
||||
func (l *ContextualLogger) LogBusinessInfo(msg string, fields ...zapcore.Field) {
|
||||
allFields := l.buildBaseFields()
|
||||
allFields = append(allFields, zap.String("log_type", "business"))
|
||||
allFields = append(allFields, fields...)
|
||||
|
||||
l.logger.Info(msg, allFields...)
|
||||
}
|
||||
|
||||
// LogUserAction 记录用户行为(Handler层)
|
||||
func (l *ContextualLogger) LogUserAction(msg string, fields ...zapcore.Field) {
|
||||
allFields := l.buildBaseFields()
|
||||
allFields = append(allFields, zap.String("log_type", "user_action"))
|
||||
allFields = append(allFields, fields...)
|
||||
|
||||
l.logger.Info(msg, allFields...)
|
||||
}
|
||||
|
||||
// LogRequestFailed 记录请求失败(Handler层)
|
||||
func (l *ContextualLogger) LogRequestFailed(msg string, errorType string, fields ...zapcore.Field) {
|
||||
allFields := l.buildBaseFields()
|
||||
allFields = append(allFields, zap.String("log_type", "request_failed"))
|
||||
allFields = append(allFields, zap.String("error_category", errorType))
|
||||
allFields = append(allFields, fields...)
|
||||
|
||||
l.logger.Info(msg, allFields...)
|
||||
}
|
||||
|
||||
// getStringFromContext 从上下文获取字符串值
|
||||
func getStringFromContext(ctx context.Context, key string) string {
|
||||
if value := ctx.Value(key); value != nil {
|
||||
if str, ok := value.(string); ok {
|
||||
return str
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ErrorCategory 错误分类
|
||||
type ErrorCategory string
|
||||
|
||||
const (
|
||||
DatabaseError ErrorCategory = "database"
|
||||
NetworkError ErrorCategory = "network"
|
||||
ValidationError ErrorCategory = "validation"
|
||||
BusinessError ErrorCategory = "business"
|
||||
AuthError ErrorCategory = "auth"
|
||||
ExternalAPIError ErrorCategory = "external_api"
|
||||
)
|
||||
|
||||
// CategorizeError 错误分类
|
||||
func CategorizeError(err error) ErrorCategory {
|
||||
errMsg := strings.ToLower(err.Error())
|
||||
|
||||
switch {
|
||||
case strings.Contains(errMsg, "database") ||
|
||||
strings.Contains(errMsg, "sql") ||
|
||||
strings.Contains(errMsg, "gorm"):
|
||||
return DatabaseError
|
||||
case strings.Contains(errMsg, "network") ||
|
||||
strings.Contains(errMsg, "connection") ||
|
||||
strings.Contains(errMsg, "timeout"):
|
||||
return NetworkError
|
||||
case strings.Contains(errMsg, "validation") ||
|
||||
strings.Contains(errMsg, "invalid") ||
|
||||
strings.Contains(errMsg, "format"):
|
||||
return ValidationError
|
||||
case strings.Contains(errMsg, "unauthorized") ||
|
||||
strings.Contains(errMsg, "forbidden") ||
|
||||
strings.Contains(errMsg, "token"):
|
||||
return AuthError
|
||||
default:
|
||||
return BusinessError
|
||||
}
|
||||
}
|
||||
147
internal/shared/logger/factory.go
Normal file
147
internal/shared/logger/factory.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// LoggerFactory 日志器工厂 - 基于 Zap 官方推荐
|
||||
type LoggerFactory struct {
|
||||
config Config
|
||||
}
|
||||
|
||||
// NewLoggerFactory 创建日志器工厂
|
||||
func NewLoggerFactory(config Config) *LoggerFactory {
|
||||
return &LoggerFactory{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateLogger 创建普通日志器
|
||||
func (f *LoggerFactory) CreateLogger() (Logger, error) {
|
||||
return NewLogger(f.config)
|
||||
}
|
||||
|
||||
// CreateProductionLogger 创建生产环境日志器 - 使用 Zap 官方推荐
|
||||
func (f *LoggerFactory) CreateProductionLogger() (*zap.Logger, error) {
|
||||
// 使用 Zap 官方的生产环境预设
|
||||
logger, err := zap.NewProduction(
|
||||
zap.AddCaller(),
|
||||
zap.AddCallerSkip(1),
|
||||
zap.AddStacktrace(zapcore.ErrorLevel),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 如果配置为文件输出,需要手动设置 Core
|
||||
if f.config.Output == "file" {
|
||||
writeSyncer, err := createFileWriteSyncer(f.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建新的 Core 并替换
|
||||
encoder := getEncoder(f.config.Format, f.config)
|
||||
level := getLogLevel(f.config.Level)
|
||||
core := zapcore.NewCore(encoder, writeSyncer, level)
|
||||
logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel))
|
||||
}
|
||||
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
// CreateDevelopmentLogger 创建开发环境日志器 - 使用 Zap 官方推荐
|
||||
func (f *LoggerFactory) CreateDevelopmentLogger() (*zap.Logger, error) {
|
||||
// 使用 Zap 官方的开发环境预设
|
||||
logger, err := zap.NewDevelopment(
|
||||
zap.AddCaller(),
|
||||
zap.AddCallerSkip(1),
|
||||
zap.AddStacktrace(zapcore.ErrorLevel),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 如果配置为文件输出,需要手动设置 Core
|
||||
if f.config.Output == "file" {
|
||||
writeSyncer, err := createFileWriteSyncer(f.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建新的 Core 并替换
|
||||
encoder := getEncoder(f.config.Format, f.config)
|
||||
level := getLogLevel(f.config.Level)
|
||||
core := zapcore.NewCore(encoder, writeSyncer, level)
|
||||
logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel))
|
||||
}
|
||||
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
// CreateCustomLogger 创建自定义配置日志器
|
||||
func (f *LoggerFactory) CreateCustomLogger() (*zap.Logger, error) {
|
||||
// 根据环境选择预设
|
||||
if f.config.Development {
|
||||
return f.CreateDevelopmentLogger()
|
||||
}
|
||||
return f.CreateProductionLogger()
|
||||
}
|
||||
|
||||
// CreateLoggerByEnvironment 根据环境创建合适的日志器
|
||||
func (f *LoggerFactory) CreateLoggerByEnvironment() (*zap.Logger, error) {
|
||||
if f.config.Development {
|
||||
return f.CreateDevelopmentLogger()
|
||||
}
|
||||
return f.CreateProductionLogger()
|
||||
}
|
||||
|
||||
// CreateLoggerWithOptions 使用选项模式创建日志器
|
||||
func (f *LoggerFactory) CreateLoggerWithOptions(options ...LoggerOption) (*zap.Logger, error) {
|
||||
// 应用选项
|
||||
for _, option := range options {
|
||||
option(&f.config)
|
||||
}
|
||||
|
||||
// 创建日志器
|
||||
return f.CreateLoggerByEnvironment()
|
||||
}
|
||||
|
||||
// LoggerOption 日志器选项函数
|
||||
type LoggerOption func(*Config)
|
||||
|
||||
// WithLevel 设置日志级别
|
||||
func WithLevel(level string) LoggerOption {
|
||||
return func(config *Config) {
|
||||
config.Level = level
|
||||
}
|
||||
}
|
||||
|
||||
// WithFormat 设置日志格式
|
||||
func WithFormat(format string) LoggerOption {
|
||||
return func(config *Config) {
|
||||
config.Format = format
|
||||
}
|
||||
}
|
||||
|
||||
// WithOutput 设置输出目标
|
||||
func WithOutput(output string) LoggerOption {
|
||||
return func(config *Config) {
|
||||
config.Output = output
|
||||
}
|
||||
}
|
||||
|
||||
// WithDevelopment 设置是否为开发环境
|
||||
func WithDevelopment(development bool) LoggerOption {
|
||||
return func(config *Config) {
|
||||
config.Development = development
|
||||
}
|
||||
}
|
||||
|
||||
// WithColor 设置是否使用彩色输出
|
||||
func WithColor(useColor bool) LoggerOption {
|
||||
return func(config *Config) {
|
||||
config.UseColor = useColor
|
||||
}
|
||||
}
|
||||
228
internal/shared/logger/level_logger.go
Normal file
228
internal/shared/logger/level_logger.go
Normal file
@@ -0,0 +1,228 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
// LevelLogger 级别分文件日志器 - 基于 Zap 官方推荐
|
||||
type LevelLogger struct {
|
||||
logger *zap.Logger
|
||||
levelLoggers map[zapcore.Level]*zap.Logger
|
||||
config LevelLoggerConfig
|
||||
}
|
||||
|
||||
// LevelLoggerConfig 级别分文件日志器配置
|
||||
type LevelLoggerConfig struct {
|
||||
BaseConfig Config
|
||||
EnableLevelSeparation bool
|
||||
LevelConfigs map[zapcore.Level]LevelFileConfig
|
||||
}
|
||||
|
||||
// LevelFileConfig 单个级别文件配置
|
||||
type LevelFileConfig struct {
|
||||
MaxSize int `mapstructure:"max_size"`
|
||||
MaxBackups int `mapstructure:"max_backups"`
|
||||
MaxAge int `mapstructure:"max_age"`
|
||||
Compress bool `mapstructure:"compress"`
|
||||
}
|
||||
|
||||
// NewLevelLogger 创建级别分文件日志器
|
||||
func NewLevelLogger(config LevelLoggerConfig) (Logger, error) {
|
||||
// 根据环境创建基础日志器
|
||||
var baseLogger *zap.Logger
|
||||
var err error
|
||||
|
||||
if config.BaseConfig.Development {
|
||||
baseLogger, err = zap.NewDevelopment(
|
||||
zap.AddCaller(),
|
||||
zap.AddCallerSkip(1),
|
||||
zap.AddStacktrace(zapcore.ErrorLevel),
|
||||
)
|
||||
} else {
|
||||
baseLogger, err = zap.NewProduction(
|
||||
zap.AddCaller(),
|
||||
zap.AddCallerSkip(1),
|
||||
zap.AddStacktrace(zapcore.ErrorLevel),
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建级别分文件的日志器
|
||||
levelLogger := &LevelLogger{
|
||||
logger: baseLogger,
|
||||
levelLoggers: make(map[zapcore.Level]*zap.Logger),
|
||||
config: config,
|
||||
}
|
||||
|
||||
// 为每个级别创建专门的日志器
|
||||
if config.EnableLevelSeparation {
|
||||
levelLogger.createLevelLoggers()
|
||||
}
|
||||
|
||||
return levelLogger, nil
|
||||
}
|
||||
|
||||
// createLevelLoggers 创建各级别的日志器
|
||||
func (l *LevelLogger) createLevelLoggers() {
|
||||
levels := []zapcore.Level{
|
||||
zapcore.DebugLevel,
|
||||
zapcore.InfoLevel,
|
||||
zapcore.WarnLevel,
|
||||
zapcore.ErrorLevel,
|
||||
zapcore.FatalLevel,
|
||||
zapcore.PanicLevel,
|
||||
}
|
||||
|
||||
for _, level := range levels {
|
||||
// 获取该级别的配置
|
||||
levelConfig, exists := l.config.LevelConfigs[level]
|
||||
if !exists {
|
||||
// 如果没有配置,使用默认配置
|
||||
levelConfig = LevelFileConfig{
|
||||
MaxSize: 100,
|
||||
MaxBackups: 5,
|
||||
MaxAge: 30,
|
||||
Compress: true,
|
||||
}
|
||||
}
|
||||
|
||||
// 创建该级别的文件输出
|
||||
writeSyncer := l.createLevelWriteSyncer(level, levelConfig)
|
||||
|
||||
// 创建编码器
|
||||
encoder := getEncoder(l.config.BaseConfig.Format, l.config.BaseConfig)
|
||||
|
||||
// 创建 Core
|
||||
core := zapcore.NewCore(encoder, writeSyncer, level)
|
||||
|
||||
// 创建该级别的日志器
|
||||
levelLogger := zap.New(core,
|
||||
zap.AddCaller(),
|
||||
zap.AddCallerSkip(1),
|
||||
zap.AddStacktrace(zapcore.ErrorLevel),
|
||||
)
|
||||
|
||||
l.levelLoggers[level] = levelLogger
|
||||
}
|
||||
}
|
||||
|
||||
// createLevelWriteSyncer 创建级别特定的文件输出同步器
|
||||
func (l *LevelLogger) createLevelWriteSyncer(level zapcore.Level, config LevelFileConfig) zapcore.WriteSyncer {
|
||||
// 构建文件路径
|
||||
var logFilePath string
|
||||
if l.config.BaseConfig.UseDaily {
|
||||
// 按日期分包:logs/2024-01-01/debug.log
|
||||
date := time.Now().Format("2006-01-02")
|
||||
levelName := level.String()
|
||||
logFilePath = filepath.Join(l.config.BaseConfig.LogDir, date, levelName+".log")
|
||||
} else {
|
||||
// 传统方式:logs/debug.log
|
||||
levelName := level.String()
|
||||
logFilePath = filepath.Join(l.config.BaseConfig.LogDir, levelName+".log")
|
||||
}
|
||||
|
||||
// 创建 lumberjack 日志轮转器
|
||||
rotator := &lumberjack.Logger{
|
||||
Filename: logFilePath,
|
||||
MaxSize: config.MaxSize,
|
||||
MaxBackups: config.MaxBackups,
|
||||
MaxAge: config.MaxAge,
|
||||
Compress: config.Compress,
|
||||
}
|
||||
|
||||
return zapcore.AddSync(rotator)
|
||||
}
|
||||
|
||||
// GetLevelLogger 获取指定级别的日志器
|
||||
func (l *LevelLogger) GetLevelLogger(level zapcore.Level) *zap.Logger {
|
||||
if logger, exists := l.levelLoggers[level]; exists {
|
||||
return logger
|
||||
}
|
||||
return l.logger
|
||||
}
|
||||
|
||||
// 实现 Logger 接口
|
||||
func (l *LevelLogger) Debug(msg string, fields ...zapcore.Field) {
|
||||
if logger := l.GetLevelLogger(zapcore.DebugLevel); logger != nil {
|
||||
logger.Debug(msg, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LevelLogger) Info(msg string, fields ...zapcore.Field) {
|
||||
if logger := l.GetLevelLogger(zapcore.InfoLevel); logger != nil {
|
||||
logger.Info(msg, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LevelLogger) Warn(msg string, fields ...zapcore.Field) {
|
||||
if logger := l.GetLevelLogger(zapcore.WarnLevel); logger != nil {
|
||||
logger.Warn(msg, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LevelLogger) Error(msg string, fields ...zapcore.Field) {
|
||||
if logger := l.GetLevelLogger(zapcore.ErrorLevel); logger != nil {
|
||||
logger.Error(msg, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LevelLogger) Fatal(msg string, fields ...zapcore.Field) {
|
||||
if logger := l.GetLevelLogger(zapcore.FatalLevel); logger != nil {
|
||||
logger.Fatal(msg, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LevelLogger) Panic(msg string, fields ...zapcore.Field) {
|
||||
if logger := l.GetLevelLogger(zapcore.PanicLevel); logger != nil {
|
||||
logger.Panic(msg, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LevelLogger) With(fields ...zapcore.Field) Logger {
|
||||
// 为所有级别添加字段
|
||||
for level, logger := range l.levelLoggers {
|
||||
l.levelLoggers[level] = logger.With(fields...)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *LevelLogger) WithContext(ctx context.Context) Logger {
|
||||
// 从上下文提取字段
|
||||
fields := extractFieldsFromContext(ctx)
|
||||
return l.With(fields...)
|
||||
}
|
||||
|
||||
func (l *LevelLogger) Named(name string) Logger {
|
||||
// 为所有级别添加名称
|
||||
for level, logger := range l.levelLoggers {
|
||||
l.levelLoggers[level] = logger.Named(name)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *LevelLogger) Sync() error {
|
||||
// 同步所有级别的日志器
|
||||
for _, logger := range l.levelLoggers {
|
||||
if err := logger.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return l.logger.Sync()
|
||||
}
|
||||
|
||||
func (l *LevelLogger) Core() zapcore.Core {
|
||||
return l.logger.Core()
|
||||
}
|
||||
|
||||
func (l *LevelLogger) GetZapLogger() *zap.Logger {
|
||||
return l.logger
|
||||
}
|
||||
322
internal/shared/logger/logger.go
Normal file
322
internal/shared/logger/logger.go
Normal file
@@ -0,0 +1,322 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"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...)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 全局日志器 - 基于 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
|
||||
}
|
||||
Reference in New Issue
Block a user