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

215 lines
5.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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