package errors import ( "github.com/gofiber/fiber/v2" "go.uber.org/zap" "github.com/break/junhong_cmp_fiber/pkg/constants" ) // ErrorContext 错误发生时的请求上下文(用于日志记录) type ErrorContext struct { RequestID string // 请求 ID(唯一标识) Method string // HTTP 方法 Path string // 请求路径 Query string // Query 参数 Body string // 请求 Body(限制 50KB) IP string // 客户端 IP UserAgent string // User-Agent UserID string // 用户 ID(如果已认证) } const ( // MaxBodyLogSize 请求 Body 日志记录最大字节数(50KB) MaxBodyLogSize = 50 * 1024 ) // FromFiberContext 从 Fiber Context 提取错误上下文 func FromFiberContext(c *fiber.Ctx) *ErrorContext { ctx := &ErrorContext{ Method: c.Method(), Path: c.Path(), Query: c.Request().URI().QueryArgs().String(), IP: c.IP(), UserAgent: c.Get("User-Agent"), } // 提取 Request ID if rid := c.Locals(constants.ContextKeyRequestID); rid != nil { ctx.RequestID = rid.(string) } if ctx.RequestID == "" { ctx.RequestID = c.Get("X-Request-ID") } // 提取 User ID(如果已认证) if uid := c.Locals("user_id"); uid != nil { if userID, ok := uid.(string); ok { ctx.UserID = userID } } // 提取请求 Body(限制 50KB) bodyBytes := c.Body() if len(bodyBytes) > 0 { if len(bodyBytes) > MaxBodyLogSize { // 超过限制时截断并添加提示 ctx.Body = string(bodyBytes[:MaxBodyLogSize]) + " ... (truncated)" } else { ctx.Body = string(bodyBytes) } } return ctx } // ToLogFields 转换为 Zap 日志字段 func (ec *ErrorContext) ToLogFields() []zap.Field { fields := []zap.Field{ zap.String("request_id", ec.RequestID), zap.String("method", ec.Method), zap.String("path", ec.Path), zap.String("ip", ec.IP), } // 可选字段(非空时添加) if ec.Query != "" { fields = append(fields, zap.String("query", ec.Query)) } if ec.Body != "" { fields = append(fields, zap.String("body", ec.Body)) } if ec.UserAgent != "" { fields = append(fields, zap.String("user_agent", ec.UserAgent)) } if ec.UserID != "" { fields = append(fields, zap.String("user_id", ec.UserID)) } return fields }