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