What transport-metrics does not do¶
Some of these are deliberate, some are just not built yet, and the difference matters when you are deciding whether to work around one. Each entry says which it is.
It cannot push metrics anywhere¶
There is no OTLP exporter, no remote-write client, no Pushgateway support and no
StatsD bridge. The only way data leaves the process is a scraper fetching
GET /metrics.
Deliberate. Pull is the model this module exists to serve; push is what
go/observability is for. The two are meant to be run together
where you want both, not merged into one module that does each badly.
The practical consequence is that a short-lived process — a cron job, a CLI invocation — has nothing useful to expose here, because it will have exited before anything scrapes it. A Pushgateway is the usual answer to that shape of problem, and this module is not it.
It has no configuration file, environment variables or CLI flags¶
Everything is configured in Go, through functional options passed to New,
Register, HTTPMiddleware and the helper constructors. There is no
PHPBOYSCOUT_METRICS_* environment variable, no YAML key, and no flag to register on
a command.
Deliberate. It is a library, not a service. If your tool wants an operator to be able to change the metrics path or enable pprof at runtime, read that from your own configuration and pass it through as an option.
It does not instrument outbound calls¶
There are no client-side metrics: no instrumented http.RoundTripper, no gRPC client
interceptors, no http_client_* or grpc_client_* series. HTTPMiddleware and the
metrics/grpc interceptors are both server-side only.
Not built yet. Client-side instrumentation is listed on the
roadmap as a follow-up rather than ruled out. Until then,
client_golang's own promhttp.InstrumentRoundTripper* helpers register on a
registry you can get from m.Registry(), so they compose with this module without
needing anything from it.
The route label depends on http.ServeMux¶
The default route label reads http.Request.Pattern, which only http.ServeMux
populates. Under chi, gin, echo or any other router the field is empty and every
request falls into the single <other> bucket.
Deliberate, with an escape hatch. Reading a template out of each third-party
router would mean depending on all of them. WithRouteLabelFunc lets you supply one
function that reads yours — chi.RouteContext(r.Context()).RoutePattern(),
c.FullPath(), and so on.
The failure mode here is quiet: you get metrics, they are just useless per route. Nothing errors and nothing warns.
Exemplars are histogram-only, and OpenMetrics-only¶
Exemplars are attached in one place — the shared Metrics.Observe path — which means
duration histograms and nothing else. Counters take no exemplars, even though
OpenMetrics permits them, and Queue.ObserveWait records without one because it takes
no context to read a trace id from.
They are also invisible unless the scraper negotiates OpenMetrics. curl with no
Accept header shows none, which reliably reads as "exemplars are not working".
Deliberate on the format — that is how OpenMetrics works. Incidental on the coverage; nothing prevents counter exemplars being added.
It cannot tell whether your endpoint is actually guarded¶
The unguarded-mount warning fires on exactly one signal: whether any middleware was
supplied. It does not know your listener's bind address, so a service bound to
127.0.0.1 warns anyway, and a service that passed a middleware which authenticates
nothing does not.
A real limitation, not a design choice. The module is handed a *http.ServeMux,
which carries no address. Treat the warning as a prompt to check the posture, not as a
verdict on it.
Registration is one-way¶
There is no Unregister, no way to remove an instrument set, and no way to rebuild
one with different options. A Metrics and everything on it live for the process.
The registry underneath does support unregistration — reach for
m.Registry().Unregister(...) if you genuinely need it — but nothing in this module's
API exposes it.
Related: build_info cannot be switched off. WithoutRuntimeCollectors drops the
go_* and process_* collectors and leaves build_info registered, and there is no
option that removes it.
No native histograms, no summaries, no configurable quantiles¶
Every duration instrument this module creates is a classic bucketed histogram.
Prometheus native histograms are not used, no constructor produces a
prometheus.Summary, and there is no option for quantile targets —
WithDurationBuckets / WithBuckets change the bucket boundaries and nothing else.
(go_gc_duration_seconds is a summary, but that comes from client_golang's Go
collector, not from here.)
Not built yet. Buckets default to prometheus.DefBuckets, which spans 5 ms to
10 s and suits HTTP-ish latencies; a millisecond-scale in-memory operation will pile
into the first bucket and needs its own boundaries.
Gauges are pushed, not observed¶
Cache.SetSize, Cache.SetCapacity and Queue.SetDepth are values your code sets.
Nothing inspects your cache or queue. A gauge you never set reads 0, which is
indistinguishable from an empty queue.
The one exception is DB.RegisterPool, which takes a callback invoked at scrape time
— that one really does read live state.
Deliberate. The helpers instrument any cache or queue rather than a particular implementation, which means they cannot know how to read yours.
One thing per name, per registry¶
Each helper registers a fixed set of metric names distinguished only by a constant
identifying label, so NewDB("orders") twice on the same Metrics is a
duplicate-registration error. Distinct names coexist; the same name does not, unless
you also change the namespace. Nothing is per-request or dynamic — you cannot create
an instrument set on the fly from an incoming label value, which is exactly the
pattern that would explode cardinality.
What it deliberately does not decide for you¶
- Whether the endpoint is exposed. It mounts on the mux you hand it and never
starts a listener of its own. Even the standalone
metrics/serverreturns an*http.Serverfor you to run. - Whether pprof is safe here. Off by default, on when you ask.
- What your labels mean. Bounding the cardinality of a label value you supply is your job — see Cardinality safety.
Related¶
- Pull vs push — why the push model lives in a different module.
- Architecture & roadmap — what is planned, and why the core stays lean.
- Failure modes — what each of these looks like when you hit it.