Documentation
¶
Overview ¶
Package metrics provides comprehensive, cardinality-safe Prometheus instrumentation for Go services — the pull/scrape counterpart to the OTel (push) modules in the phpboyscout Go toolkit. It complements, and never conflicts with, gitlab.com/phpboyscout/go/observability: a tool may use both.
Phase 1 (this release) covers the scrape endpoint and runtime observability:
- Register mounts a Prometheus GET /metrics endpoint (Prometheus text / OpenMetrics) onto a caller-provided *http.ServeMux, wrapped by the caller's auth/guard middleware.
- By default the registry carries the Go runtime and process collectors plus a build-info gauge, so heap, goroutines, GC, uptime and the running version are scrapeable with zero external infrastructure.
- WithPprof additionally mounts net/http/pprof behind the same guard.
- WithRegistry / WithCollectors register application collectors.
The core depends only on prometheus/client_golang and go/transit (the middleware type); heavier integrations live in opt-in subpackages so importing the core never pulls them:
- metrics/server — a standalone metrics HTTP server on go/transport.
- metrics/grpc — gRPC server interceptors (Phase 2).
- metrics/otel — an OpenMetrics-exemplar trace source (Phase 2).
Guarding the endpoint ¶
A /metrics (and especially a pprof) endpoint must never be an unauthenticated open port. Pass the service's auth middleware via WithMiddleware, or bind the server to loopback. When no middleware is supplied, Register logs a warning.
Index ¶
- func ObserveResult[T any](o *Operations, ctx context.Context, name string, fn func() (T, error)) (T, error)
- func Register(mux *http.ServeMux, opts ...Option) error
- type BreakerState
- type BuildInfo
- type Cache
- type CircuitBreaker
- type DB
- type ExemplarSource
- type HTTPOption
- func WithDurationBuckets(b ...float64) HTTPOption
- func WithHTTPConstLabels(l prometheus.Labels) HTTPOption
- func WithHTTPNamespace(ns string) HTTPOption
- func WithRouteLabelFunc(f func(*http.Request) string) HTTPOption
- func WithStatusClass() HTTPOption
- func WithUnmatchedRouteLabel(s string) HTTPOption
- type HelperOption
- type Metrics
- func (m *Metrics) BuildInfo() BuildInfo
- func (m *Metrics) ExemplarSource() ExemplarSource
- func (m *Metrics) HTTPMiddleware(opts ...HTTPOption) (func(http.Handler) http.Handler, error)
- func (m *Metrics) Handler() http.Handler
- func (m *Metrics) MountOn(mux *http.ServeMux)
- func (m *Metrics) NewCache(name string, opts ...HelperOption) (*Cache, error)
- func (m *Metrics) NewCircuitBreaker(name string, opts ...HelperOption) (*CircuitBreaker, error)
- func (m *Metrics) NewDB(name string, opts ...HelperOption) (*DB, error)
- func (m *Metrics) NewOperations(opts ...HelperOption) (*Operations, error)
- func (m *Metrics) NewQueue(name string, opts ...HelperOption) (*Queue, error)
- func (m *Metrics) Observe(ctx context.Context, obs prometheus.Observer, v float64)
- func (m *Metrics) Registry() *prometheus.Registry
- type Operations
- type Option
- func WithBuildInfo(bi BuildInfo) Option
- func WithCollectors(cs ...prometheus.Collector) Option
- func WithExemplarSource(src ExemplarSource) Option
- func WithLogger(l *slog.Logger) Option
- func WithMetricsPath(p string) Option
- func WithMiddleware(mw ...func(http.Handler) http.Handler) Option
- func WithPprof() Option
- func WithPprofPath(prefix string) Option
- func WithRegistry(r *prometheus.Registry) Option
- func WithoutRuntimeCollectors() Option
- type PoolStats
- type Queue
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ObserveResult ¶ added in v0.3.0
func ObserveResult[T any](o *Operations, ctx context.Context, name string, fn func() (T, error)) (T, error)
ObserveResult is Operations.Observe for an operation that returns a value.
func Register ¶
Register is the convenience path: it builds a Metrics from opts and mounts it on mux in one call. For finer control (serving the handler yourself, or a standalone server) use New.
Example ¶
ExampleRegister mounts a guarded /metrics endpoint on an existing mux. The exposition text is not deterministic, so there is no Output block.
package main
import (
"log"
"net/http"
metrics "gitlab.com/phpboyscout/go/transport-metrics"
)
func main() {
mux := http.NewServeMux()
// In a real service, pass the auth guard as middleware so the endpoint is
// never an unauthenticated open port.
guard := func(next http.Handler) http.Handler { return next }
if err := metrics.Register(mux, metrics.WithMiddleware(guard)); err != nil {
log.Fatal(err)
}
}
Output:
Types ¶
type BreakerState ¶ added in v0.3.0
type BreakerState int
BreakerState is a circuit-breaker state, exposed as the numeric value of the circuit_breaker_state gauge.
The numeric ordering deliberately matches go/transit's resilience breaker states (resilience.State: Closed=0, Open=1, HalfOpen=2) so a value observed from a resilience breaker maps to the same gauge value here. Use FromResilienceState for an explicit, self-documenting conversion rather than a bare numeric cast.
const ( // BreakerClosed — requests flow normally. BreakerClosed BreakerState = 0 // BreakerOpen — requests are short-circuited. BreakerOpen BreakerState = 1 // BreakerHalfOpen — a trial request is allowed to test recovery. BreakerHalfOpen BreakerState = 2 )
func FromResilienceState ¶ added in v0.4.0
func FromResilienceState(state int) BreakerState
FromResilienceState maps a go/transit resilience breaker-state value (passed as its numeric int, e.g. int(resilience.StateOpen)) to the equivalent BreakerState. Both orderings are Closed=0, Open=1, HalfOpen=2, so the mapping is intentionally identity for the three known states; the explicit switch documents that contract and pins it against future drift. Unknown values fall back to BreakerClosed.
type BuildInfo ¶
type BuildInfo struct {
// Version is the release version (e.g. "v1.4.0"). Falls back to the main
// module's version.
Version string
// Revision is the VCS commit. Falls back to the vcs.revision build setting.
Revision string
// GoVersion is the Go toolchain version. Falls back to runtime.Version().
GoVersion string
// Name is an optional tool name label. Left off the gauge when empty.
Name string
}
BuildInfo is the build metadata exposed as the build_info gauge and available for stamping onto other metrics as constant labels. Any empty field is filled from runtime/debug.ReadBuildInfo where possible.
type Cache ¶ added in v0.3.0
type Cache struct {
// contains filtered or unexported fields
}
Cache instruments a cache with the USE-style signals: hit/miss/eviction counters and size/capacity gauges. The hit ratio is derivable as rate(hits)/(rate(hits)+rate(misses)). All metrics carry a constant cache="<name>" label.
func (*Cache) SetCapacity ¶ added in v0.3.0
SetCapacity sets the cache capacity (maximum entries; 0 = unbounded).
type CircuitBreaker ¶ added in v0.3.0
type CircuitBreaker struct {
// contains filtered or unexported fields
}
CircuitBreaker instruments a circuit breaker: a state gauge plus counters for trips, short-circuits, and the successes/failures that drive state. All metrics carry a constant name="<name>" label.
func (*CircuitBreaker) Failure ¶ added in v0.3.0
func (cb *CircuitBreaker) Failure()
Failure records a failed request observed by the breaker.
func (*CircuitBreaker) SetState ¶ added in v0.3.0
func (cb *CircuitBreaker) SetState(s BreakerState)
SetState sets the current breaker state.
func (*CircuitBreaker) ShortCircuit ¶ added in v0.3.0
func (cb *CircuitBreaker) ShortCircuit()
ShortCircuit records a request rejected while the breaker is open.
func (*CircuitBreaker) Success ¶ added in v0.3.0
func (cb *CircuitBreaker) Success()
Success records a successful request observed by the breaker.
func (*CircuitBreaker) Trip ¶ added in v0.3.0
func (cb *CircuitBreaker) Trip()
Trip records a transition to the open state (and sets the state gauge to open).
type DB ¶ added in v0.3.0
type DB struct {
// contains filtered or unexported fields
}
DB instruments a database: query RED (rate, errors, duration, in-flight) labelled by a bounded query name, and — when DB.RegisterPool is called — pool USE gauges (utilization, saturation). All metrics carry a constant db="<name>" label.
func (*DB) Query ¶ added in v0.3.0
Query runs fn as the named query, recording the RED signals (count, errors, duration, in-flight) and a trace-id exemplar on the duration sample when configured.
func (*DB) RegisterPool ¶ added in v0.3.0
RegisterPool adds pool USE gauges (max-open, open, in-use, idle, wait count and wait seconds) sourced from fn, which is called on each scrape — keep it cheap (e.g. return a mapping of sql.DB.Stats()). It may be called once per DB.
type ExemplarSource ¶ added in v0.2.0
ExemplarSource extracts a trace id from a request context for OpenMetrics exemplars — it returns the current trace id and whether one is present. It is pluggable so the core needs no tracing dependency: pass one from the metrics/otel subpackage (which reads the OTel span context) or your own.
Exemplars attach a trace id to an individual metric *sample* (not a label), so a latency spike on a Prometheus dashboard links to the exact trace without any cardinality cost. See docs: cardinality safety.
type HTTPOption ¶ added in v0.2.0
type HTTPOption func(*httpConfig)
HTTPOption configures Metrics.HTTPMiddleware.
func WithDurationBuckets ¶ added in v0.2.0
func WithDurationBuckets(b ...float64) HTTPOption
WithDurationBuckets overrides the request-duration histogram buckets (seconds). The default is prometheus.DefBuckets.
func WithHTTPConstLabels ¶ added in v0.2.0
func WithHTTPConstLabels(l prometheus.Labels) HTTPOption
WithHTTPConstLabels stamps constant labels (e.g. version) onto the HTTP metrics.
func WithHTTPNamespace ¶ added in v0.2.0
func WithHTTPNamespace(ns string) HTTPOption
WithHTTPNamespace prefixes the HTTP metric names (e.g. "myapp" -> myapp_http_requests_total).
func WithRouteLabelFunc ¶ added in v0.2.0
func WithRouteLabelFunc(f func(*http.Request) string) HTTPOption
WithRouteLabelFunc overrides how the low-cardinality route label is derived. The default reads the matched route template from http.Request.Pattern (Go 1.22+ ServeMux). Supply this for non-stdlib routers — it MUST return a bounded template (e.g. "/users/{id}"), never a raw path, or cardinality explodes.
func WithStatusClass ¶ added in v0.2.0
func WithStatusClass() HTTPOption
WithStatusClass records the status label as its class ("2xx", "4xx", …) instead of the exact code — a coarser, even-lower-cardinality option.
func WithUnmatchedRouteLabel ¶ added in v0.2.0
func WithUnmatchedRouteLabel(s string) HTTPOption
WithUnmatchedRouteLabel sets the label used when no route template matched (default "<other>"). A single bounded bucket prevents a flood of one series per stray path.
type HelperOption ¶ added in v0.3.0
type HelperOption func(*helperConfig)
HelperOption configures a RED/USE domain helper.
func WithBuckets ¶ added in v0.3.0
func WithBuckets(b ...float64) HelperOption
WithBuckets overrides the duration-histogram buckets (seconds). The default is prometheus.DefBuckets.
func WithConstLabels ¶ added in v0.3.0
func WithConstLabels(l prometheus.Labels) HelperOption
WithConstLabels adds constant labels to every metric the helper produces — merged with the helper's own identifying label (e.g. db name).
func WithNamespace ¶ added in v0.3.0
func WithNamespace(ns string) HelperOption
WithNamespace prefixes the helper's metric names (e.g. "myapp" -> myapp_db_query_duration_seconds).
type Metrics ¶
type Metrics struct {
// contains filtered or unexported fields
}
Metrics owns a Prometheus registry and its collectors, and knows how to expose them. Build one with New (or use the Register convenience), then mount it on a mux with Metrics.MountOn, serve Metrics.Handler yourself, or hand it to the metrics/server subpackage for a standalone server.
func New ¶
New builds a Metrics: it resolves the registry (a fresh private one unless WithRegistry is given), registers the Go runtime, process and build-info collectors (unless WithoutRuntimeCollectors), and registers any collectors from WithCollectors.
Example ¶
ExampleNew builds a Metrics with application collectors and serves the handler directly, wrapping it with the caller's own guard.
package main
import (
"log"
"net/http"
metrics "gitlab.com/phpboyscout/go/transport-metrics"
)
func main() {
m, err := metrics.New(
metrics.WithBuildInfo(metrics.BuildInfo{Version: "v1.0.0", Name: "widgetd"}),
)
if err != nil {
log.Fatal(err)
}
mux := http.NewServeMux()
mux.Handle("GET /metrics", m.Handler())
}
Output:
func (*Metrics) BuildInfo ¶
BuildInfo returns the resolved build metadata (version, revision, Go version), handy for stamping the same labels onto other metrics.
func (*Metrics) ExemplarSource ¶ added in v0.2.0
func (m *Metrics) ExemplarSource() ExemplarSource
ExemplarSource returns the configured exemplar source, or nil when none was set. Subpackages (metrics/grpc) use it to observe durations with exemplars on the same terms as the core HTTP instrumentation.
func (*Metrics) HTTPMiddleware ¶ added in v0.2.0
HTTPMiddleware returns middleware that records the RED signals for each request — requests total, request duration, and in-flight — registering its instruments on this Metrics' registry so they appear on the same /metrics endpoint. Duration samples carry a trace-id exemplar when an ExemplarSource is configured.
Labels are method, route and status. The route label is the matched route TEMPLATE (http.Request.Pattern), never the raw URL, so /users/{id} is one series rather than one per id. Wire the returned middleware through your server's middleware chain (e.g. go/transport's WithMiddleware).
func (*Metrics) Handler ¶
Handler returns the Prometheus exposition handler for this registry (Prometheus text format, OpenMetrics-capable). It is unwrapped — Metrics.MountOn applies the configured middleware; if you serve this directly, wrap it yourself.
func (*Metrics) MountOn ¶
MountOn mounts the metrics endpoint (and pprof, if WithPprof was given) onto mux, wrapping every handler with the configured WithMiddleware chain. When no middleware was supplied it logs a warning: a /metrics endpoint should be behind auth or bound to loopback, never an unauthenticated open port.
func (*Metrics) NewCache ¶ added in v0.3.0
func (m *Metrics) NewCache(name string, opts ...HelperOption) (*Cache, error)
NewCache registers the cache instruments on the registry, labelled cache=name.
func (*Metrics) NewCircuitBreaker ¶ added in v0.3.0
func (m *Metrics) NewCircuitBreaker(name string, opts ...HelperOption) (*CircuitBreaker, error)
NewCircuitBreaker registers the breaker instruments on the registry, labelled name=name.
func (*Metrics) NewDB ¶ added in v0.3.0
func (m *Metrics) NewDB(name string, opts ...HelperOption) (*DB, error)
NewDB registers the database query instruments on the registry, labelled db=name. Multiple NewDB calls with distinct names coexist (the db label distinguishes them); a repeated name is a duplicate-registration error. Add pool USE gauges with DB.RegisterPool.
The query label MUST be a bounded name (e.g. "get_user"), never a raw SQL string with interpolated values.
func (*Metrics) NewOperations ¶ added in v0.3.0
func (m *Metrics) NewOperations(opts ...HelperOption) (*Operations, error)
NewOperations registers the operation RED instruments on the registry: operations_total, operation_errors_total, operation_duration_seconds and operations_in_flight, each labelled by "operation".
The operation label MUST come from a small fixed set of names — never interpolate a per-call value (an id, a query string) into it, or cardinality explodes.
func (*Metrics) NewQueue ¶ added in v0.3.0
func (m *Metrics) NewQueue(name string, opts ...HelperOption) (*Queue, error)
NewQueue registers the queue instruments on the registry, labelled queue=name.
func (*Metrics) Observe ¶ added in v0.2.0
Observe records v on obs, attaching a trace-id exemplar when the configured ExemplarSource yields one for ctx and obs supports exemplars. It is the shared observation path for every duration histogram in the module, so HTTP and gRPC instrumentation link to traces identically.
func (*Metrics) Registry ¶
func (m *Metrics) Registry() *prometheus.Registry
Registry returns the underlying registry, so callers can register additional collectors after construction.
type Operations ¶ added in v0.3.0
type Operations struct {
// contains filtered or unexported fields
}
Operations is a RED (Rate, Errors, Duration) instrument set for arbitrary operations — the generic way to instrument business logic. Each call is labelled by a bounded operation name.
ops, _ := m.NewOperations()
err := ops.Observe(ctx, "reindex", func() error { return svc.Reindex(ctx) })
func (*Operations) Observe ¶ added in v0.3.0
Observe runs fn, recording the RED signals for the named operation: it counts the call, times it, counts an error when fn returns one, and (when an ExemplarSource is configured) tags the duration sample with the trace id. fn's error is returned unchanged.
type Option ¶
type Option func(*config)
Option configures a Metrics and how it is mounted.
func WithBuildInfo ¶
WithBuildInfo overrides the auto-detected build metadata used for the build-info gauge and label stamping. Empty fields fall back to runtime/debug.ReadBuildInfo.
func WithCollectors ¶
func WithCollectors(cs ...prometheus.Collector) Option
WithCollectors registers additional application collectors on the registry (cache sizes, queue depths, and so on).
func WithExemplarSource ¶ added in v0.2.0
func WithExemplarSource(src ExemplarSource) Option
WithExemplarSource enables OpenMetrics exemplars on duration histograms: for each observation the source extracts the current trace id from the request context and attaches it to the sample. Pass metricsotel.ExemplarSource() (from the metrics/otel subpackage) to read the OTel span context, or your own. When unset, no exemplars are recorded.
func WithLogger ¶
WithLogger sets the logger used for advisory warnings (e.g. an unguarded endpoint). Defaults to slog.Default().
func WithMetricsPath ¶
WithMetricsPath sets the path the /metrics endpoint is served at (default "/metrics").
func WithMiddleware ¶
WithMiddleware wraps every mounted handler (the metrics endpoint and, if enabled, pprof) with the given middleware — pass the service's auth guard here so the endpoint is never an unauthenticated open port. The parameter is the standard Go middleware signature, so values of any compatible named type (e.g. go/transport's AuthMiddleware result) are accepted without a conversion and the core stays dependency-light.
func WithPprof ¶
func WithPprof() Option
WithPprof additionally mounts net/http/pprof profiles (heap, goroutine, profile, trace, …) behind the same middleware. Off by default — pprof exposes richer internals and should be a deliberate choice.
func WithPprofPath ¶
WithPprofPath sets the path prefix pprof is mounted at (default "/debug/pprof/"). It must end in a slash.
func WithRegistry ¶
func WithRegistry(r *prometheus.Registry) Option
WithRegistry supplies the *prometheus.Registry to use. The default is a fresh, private registry (never the global prometheus.DefaultRegisterer), so a tool's metrics are self-contained and tests do not collide. Supply your own to register application collectors alongside the runtime ones.
func WithoutRuntimeCollectors ¶
func WithoutRuntimeCollectors() Option
WithoutRuntimeCollectors disables the default Go runtime and process collectors.
type PoolStats ¶ added in v0.3.0
type PoolStats struct {
MaxOpen int // configured max open connections (0 = unlimited)
Open int // connections currently open
InUse int // connections currently in use
Idle int // idle connections
WaitCount int64 // total connection waits
WaitSeconds float64 // cumulative time spent waiting for a connection
}
PoolStats is a snapshot of a connection pool, mapped by the caller from its driver's stats (e.g. database/sql's sql.DBStats). It feeds the USE gauges of a DB when supplied via DB.RegisterPool.
type Queue ¶ added in v0.3.0
type Queue struct {
// contains filtered or unexported fields
}
Queue instruments a work queue: a depth gauge, enqueue/dequeue counters, and wait- and processing-time histograms. All metrics carry a constant queue="<name>" label.
func (*Queue) Dequeue ¶ added in v0.3.0
func (q *Queue) Dequeue()
Dequeue records an item removed from the queue.
func (*Queue) Enqueue ¶ added in v0.3.0
func (q *Queue) Enqueue()
Enqueue records an item added to the queue.
func (*Queue) ObserveProcessing ¶ added in v0.3.0
ObserveProcessing records how long an item took to process, attaching a trace-id exemplar from ctx when an ExemplarSource is configured.
func (*Queue) ObserveWait ¶ added in v0.3.0
ObserveWait records how long an item waited in the queue before processing.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package grpc provides gRPC server interceptors that record RED metrics per RPC on a gitlab.com/phpboyscout/go/transport-metrics registry — requests handled, handling duration, and in-flight — labelled by the (bounded) full method name and the gRPC status code.
|
Package grpc provides gRPC server interceptors that record RED metrics per RPC on a gitlab.com/phpboyscout/go/transport-metrics registry — requests handled, handling duration, and in-flight — labelled by the (bounded) full method name and the gRPC status code. |
|
Package otel provides an OpenTelemetry-backed metrics.ExemplarSource: it reads the current span's trace id from the context so duration histograms can carry a trace-id exemplar, linking a Prometheus metric sample to the OTel trace.
|
Package otel provides an OpenTelemetry-backed metrics.ExemplarSource: it reads the current span's trace id from the context so duration histograms can carry a trace-id exemplar, linking a Prometheus metric sample to the OTel trace. |
|
Package server stands up a standalone HTTP server dedicated to a Prometheus metrics endpoint, built on gitlab.com/phpboyscout/go/transport for consistent lifecycle, graceful shutdown and TLS with the rest of the toolkit.
|
Package server stands up a standalone HTTP server dedicated to a Prometheus metrics endpoint, built on gitlab.com/phpboyscout/go/transport for consistent lifecycle, graceful shutdown and TLS with the rest of the toolkit. |