Files
Edu/services/api-gateway/internal/observability/metrics.go
SpecialX 4307f6b73c 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 均通过。
2026-07-10 18:15:48 +08:00

97 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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())
}