b6a6875795
往"生产可运维"推一步(网关前门):
- Prometheus /metrics:sundynix_http_requests_total{method,route,status}、
request_duration_seconds 直方图、requests_in_flight。route 用 c.FullPath()
路由模板(/tasks/:id/...)避免按真实路径高基数。
- 结构化访问日志:slog JSON 到 stderr(request_id/method/route/status/latency_ms/
ip/uid/bytes),替代 gin 默认文本日志;gin.New()+Recovery 自管中间件链。
- RequestID 中间件:生成/透传 X-Request-ID,写上下文+响应头,供日志关联。
- 探针:/healthz(liveness,不查依赖)、/readyz(readiness,DB+Redis 就绪才 200,
否则 503),供 k8s 等导流判断;/api/v1/health 深度聚合保留。
- 三个根端点不挂业务鉴权(/metrics 生产应由网络层限制抓取来源)。
验证:单测(计数 +1 / X-Request-ID 生成与透传);实跑 /healthz 200、/readyz 200
(db,redis ready)、/metrics 输出真实指标、访问日志 JSON 正常、X-Request-ID 回写。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
49 lines
1.3 KiB
Go
49 lines
1.3 KiB
Go
package middleware
|
||
|
||
import (
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"testing"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/prometheus/client_golang/prometheus/testutil"
|
||
)
|
||
|
||
func newEngine() *gin.Engine {
|
||
gin.SetMode(gin.TestMode)
|
||
r := gin.New()
|
||
r.Use(RequestID(), Observe())
|
||
r.GET("/ping", func(c *gin.Context) { c.String(http.StatusOK, "pong") })
|
||
return r
|
||
}
|
||
|
||
func TestObserve_CountsAndRequestID(t *testing.T) {
|
||
r := newEngine()
|
||
before := testutil.ToFloat64(httpRequests.WithLabelValues("GET", "/ping", "200"))
|
||
|
||
w := httptest.NewRecorder()
|
||
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/ping", nil))
|
||
|
||
if w.Code != 200 {
|
||
t.Fatalf("状态码=%d", w.Code)
|
||
}
|
||
if w.Header().Get("X-Request-ID") == "" {
|
||
t.Error("应自动生成并回写 X-Request-ID")
|
||
}
|
||
after := testutil.ToFloat64(httpRequests.WithLabelValues("GET", "/ping", "200"))
|
||
if after != before+1 {
|
||
t.Errorf("请求计数应 +1:before=%v after=%v", before, after)
|
||
}
|
||
}
|
||
|
||
func TestRequestID_PropagatesIncoming(t *testing.T) {
|
||
r := newEngine()
|
||
w := httptest.NewRecorder()
|
||
req := httptest.NewRequest(http.MethodGet, "/ping", nil)
|
||
req.Header.Set("X-Request-ID", "trace-abc-123")
|
||
r.ServeHTTP(w, req)
|
||
if got := w.Header().Get("X-Request-ID"); got != "trace-abc-123" {
|
||
t.Errorf("应透传入站 X-Request-ID,got %q", got)
|
||
}
|
||
}
|