package metrics import ( "net/http" "os" "sync" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) var ( globalMetrics *Metrics globalOnce sync.Once ) // Metrics 包含所有 Prometheus 指标 type Metrics struct { RequestsTotal *prometheus.CounterVec RequestErrorsTotal *prometheus.CounterVec RequestRetriesTotal *prometheus.CounterVec TokensTotal *prometheus.CounterVec QuotaConsumedTotal *prometheus.CounterVec RequestDuration *prometheus.HistogramVec UpstreamDuration *prometheus.HistogramVec FirstTokenDuration *prometheus.HistogramVec ActiveRequests *prometheus.GaugeVec } // NewMetrics 创建并注册所有指标到给定 Registry func NewMetrics(reg prometheus.Registerer) *Metrics { m := &Metrics{ RequestsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "newapi_requests_total", Help: "Total number of API requests", }, []string{"channel", "model", "code", "is_stream", "relay_mode"}), RequestErrorsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "newapi_request_errors_total", Help: "Total number of request errors", }, []string{"channel", "model", "error_type", "error_code"}), RequestRetriesTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "newapi_request_retries_total", Help: "Total number of request retries", }, []string{"channel", "model"}), TokensTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "newapi_tokens_total", Help: "Total tokens used", }, []string{"channel", "model", "type"}), QuotaConsumedTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "newapi_quota_consumed_total", Help: "Total quota consumed", }, []string{"channel", "model", "billing_source"}), RequestDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{ Name: "newapi_request_duration_seconds", Help: "End-to-end request duration in seconds", Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120}, }, []string{"channel", "model", "is_stream", "relay_mode"}), UpstreamDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{ Name: "newapi_upstream_duration_seconds", Help: "Upstream response duration in seconds", Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120}, }, []string{"channel", "model"}), FirstTokenDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{ Name: "newapi_first_token_duration_seconds", Help: "Time to first token in seconds (streaming only)", Buckets: []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}, }, []string{"channel", "model"}), ActiveRequests: prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "newapi_active_requests", Help: "Current number of active requests", }, []string{"model"}), } reg.MustRegister( m.RequestsTotal, m.RequestErrorsTotal, m.RequestRetriesTotal, m.TokensTotal, m.QuotaConsumedTotal, m.RequestDuration, m.UpstreamDuration, m.FirstTokenDuration, m.ActiveRequests, ) return m } // GetMetrics 返回全局 Metrics 实例 func GetMetrics() *Metrics { globalOnce.Do(func() { if os.Getenv("METRICS_ENABLED") == "false" { globalMetrics = NewMetrics(prometheus.NewPedanticRegistry()) return } globalMetrics = NewMetrics(prometheus.DefaultRegisterer) prometheus.MustRegister(NewChannelCollector()) }) return globalMetrics } // IsEnabled 返回 metrics 是否启用 func IsEnabled() bool { return os.Getenv("METRICS_ENABLED") != "false" } // NewMetricsHandler 返回默认 registry 的 /metrics HTTP handler func NewMetricsHandler() http.Handler { return promhttp.Handler() } // NewMetricsHandlerWithRegistry 返回指定 registry 的 /metrics HTTP handler func NewMetricsHandlerWithRegistry(reg *prometheus.Registry) http.Handler { return promhttp.HandlerFor(reg, promhttp.HandlerOpts{}) }