Run a standalone metrics server¶
Sometimes you want metrics on their own port/listener — separate from your main
application server, typically bound to loopback or an internal address — rather than
mounted on the app mux. The opt-in metrics/server subpackage stands one up on
go/transport, so it shares the toolkit's lifecycle, graceful shutdown and TLS.
Importing metrics/server is the only thing that pulls go/transport into your
build; the core metrics package never does.
Loopback bind default¶
Behaviour change — the server now binds loopback by default
server.New binds 127.0.0.1 (loopback) only unless you say otherwise.
Previously the server bound all interfaces (:<port>), exposing the
metrics/pprof port on every network the host was reachable on. If you relied
on that reachability — for example an off-host Prometheus scraping the port
directly — you must now opt back in explicitly:
// All interfaces (the old default):
metricsserver.New(ctx, settings, m, metricsserver.WithHost(""))
// A specific internal address:
metricsserver.New(ctx, settings, m, metricsserver.WithBindAddress("10.1.2.3"))
A metrics/pprof endpoint must never be an unauthenticated open port, so the safe posture — loopback, or auth-guarded — is now the default. See Guard the endpoint.
Bind-host options (server.Option):
| Option | Effect |
|---|---|
| (none) | Bind 127.0.0.1 (loopback) — the default. |
WithHost(host) / WithBindAddress(host) |
Bind host; pass "" for all interfaces. |
WithServerOptions(...) |
Thread go/transport construction options (port, TLS, timeouts). |
import (
"context"
transporthttp "gitlab.com/phpboyscout/go/transport/http"
metrics "gitlab.com/phpboyscout/go/transport-metrics"
metricsserver "gitlab.com/phpboyscout/go/transport-metrics/server"
)
func startMetrics(ctx context.Context) (*http.Server, error) {
m, err := metrics.New(
metrics.WithBuildInfo(metrics.BuildInfo{Version: version, Name: "mytool"}),
metrics.WithPprof(),
)
if err != nil {
return nil, err
}
// A dedicated server serving m's endpoint. It binds 127.0.0.1 by default,
// so the metrics/pprof port is never externally reachable (the guard for a
// metrics port). Use WithHost/WithBindAddress to opt out.
return metricsserver.New(ctx, transporthttp.ServerSettings{Port: 9090}, m)
}
Running and stopping it¶
server.New returns a *http.Server. Run and stop it with go/transport's
lifecycle helpers, or register those with a go/controls controller alongside your
application server so both start and stop together:
start := transporthttp.StartWithTLSPair(logger, srv, tlsPair) // controls.StartFunc
stop := transporthttp.Stop(logger, srv) // controls.StopFunc
See the go/transport docs for the full lifecycle and TLS options.
Attach, or standalone — or both¶
Use whichever fits:
- Attach (
metrics.Register(mux, …)) — one server, metrics alongside your API. Simplest; the app's auth guards it. - Standalone (
metrics/server) — metrics isolated on their own (loopback) port, independent lifecycle. Keeps scrape traffic off the app port.
A tool can even do both — mount a guarded /metrics on the public API and run a
loopback pprof server for local debugging.