mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
# Which Problems Are Solved ZITADEL opens its Postgres connection pool via `pgxpool.NewWithConfig` (`internal/database/postgres/pg.go`) but never reads `pgxpool.Pool.Stat()`, so operators have no way to observe pool saturation (acquired/idle/total conns vs. `MaxConns`) or connection churn (acquire waits, canceled/empty acquires, idle/lifetime destroys, new connections) through ZITADEL's existing metrics pipeline. # How the Problems Are Solved - Adds `RegisterCounterObserver` (backed by an OTel `Int64ObservableCounter`) to the `Metrics` interface, its implementation, and the test mock (`backend/v3/instrumentation/metric.go`, `backend/v3/instrumentation/metrics/metric.go`, `backend/v3/instrumentation/metrics/mock.go`), mirroring the existing `RegisterValueObserver`/`RegisterUpDownSumObserver` — there was previously no observable instrument for cumulative counters. - Adds `database.RegisterPoolMetrics(ctx, pool)` (`internal/database/metrics.go`), which registers 5 observable gauges (`acquired_conns`, `constructing_conns`, `idle_conns`, `max_conns`, `total_conns`) and 7 observable counters (`acquire_count`, `acquire_duration_milliseconds`, `canceled_acquire_count`, `empty_acquire_count`, `max_idle_destroy_count`, `max_lifetime_destroy_count`, `new_conns_count`) sourced directly from `pgxpool.Pool.Stat()`, published under `zitadel.database.pool.*` metric names. - Wires the registration call into `cmd/start/start.go` right after the pool is created, so the running server process exposes these series on `/metrics`. # Additional Changes - Adds unit tests covering the new `RegisterCounterObserver` method (`backend/v3/instrumentation/metric_test.go`) and the pool metrics registration (`internal/database/metrics_test.go`). # Additional Context - Scope is intentionally limited to `cmd/start` (the long-running process that serves `/metrics`); one-shot commands (`setup`, `mirror`, `initialise`, `key`) are left untouched since an exporter running for a single command invocation wouldn't produce useful series. - https://pkg.go.dev/github.com/jackc/pgx/v5/pgxpool#Stat
77 lines
2.7 KiB
Go
77 lines
2.7 KiB
Go
package metrics
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"sync"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/metric"
|
|
|
|
"github.com/zitadel/zitadel/backend/v3/instrumentation"
|
|
"github.com/zitadel/zitadel/cmd/build"
|
|
)
|
|
|
|
const (
|
|
ActiveSessionCounter = "zitadel.active_session_counter"
|
|
ActiveSessionCounterDescription = "Active session counter"
|
|
SpoolerDivCounter = "zitadel.spooler_div_milliseconds"
|
|
SpoolerDivCounterDescription = "Spooler div from last successful run to now in milliseconds"
|
|
Database = "database"
|
|
ViewName = "view_name"
|
|
)
|
|
|
|
type Metrics interface {
|
|
GetExporter() http.Handler
|
|
RegisterCounter(name, description string) error
|
|
AddCount(ctx context.Context, name string, value int64, labels map[string]attribute.Value) error
|
|
AddHistogramMeasurement(ctx context.Context, name string, value float64, labels map[string]attribute.Value) error
|
|
RegisterUpDownSumObserver(name, description string, callbackFunc metric.Int64Callback) error
|
|
RegisterValueObserver(name, description string, callbackFunc metric.Int64Callback) error
|
|
RegisterCounterObserver(name, description string, callbackFunc metric.Int64Callback) error
|
|
RegisterHistogram(name, description, unit string, buckets []float64) error
|
|
}
|
|
|
|
var globalMeter = sync.OnceValue(func() Metrics {
|
|
return instrumentation.NewMeter(
|
|
instrumentation.Name,
|
|
metric.WithInstrumentationVersion(build.Version()),
|
|
)
|
|
})
|
|
|
|
func GlobalMeter() Metrics {
|
|
return globalMeter()
|
|
}
|
|
|
|
func GetExporter() http.Handler {
|
|
return globalMeter().GetExporter()
|
|
}
|
|
|
|
func RegisterCounter(name, description string) error {
|
|
return globalMeter().RegisterCounter(name, description)
|
|
}
|
|
|
|
func AddCount(ctx context.Context, name string, value int64, labels map[string]attribute.Value) error {
|
|
return globalMeter().AddCount(ctx, name, value, labels)
|
|
}
|
|
|
|
func AddHistogramMeasurement(ctx context.Context, name string, value float64, labels map[string]attribute.Value) error {
|
|
return globalMeter().AddHistogramMeasurement(ctx, name, value, labels)
|
|
}
|
|
|
|
func RegisterHistogram(name, description, unit string, buckets []float64) error {
|
|
return globalMeter().RegisterHistogram(name, description, unit, buckets)
|
|
}
|
|
|
|
func RegisterUpDownSumObserver(name, description string, callbackFunc metric.Int64Callback) error {
|
|
return globalMeter().RegisterUpDownSumObserver(name, description, callbackFunc)
|
|
}
|
|
|
|
func RegisterValueObserver(name, description string, callbackFunc metric.Int64Callback) error {
|
|
return globalMeter().RegisterValueObserver(name, description, callbackFunc)
|
|
}
|
|
|
|
func RegisterCounterObserver(name, description string, callbackFunc metric.Int64Callback) error {
|
|
return globalMeter().RegisterCounterObserver(name, description, callbackFunc)
|
|
}
|