215 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | ||
| 	}
 | ||
| }
 |