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