优化生产日志体积:访问降噪、压缩清理与退款告警节流

跳过轮询/封面等热路径成功请求,按日 gzip 并保留可配天数,退款 max-retry 汇总告警。
This commit is contained in:
yml2213
2026-07-13 22:02:20 +08:00
parent f51aed1f0f
commit 2efcede800
8 changed files with 361 additions and 41 deletions
+56 -9
View File
@@ -8,24 +8,40 @@ import (
"go.uber.org/zap"
)
// 成功且延迟低于该阈值的热路径请求不再写访问日志(错误/慢请求仍全量记录)。
const slowRequestThresholdMs = 200
func RequestLogger(logger *zap.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
latency := time.Since(start)
latencyMs := float64(time.Since(start).Microseconds()) / 1000
status := c.Writer.Status()
path := c.Request.URL.Path
route := c.FullPath()
if shouldSkipHTTPLog(path, route, status, latencyMs) {
return
}
fields := []zap.Field{
zap.String("request_id", GetRequestID(c)),
zap.String("method", c.Request.Method),
zap.String("path", c.Request.URL.Path),
zap.String("route", c.FullPath()),
zap.Int("status", c.Writer.Status()),
zap.Float64("latency_ms", float64(latency.Microseconds())/1000),
zap.String("path", path),
zap.String("route", route),
zap.Int("status", status),
zap.Float64("latency_ms", latencyMs),
zap.String("client_ip", c.ClientIP()),
zap.String("user_agent", c.GetHeader("User-Agent")),
zap.String("referer", c.GetHeader("Referer")),
zap.Int("response_size", c.Writer.Size()),
}
// 成功响应省略 UA/referer,错误与 4xx/5xx 保留完整现场
if status >= 400 {
fields = append(fields,
zap.String("user_agent", c.GetHeader("User-Agent")),
zap.String("referer", c.GetHeader("Referer")),
)
}
if userID, ok := c.Get(ContextUserID); ok {
fields = append(fields, zap.Any("user_id", userID))
}
@@ -37,12 +53,43 @@ func RequestLogger(logger *zap.Logger) gin.HandlerFunc {
}
switch {
case c.Writer.Status() >= 500:
case status >= 500:
logger.Error("http request", fields...)
case c.Writer.Status() >= 400:
case status >= 400:
logger.Warn("http request", fields...)
default:
logger.Info("http request", fields...)
}
}
}
// shouldSkipHTTPLog 判断是否跳过写入:仅跳过「成功 + 非慢请求」的高频热路径。
func shouldSkipHTTPLog(path, route string, status int, latencyMs float64) bool {
if status >= 400 {
return false
}
if latencyMs >= slowRequestThresholdMs {
return false
}
return isHotPath(path, route)
}
func isHotPath(path, route string) bool {
if strings.HasSuffix(path, "/unread-count") {
return true
}
if path == "/api/wallet/balance" {
return true
}
if path == "/api/mobile-home-config" {
return true
}
if strings.HasSuffix(path, "/cover") || route == "/api/listings/:id/cover" {
return true
}
if path == "/api/files/object" || path == "/api/admin/files/object" ||
route == "/api/files/object" || route == "/api/admin/files/object" {
return true
}
return false
}
@@ -0,0 +1,33 @@
package middleware
import "testing"
func TestShouldSkipHTTPLog(t *testing.T) {
tests := []struct {
name string
path string
route string
status int
latencyMs float64
wantSkip bool
}{
{name: "轮询未读成功跳过", path: "/api/chats/unread-count", status: 200, latencyMs: 1, wantSkip: true},
{name: "钱包余额成功跳过", path: "/api/wallet/balance", status: 200, latencyMs: 2, wantSkip: true},
{name: "封面成功跳过", path: "/api/listings/12/cover", route: "/api/listings/:id/cover", status: 200, latencyMs: 3, wantSkip: true},
{name: "文件对象成功跳过", path: "/api/files/object", status: 200, latencyMs: 5, wantSkip: true},
{name: "首页配置成功跳过", path: "/api/mobile-home-config", status: 200, latencyMs: 4, wantSkip: true},
{name: "轮询 401 不跳过", path: "/api/chats/unread-count", status: 401, latencyMs: 1, wantSkip: false},
{name: "轮询 500 不跳过", path: "/api/wallet/balance", status: 500, latencyMs: 1, wantSkip: false},
{name: "轮询慢请求不跳过", path: "/api/chats/unread-count", status: 200, latencyMs: 250, wantSkip: false},
{name: "普通业务成功不跳过", path: "/api/orders", status: 200, latencyMs: 20, wantSkip: false},
{name: "下单成功不跳过", path: "/api/orders", status: 201, latencyMs: 30, wantSkip: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := shouldSkipHTTPLog(tt.path, tt.route, tt.status, tt.latencyMs)
if got != tt.wantSkip {
t.Fatalf("shouldSkipHTTPLog() = %v, want %v", got, tt.wantSkip)
}
})
}
}