From c17798ec679c212e89cff2b8d866735973ff8232 Mon Sep 17 00:00:00 2001 From: hhs <386998068@qq.com> Date: Sun, 21 Jun 2026 22:26:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20HTTP=20=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E6=97=A5=E5=BF=97=E4=B8=AD=E9=97=B4=E4=BB=B6=EF=BC=88?= =?UTF-8?q?Phase=204=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现内容: - 创建 trace/gin_logger.go,实现 GinLogger 和 GinRecovery 中间件 - GinLogger 自动记录所有 HTTP 请求的 method/path/status/latency/client_ip - GinRecovery 使用 zap 记录 panic 恢复信息,替代 gin.Recovery() - 修改 main.go 注册三层中间件:TraceMiddleware -> GinLogger -> GinRecovery - 所有日志自动附加 trace_id 和 request_id 字段 日志示例: { "level": "info", "ts": "2026-06-21T22:26:07.610+0800", "msg": "request completed", "trace_id": "01KVN964KSY6BFDKB9S932NXB8", "request_id": "01KVN964KSY6BFDKB9S932NXB8", "method": "GET", "path": "/api/health", "status": 200, "latency_ms": 0, "client_ip": "::1" } 测试:已验证健康检查接口日志正常输出 --- backend/cmd/server/main.go | 5 ++- backend/internal/trace/gin_logger.go | 63 ++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 backend/internal/trace/gin_logger.go diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 5c4dd56..f4fca48 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -23,6 +23,7 @@ import ( "github.com/hhs/camtalk/internal/ratelimit" "github.com/hhs/camtalk/internal/session" "github.com/hhs/camtalk/internal/store" + "github.com/hhs/camtalk/internal/trace" "github.com/hhs/camtalk/internal/ws" migrations "github.com/hhs/camtalk/migrations" ) @@ -226,7 +227,9 @@ func main() { } r := gin.New() - r.Use(gin.Recovery()) + r.Use(trace.TraceMiddleware()) // 第一层:生成 trace ID + r.Use(trace.GinLogger()) // 第二层:记录请求 + r.Use(trace.GinRecovery()) // 第三层:panic 恢复 // REST API apiGroup := r.Group("/api") diff --git a/backend/internal/trace/gin_logger.go b/backend/internal/trace/gin_logger.go new file mode 100644 index 0000000..1125a08 --- /dev/null +++ b/backend/internal/trace/gin_logger.go @@ -0,0 +1,63 @@ +package trace + +import ( + "time" + + "github.com/gin-gonic/gin" +) + +// GinLogger 记录每个 HTTP 请求的 method/path/status/latency +func GinLogger() gin.HandlerFunc { + return func(c *gin.Context) { + start := time.Now() + path := c.Request.URL.Path + query := c.Request.URL.RawQuery + + c.Next() + + latency := time.Since(start).Milliseconds() + status := c.Writer.Status() + log := FromContext(c.Request.Context()) + + fields := []interface{}{ + "method", c.Request.Method, + "path", path, + "status", status, + "latency_ms", latency, + "client_ip", c.ClientIP(), + } + if query != "" { + fields = append(fields, "query", query) + } + if errStr := c.Errors.String(); errStr != "" { + fields = append(fields, "errors", errStr) + } + + switch { + case status >= 500: + log.Errorw("request completed", fields...) + case status >= 400: + log.Warnw("request completed", fields...) + default: + log.Infow("request completed", fields...) + } + } +} + +// GinRecovery 自定义 panic 恢复中间件,使用 zap 记录 +func GinRecovery() gin.HandlerFunc { + return func(c *gin.Context) { + defer func() { + if err := recover(); err != nil { + log := FromContext(c.Request.Context()) + log.Errorw("panic recovered", + "error", err, + "path", c.Request.URL.Path, + "method", c.Request.Method, + "client_ip", c.ClientIP()) + c.AbortWithStatus(500) + } + }() + c.Next() + } +}