mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: export AI Gateway metrics under new branding + keep old as alias (#26413)
> AI Tools where used in this request. Registers `coder_aibridged_*` and `coder_aibridgeproxyd_*` metrics under new prefixes: `coder_ai_gateway_*` and `coder_ai_gateway_proxy_*`. Old prefix is still exported. Will be removed in later release. Also updated the `metricsdocgen` static fixture. Added 4 previously-undocumented metrics `key_pool_state`, `key_pool_state_transitions_total`, `key_pool_exhaustions_total`, `key_pool_failover_attempts` added the `client` label to the existing interception, prompt, and token counter samples. Updated AI Gateway documentation.
This commit is contained in:
@@ -46,7 +46,7 @@ func NewMetrics(reg prometheus.Registerer) *Metrics {
|
||||
|
||||
ProvidersLastReloadSuccessTimestampSeconds: factory.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "providers_last_reload_success_timestamp_seconds",
|
||||
Help: "Unix timestamp of the last provider reload that successfully refreshed the pool. A gap against coder_aibridged_providers_last_reload_timestamp_seconds means the loop is firing but the refresh function is failing.",
|
||||
Help: "Unix timestamp of the last provider reload that successfully refreshed the pool. A gap against the providers_last_reload_timestamp_seconds gauge means the loop is firing but the refresh function is failing.",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package prometheusmetrics
|
||||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
// metricAliasRegisterer exposes each collector under multiple prefixes.
|
||||
type metricAliasRegisterer struct {
|
||||
registerers []prometheus.Registerer
|
||||
}
|
||||
|
||||
// NewMetricAliasRegisterer exposes collectors under canonicalPrefix and each
|
||||
// alias prefix. Every exported name reads from the same collector. Alias
|
||||
// prefixes are typically deprecated names scheduled for removal; see each
|
||||
// call site for the specific deprecation ticket.
|
||||
func NewMetricAliasRegisterer(base prometheus.Registerer, canonicalPrefix string, aliasPrefixes ...string) prometheus.Registerer {
|
||||
prefixes := append([]string{canonicalPrefix}, aliasPrefixes...)
|
||||
registerers := make([]prometheus.Registerer, 0, len(prefixes))
|
||||
for _, prefix := range prefixes {
|
||||
registerers = append(registerers, prometheus.WrapRegistererWithPrefix(prefix, base))
|
||||
}
|
||||
return &metricAliasRegisterer{registerers: registerers}
|
||||
}
|
||||
|
||||
// Register registers c under each prefix and rolls back on failure.
|
||||
func (m *metricAliasRegisterer) Register(c prometheus.Collector) error {
|
||||
for i, registerer := range m.registerers {
|
||||
if err := registerer.Register(c); err != nil {
|
||||
for _, registered := range m.registerers[:i] {
|
||||
registered.Unregister(c)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MustRegister registers collectors and panics on the first failure.
|
||||
func (m *metricAliasRegisterer) MustRegister(cs ...prometheus.Collector) {
|
||||
for _, c := range cs {
|
||||
if err := m.Register(c); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unregister removes c from every prefix.
|
||||
func (m *metricAliasRegisterer) Unregister(c prometheus.Collector) bool {
|
||||
ok := true
|
||||
for _, registerer := range m.registerers {
|
||||
ok = registerer.Unregister(c) && ok
|
||||
}
|
||||
return ok
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package prometheusmetrics_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
io_prometheus_client "github.com/prometheus/client_model/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/prometheusmetrics"
|
||||
)
|
||||
|
||||
func TestMetricAliasRegisterer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("EmitsCanonicalAndAliases", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
suffix string
|
||||
register func(prometheus.Registerer)
|
||||
}{
|
||||
{
|
||||
name: "counter_vec",
|
||||
suffix: "requests_total",
|
||||
register: func(reg prometheus.Registerer) {
|
||||
counter := prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "requests_total",
|
||||
Help: "Total requests.",
|
||||
}, []string{"route"})
|
||||
reg.MustRegister(counter)
|
||||
counter.WithLabelValues("/api").Add(3)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "gauge",
|
||||
suffix: "inflight_requests",
|
||||
register: func(reg prometheus.Registerer) {
|
||||
gauge := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "inflight_requests",
|
||||
Help: "Inflight requests.",
|
||||
})
|
||||
reg.MustRegister(gauge)
|
||||
gauge.Set(7)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "histogram_vec",
|
||||
suffix: "request_duration_seconds",
|
||||
register: func(reg prometheus.Registerer) {
|
||||
histogram := prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "request_duration_seconds",
|
||||
Help: "Request duration.",
|
||||
Buckets: []float64{1, 5},
|
||||
}, []string{"route"})
|
||||
reg.MustRegister(histogram)
|
||||
histogram.WithLabelValues("/api").Observe(3)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
base := prometheus.NewRegistry()
|
||||
prefixes := []string{"canonical_", "alias_one_", "alias_two_"}
|
||||
reg := prometheusmetrics.NewMetricAliasRegisterer(base, prefixes[0], prefixes[1:]...)
|
||||
|
||||
tc.register(reg)
|
||||
|
||||
families, err := base.Gather()
|
||||
require.NoError(t, err)
|
||||
|
||||
canonical := prefixes[0] + tc.suffix
|
||||
for _, aliasPrefix := range prefixes[1:] {
|
||||
assertParity(t, families, canonical, aliasPrefix+tc.suffix)
|
||||
}
|
||||
require.Len(t, families, len(prefixes))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RegisterRollsBackPartialFailure", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
base := prometheus.NewRegistry()
|
||||
counter := prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Name: "requests_total",
|
||||
Help: "Total requests.",
|
||||
})
|
||||
prometheus.WrapRegistererWithPrefix("alias_two_", base).MustRegister(counter)
|
||||
|
||||
reg := prometheusmetrics.NewMetricAliasRegisterer(base, "canonical_", "alias_one_", "alias_two_")
|
||||
err := reg.Register(counter)
|
||||
require.Error(t, err)
|
||||
|
||||
families, err := base.Gather()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, families, 1)
|
||||
require.Equal(t, "alias_two_requests_total", families[0].GetName())
|
||||
})
|
||||
|
||||
t.Run("Unregister", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
base := prometheus.NewRegistry()
|
||||
reg := prometheusmetrics.NewMetricAliasRegisterer(base, "canonical_", "alias_one_", "alias_two_")
|
||||
|
||||
counter := prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Name: "requests_total",
|
||||
Help: "Total requests.",
|
||||
})
|
||||
reg.MustRegister(counter)
|
||||
|
||||
require.True(t, reg.Unregister(counter))
|
||||
|
||||
families, err := base.Gather()
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, families)
|
||||
})
|
||||
}
|
||||
|
||||
func assertParity(t *testing.T, families []*io_prometheus_client.MetricFamily, canonical, alias string) {
|
||||
t.Helper()
|
||||
canonicalFamily := findMetricFamily(t, families, canonical)
|
||||
aliasFamily := findMetricFamily(t, families, alias)
|
||||
require.Equal(t, canonicalFamily.GetType(), aliasFamily.GetType())
|
||||
require.Equal(t, canonicalFamily.GetHelp(), aliasFamily.GetHelp())
|
||||
require.Equal(t, canonicalFamily.GetMetric(), aliasFamily.GetMetric())
|
||||
}
|
||||
|
||||
func findMetricFamily(t *testing.T, families []*io_prometheus_client.MetricFamily, name string) *io_prometheus_client.MetricFamily {
|
||||
t.Helper()
|
||||
for _, family := range families {
|
||||
if family.GetName() == name {
|
||||
return family
|
||||
}
|
||||
}
|
||||
require.Failf(t, "metric family not found", "missing metric family %q", name)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user