Documentation
¶
Overview ¶
Package metrics is the OpenTelemetry-backed metrics surface for craftgo runtimes. It owns the project-wide MeterProvider and the Prometheus exporter that the admin server scrapes.
The package itself records no metrics directly. The HTTP instruments (`http.server.request.duration` histogram, request / response size histograms, active-request gauge) are emitted by `otelhttp.NewHandler` - wired via pkg/otel.HTTPMiddleware - against whatever MeterProvider Init (or InitDefault) installs on the global slot. Application code that wants its own counters or histograms calls `otel.Meter("...")` directly; this package exists to bootstrap and expose, not to invent a parallel API.
Index ¶
- Constants
- func Init(opts ...Option) (*sdkmetric.MeterProvider, error)
- func InitDefault() (*sdkmetric.MeterProvider, error)
- func InitFromConfig(ctx context.Context, c Config) (*sdkmetric.MeterProvider, *adminServer, error)
- func IsEnabled() bool
- func RegisterRuntimeCollectors() error
- func Registerer() prom.Registerer
- func ShutdownAdmin(ctx context.Context, s *http.Server) error
- func SnapshotHandler() http.Handler
- func StartAdmin(addr string, opts ...AdminOption) (*http.Server, <-chan error)
- type AdminOption
- type Config
- type Option
Constants ¶
const ( ExporterPrometheus = "prometheus" ExporterOTLPgRPC = "otlp_grpc" ExporterOTLPHTTP = "otlp_http" ExporterNone = "none" )
Exporter selector values for [Config.Exporter]. An empty or unknown value falls back to ExporterPrometheus so a typo never silently disables metrics.
const DefaultAdminAddr = ":9090"
DefaultAdminAddr is the conventional Prometheus admin port. Used as a hint in docs; pass any addr string to StartAdmin to override.
const DefaultMetricsPath = "/metrics"
DefaultMetricsPath is the canonical Prometheus exposition path. `StartAdmin` uses this when no WithPath option is supplied.
Variables ¶
This section is empty.
Functions ¶
func Init ¶
func Init(opts ...Option) (*sdkmetric.MeterProvider, error)
Init wires an OTel MeterProvider with the supplied readers. With zero options it defaults to WithPrometheusReader so the dev-mode `/metrics` scrape works without extra plumbing. Production deployments compose the readers they actually need:
// Pull (Prometheus scrape):
metrics.Init(metrics.WithPrometheusReader())
// Push (OTLP gRPC to collector):
metrics.Init(metrics.WithOTLPgRPCReader(ctx, "collector:4317"))
// Both — side-by-side scrape and push:
metrics.Init(
metrics.WithPrometheusReader(),
metrics.WithOTLPgRPCReader(ctx, "collector:4317"),
)
Returns the configured provider so callers can shut it down via `provider.Shutdown(ctx)` during graceful termination - important for OTLP push so the final batch flushes before the process exits.
func InitDefault ¶
func InitDefault() (*sdkmetric.MeterProvider, error)
InitDefault is the dev-friendly shorthand: it calls Init with the Prometheus reader and installs the standard Go runtime / process collectors so the `/metrics` scrape surfaces `go_*` (goroutines, GC, memory) and `process_*` (RSS, CPU, FDs) alongside the HTTP instruments `otelhttp` emits. Production projects pick the more granular Init when they want a different reader set.
Errors from collector registration are surfaced; the MeterProvider itself is already installed by then, so the HTTP histogram path keeps working even if a runtime collector fails to register (rare but possible if a host strips procfs).
func InitFromConfig ¶
InitFromConfig dispatches the exporter selection encoded in c, then starts the admin scrape listener for the prometheus path. Returns the active MeterProvider and the admin server (nil when no admin listener was needed) so the caller can defer Shutdown on both.
When c.Enabled is false everything is (nil, nil, nil) - the caller can keep its shutdown code single-pathed.
func IsEnabled ¶
func IsEnabled() bool
IsEnabled reports whether the package has installed a MeterProvider. Returns false until Init / InitDefault succeeds.
func RegisterRuntimeCollectors ¶
func RegisterRuntimeCollectors() error
RegisterRuntimeCollectors attaches the standard Go runtime and process Prometheus collectors to the package registry. Call it after Init when you want `go_*` / `process_*` series alongside the OTel-bridged metrics - the config-driven main.tmpl pipeline uses it for the prometheus exporter path. Idempotent: a duplicate registration is silently swallowed so repeated boots in tests don't break.
func Registerer ¶
func Registerer() prom.Registerer
Registerer exposes the underlying Prometheus registry so application code can attach process / runtime collectors (`prometheus.NewGoCollector`, `prometheus.NewProcessCollector`) on top of the OTel-bridged metrics. The returned interface is the standard `prometheus.Registerer`, so any client_golang-compatible metric surfaces alongside the OTel ones on the same /metrics scrape.
func ShutdownAdmin ¶
ShutdownAdmin gracefully closes a running admin server with a bounded deadline. Tolerates a nil server so callers don't have to guard the StartAdmin("") sentinel - a no-op when nothing was started.
func SnapshotHandler ¶
SnapshotHandler returns the Prometheus text-format exposition handler bound to the package's registry. Wire it manually on a route when StartAdmin's dedicated listener is overkill (single port deployments, sidecar scrape configs):
srv.Handle("GET /metrics", metrics.SnapshotHandler())
The handler honours the standard scrape negotiation: clients asking for `application/openmetrics-text` get OpenMetrics; the default Prometheus exposition (text/plain; version=0.0.4) is emitted otherwise. When Init / InitDefault has not been called the registry is empty - the response stays valid (an empty scrape) so health probes and monitor smoke checks still see 200.
func StartAdmin ¶
func StartAdmin(addr string, opts ...AdminOption) (*http.Server, <-chan error)
StartAdmin spins up a dedicated HTTP listener exposing the metrics snapshot on a separate admin port (Prometheus convention, default `:9090`). Keeping telemetry off the public traffic listener is the idiomatic split - public clients get the typed API on the main port, ops scrape the admin port without being firewalled in.
addr controls the listen address (`:9090`, `127.0.0.1:9090`, ...); pass an empty string to opt out of the listener entirely (the function returns nil + nil so callers can leave the call site unconditional). Path defaults to DefaultMetricsPath; override with WithPath when a different convention applies.
The returned `*http.Server` is the live listener; callers Shutdown it during their main lifecycle (typical pattern: pair with the public server's Shutdown so `Ctrl+C` drains both).
On listener errors the returned `<-chan error` channel surfaces the failure asynchronously so the caller can decide whether to log + continue (admin failures usually shouldn't kill the public server) or hard-exit. The channel is buffered so a never-read receiver does not block the goroutine that closes it.
Types ¶
type AdminOption ¶
type AdminOption func(*adminConfig)
AdminOption mutates the admin server's configuration. Callers compose options at the call site:
metrics.StartAdmin(":9090", metrics.WithPath("/internal/metrics"))
New knobs land here as additional `WithX` constructors so the StartAdmin signature stays stable.
func WithPath ¶
func WithPath(p string) AdminOption
WithPath overrides the route the metrics snapshot is served on. Defaults to DefaultMetricsPath (`/metrics`). Useful when an existing reverse proxy already claims that path or the company convention is something else (`/internal/metrics`, `/_/observability`, ...).
type Config ¶
type Config struct {
// Enabled toggles the MeterProvider install AND the admin
// listener startup. False = no-op meter (otelhttp's recorder
// stays silent).
Enabled bool `yaml:"enabled"`
// Exporter selects the data path:
// - "prometheus" / "" - pull on AdminAddr (default)
// - "otlp_grpc" - push via OTLP gRPC
// - "otlp_http" - push via OTLP HTTP/protobuf
// - "none" - meter installed without exporter (testing)
Exporter string `yaml:"exporter"`
// Endpoint is the collector address for OTLP exporters. Ignored
// for "prometheus" / "none".
Endpoint string `yaml:"endpoint"`
// AdminAddr is the bind address for the Prometheus scrape
// listener. Ignored unless Exporter == "prometheus".
AdminAddr string `yaml:"adminAddr"`
// Path is the scrape route (default "/metrics"). Override when a
// reverse proxy already claims that path.
Path string `yaml:"path"`
}
Config is the YAML-shaped meter configuration the generated runtime hands to InitFromConfig. Mirrors the `metrics:` block in `config/config.yaml` so the call site reads `metrics.InitFromConfig(ctx, cfg.Metrics)`. Defining the type here keeps the exporter dispatch + admin-listener wiring in the library.
type Option ¶
type Option func(*config)
Option configures the MeterProvider built by Init. Each option either appends a Reader to the provider (Prometheus pull, OTLP gRPC push, OTLP HTTP push) or attaches metadata. Errors that occur while building exporters are captured on the config; the first error short-circuits the rest and surfaces from Init.
func WithOTLPHTTPReader ¶
WithOTLPHTTPReader adds a periodic OTLP HTTP/protobuf push exporter pointed at endpoint, a full URL including scheme: `"http://collector:4318"` (plaintext) or `"https://collector.example.com"` (TLS). The URL SCHEME selects transport security — `http` connects insecurely, `https` uses TLS — so no separate insecure/TLS toggle is needed; pass `otlpmetrichttp.WithTLSClientConfig(...)` in opts only for a custom certificate.
func WithOTLPgRPCReader ¶
WithOTLPgRPCReader adds a periodic OTLP gRPC push exporter pointed at addr, which is either:
- a bare `host:port` (e.g. `"otel-collector.observability:4317"`) — connects INSECURE (plain-text), the convention for collectors on a trusted local network; or
- a full URL whose SCHEME selects transport security — `http://host:4317` (plaintext) or `https://host:4317` (TLS).
The URL form lets a project enable TLS straight from config.yaml (`endpoint: https://...`) instead of needing code. Push interval defaults to 60s — the OTel SDK default; pass `otlpmetricgrpc.WithCompressor("gzip")` and friends through opts for transport tuning.
func WithPrometheusReader ¶
func WithPrometheusReader() Option
WithPrometheusReader adds a Prometheus pull exporter scoped to the package registry. Pair it with StartAdmin (or wire SnapshotHandler manually) to expose `/metrics` for scrapers.
The default when Init is called with no options.
func WithReader ¶
WithReader is the escape hatch: hand any pre-built `sdkmetric.Reader` to Init. Use it when the project needs a custom exporter (in-memory for tests, third-party SaaS, exotic transport) the Prometheus / OTLP helpers don't cover. Stack as many WithReader options as needed - the same MeterProvider fans every metric to all readers.