feat(api-gateway): 实现 W1-W8 网关硬化与 P2-P5 路由扩展
依据 coord-final-decisions §3.8 W1-W8 裁决与 president-final-rulings §2.15/§2.16/§2.19 完整实现网关硬化: - W1/W2: 错误码 GW_ 前缀 + ActionState 信封响应体 - W3: 全量替换为 log/slog 结构化日志 - W4: /readyz 并行 ping 9 下游 + 软失败规则 - W5: 7 个业务 Prometheus 指标 + /metrics 端点 - W6: tracer 资源属性补全(name/version/env/host) - W7: DevMode=true && ENV=production panic 防护 - W8: 保持共享 downstream 熔断 P2 RS256 升级:接入 shared-go/jwks.Fetcher(TTL 5min)。 P2.7+P3-P5 路由扩展:student/parent/messages/dashboard。 文档同步:README/01/02/known-issues,arch.db 已更新。 质量校验:go vet + build + test 均通过。
This commit is contained in:
96
services/api-gateway/internal/observability/metrics.go
Normal file
96
services/api-gateway/internal/observability/metrics.go
Normal file
@@ -0,0 +1,96 @@
|
||||
// Package observability provides tracer, metrics, and structured logging
|
||||
// initialization for the api-gateway.
|
||||
package observability
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
// 业务指标(W5 裁决:7 个业务 metrics)
|
||||
var (
|
||||
httpRequestsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "api_gateway_http_requests_total",
|
||||
Help: "Total number of HTTP requests processed by the gateway.",
|
||||
}, []string{"method", "endpoint", "status"})
|
||||
|
||||
httpRequestDurationSeconds = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "api_gateway_http_request_duration_seconds",
|
||||
Help: "HTTP request processing duration in seconds.",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
}, []string{"method", "endpoint"})
|
||||
|
||||
circuitBreakerState = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "api_gateway_circuit_breaker_state",
|
||||
Help: "Circuit breaker state by service: 1=active state, 0=inactive.",
|
||||
}, []string{"service", "state"})
|
||||
|
||||
rateLimitedTotal = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "api_gateway_rate_limited_total",
|
||||
Help: "Total number of rate-limited requests.",
|
||||
}, []string{"ip"})
|
||||
|
||||
proxyUpstreamDurationSeconds = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "api_gateway_proxy_upstream_duration_seconds",
|
||||
Help: "Upstream proxy response duration in seconds.",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
}, []string{"upstream"})
|
||||
|
||||
jwksRefreshTotal = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "api_gateway_jwks_refresh_total",
|
||||
Help: "Total number of JWKS cache refresh attempts.",
|
||||
}, []string{"result"})
|
||||
|
||||
authFailuresTotal = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "api_gateway_auth_failures_total",
|
||||
Help: "Total number of authentication failures.",
|
||||
}, []string{"reason"})
|
||||
)
|
||||
|
||||
// Metrics 返回 HTTP 请求统计中间件(W5 裁决)。
|
||||
// 记录请求总数(method/endpoint/status)与请求延迟(method/endpoint)。
|
||||
// endpoint 使用 gin 路由模式(c.FullPath())避免高基数。
|
||||
func Metrics() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
start := time.Now()
|
||||
c.Next()
|
||||
|
||||
duration := time.Since(start).Seconds()
|
||||
status := strconv.Itoa(c.Writer.Status())
|
||||
endpoint := c.FullPath()
|
||||
if endpoint == "" {
|
||||
endpoint = "unknown"
|
||||
}
|
||||
httpRequestsTotal.WithLabelValues(c.Request.Method, endpoint, status).Inc()
|
||||
httpRequestDurationSeconds.WithLabelValues(c.Request.Method, endpoint).Observe(duration)
|
||||
}
|
||||
}
|
||||
|
||||
// IncAuthFailure 递增鉴权失败计数器。
|
||||
func IncAuthFailure(reason string) {
|
||||
authFailuresTotal.WithLabelValues(reason).Inc()
|
||||
}
|
||||
|
||||
// IncRateLimited 递增限流计数器。
|
||||
func IncRateLimited(ip string) {
|
||||
rateLimitedTotal.WithLabelValues(ip).Inc()
|
||||
}
|
||||
|
||||
// IncJWKSRefresh 递增 JWKS 刷新计数器(result: success/failure)。
|
||||
func IncJWKSRefresh(result string) {
|
||||
jwksRefreshTotal.WithLabelValues(result).Inc()
|
||||
}
|
||||
|
||||
// SetCircuitBreakerState 更新熔断器状态 gauge。
|
||||
func SetCircuitBreakerState(service, state string, value float64) {
|
||||
circuitBreakerState.WithLabelValues(service, state).Set(value)
|
||||
}
|
||||
|
||||
// ObserveProxyUpstreamDuration 记录上游代理响应延迟。
|
||||
func ObserveProxyUpstreamDuration(upstream string, duration time.Duration) {
|
||||
proxyUpstreamDurationSeconds.WithLabelValues(upstream).Observe(duration.Seconds())
|
||||
}
|
||||
Reference in New Issue
Block a user