pkg/gateway/metrics/registry.go
Source
- Package:
metrics - File:
pkg/gateway/metrics/registry.go - GitHub: https://github.com/theroutercompany/api_router/blob/main/pkg/gateway/metrics/registry.go
Overview
What:
Wraps a Prometheus registry with gateway-friendly defaults and small helper methods.
This package is intentionally tiny. It standardizes:
- how the gateway creates a Prometheus registry
- whether default Go/process collectors are registered
- how the
/metricsHTTP handler is exposed
Why:
Metrics wiring shows up in multiple places (server startup, tests, local tools).
Having a single constructor avoids subtle issues like:
- registering default collectors multiple times (panic on duplicate registration)
- forgetting to expose a handler for the correct registry (accidentally using the global Prometheus registry)
- inconsistent naming conventions across packages
How:
Call NewRegistry(...) early during runtime wiring. It constructs an isolated prometheus.Registry,
optionally registers the standard Go and process collectors, and returns a small wrapper.
Downstream code can then:
- use
Handler()to mount/metrics - use
Register()to add custom collectors to the same registry - use
Raw()for advanced Prometheus APIs when needed
Notes:
The namespace field is advisory. This package does not automatically rewrite metric names.
If you want namespaced metrics, incorporate Registry.Namespace() when building collectors.
Contents
Imports
import block 1
import (
"net/http"
"strings"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
Types
type block 1
type Option func(*options)
Option
What: Functional option used to configure NewRegistry.
Why: Keeps the NewRegistry signature stable while allowing future tuning knobs.
How: Each option mutates an internal options struct.
type block 2
type options struct {
namespace string
registerDefaultCollectors bool
}
options
What: Internal settings used while constructing a Registry.
Why: Separates construction-time configuration from the exported wrapper type.
How: Populated by Option functions and read by NewRegistry.
type block 3
type Registry struct {
namespace string
registry *prometheus.Registry
}
Registry
What: Wrapper that holds the namespace string and the underlying Prometheus registry.
Why: Makes it easy to pass around "the gateway's registry" without exposing all Prometheus types everywhere.
How: Constructed by NewRegistry and consumed by server/runtime wiring and metrics producers.
Functions and Methods
WithNamespace
What: Sets an optional namespace string on the registry wrapper.
Why: Provides a shared convention for metric naming across packages without forcing it on every call site.
How: Trims whitespace and stores the namespace in internal options; later exposed via (*Registry).Namespace().
Notes: This does not automatically prefix metric names; callers must incorporate it when constructing metrics.
func WithNamespace(namespace string) Option {
return func(o *options) {
o.namespace = strings.TrimSpace(namespace)
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L24:
return func(o *options) { o.namespace = strings.TrimSpace(namespace) }- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L24:
func(o *options) { o.namespace = strings.TrimSpace(namespace) }- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L25:
o.namespace = strings.TrimSpace(namespace)- What: Assigns o.namespace.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L25:
- L24:
WithoutDefaultCollectors
What: Disables automatic registration of Go/process collectors.
Why: Useful in tests (avoid noisy metrics) and in embeddings where another component already registers collectors.
How: Sets registerDefaultCollectors to false in internal options.
func WithoutDefaultCollectors() Option {
return func(o *options) {
o.registerDefaultCollectors = false
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L32:
return func(o *options) { o.registerDefaultCollectors = false }- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L32:
func(o *options) { o.registerDefaultCollectors = false }- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L33:
o.registerDefaultCollectors = false- What: Assigns o.registerDefaultCollectors.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L33:
- L32:
NewRegistry
What: Constructs a new gateway metrics registry.
Why: Ensures each gateway runtime instance has an isolated Prometheus registry with consistent defaults.
How: Applies provided options, creates a new prometheus.Registry, optionally registers Go/process collectors, and returns a Registry wrapper.
func NewRegistry(opts ...Option) *Registry {
settings := options{
registerDefaultCollectors: true,
}
for _, opt := range opts {
if opt != nil {
opt(&settings)
}
}
reg := prometheus.NewRegistry()
if settings.registerDefaultCollectors {
reg.MustRegister(collectors.NewGoCollector())
reg.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
}
return &Registry{
namespace: settings.namespace,
registry: reg,
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L47:
settings := options{ registerDefaultCollectors: true, }- What: Defines settings.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L50:
for _, opt := range opts { if opt != nil { opt(&settings) } }- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L51:
if opt != nil { opt(&settings) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L52:
opt(&settings)- What: Calls opt.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L52:
- L51:
- L56:
reg := prometheus.NewRegistry()- What: Defines reg.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L57:
if settings.registerDefaultCollectors { reg.MustRegister(collectors.NewGoCollector()) reg.MustRegister(collectors.NewProcessCollector(colle…- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L58:
reg.MustRegister(collectors.NewGoCollector())- What: Calls reg.MustRegister.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L59:
reg.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))- What: Calls reg.MustRegister.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L58:
- L62:
return &Registry{ namespace: settings.namespace, registry: reg, }- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*Registry).Namespace
What: Returns the configured namespace string (or empty).
Why: Allows metric constructors in other packages to follow a consistent naming convention.
How: Returns r.namespace with nil-safety.
func (r *Registry) Namespace() string {
if r == nil {
return ""
}
return r.namespace
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L70:
if r == nil { return "" }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L71:
return ""- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L71:
- L73:
return r.namespace- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*Registry).Handler
What: Returns an HTTP handler that serves Prometheus metrics for this registry.
Why: Keeps /metrics wiring simple and avoids accidentally using the global Prometheus registry.
How: Uses promhttp.HandlerFor(r.registry, ...) and returns http.NotFoundHandler() when the receiver/registry is nil.
func (r *Registry) Handler() http.Handler {
if r == nil || r.registry == nil {
return http.NotFoundHandler()
}
return promhttp.HandlerFor(r.registry, promhttp.HandlerOpts{})
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L79:
if r == nil || r.registry == nil { return http.NotFoundHandler() }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L80:
return http.NotFoundHandler()- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L80:
- L82:
return promhttp.HandlerFor(r.registry, promhttp.HandlerOpts{})- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*Registry).Register
What: Registers a collector with this registry.
Why: Central place to add custom gateway metrics while sharing the same registry instance.
How: Calls MustRegister on the underlying registry when the receiver and collector are non-nil.
Notes: Prometheus MustRegister panics on registration errors (e.g., duplicate descriptors).
func (r *Registry) Register(c prometheus.Collector) {
if r == nil || r.registry == nil || c == nil {
return
}
r.registry.MustRegister(c)
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L89:
if r == nil || r.registry == nil || c == nil { return }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L90:
return- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L90:
- L92:
r.registry.MustRegister(c)- What: Calls r.registry.MustRegister.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
(*Registry).Raw
What: Exposes the underlying *prometheus.Registry.
Why: Some integrations need Prometheus APIs not covered by this wrapper.
How: Returns r.registry with nil-safety.
func (r *Registry) Raw() *prometheus.Registry {
if r == nil {
return nil
}
return r.registry
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L97:
if r == nil { return nil }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L98:
return nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L98:
- L100:
return r.registry- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).