Pull vs push: Prometheus and OTel¶
The toolkit gives you two metrics families, and they are deliberately different.
transport-metrics is the Prometheus, pull/scrape one;
go/observability is the OTel, push one. This page
explains why both exist and how to choose — or combine — them.
Two models¶
go/transport-metrics (this) |
go/observability (OTel) |
|
|---|---|---|
| Collection | pull — a scraper fetches GET /metrics |
push — the app sends OTLP to a collector |
| Infra to use | none — curl/scrape locally |
an OTLP collector/endpoint |
| Format | Prometheus text / OpenMetrics | OTLP |
| Sweet spot | local + single-binary tools; classic Prometheus/Grafana stacks | fleets with a collector pipeline; unified traces+metrics+logs |
| Trace link | OpenMetrics exemplars (Phase 2) | OTel-native span context |
Why not just one¶
The work that seeded this module was a single-binary tool whose author wanted to
watch its memory live and catch a leak — no Kubernetes, no collector, no OTLP
endpoint to stand up. For that, pull is right: expose /metrics, point
curl/prometheus/pprof at it, done. Requiring an OTLP collector for a
single-user local tool is disproportionate.
Conversely, a fleet already shipping OTLP wants its metrics in the same pipeline
as its traces and logs. For that, go/observability's push model is right.
Neither subsumes the other; forcing one model on both use cases makes one of them worse. So the toolkit ships both, clearly labelled.
Using both¶
They do not conflict — distinct modules, distinct package names, no shared global
state. transport-metrics owns its own private *prometheus.Registry;
go/observability owns the OTel meter. A tool can run OTLP push in production and
expose a loopback /metrics + pprof for local debugging, with no interference.
The exemplar bridge (Phase 2)¶
The one place the two worlds meet is exemplars: an OpenMetrics exemplar attaches
a trace id to a metric sample, so a latency spike on a Prometheus dashboard links to
the exact trace. Phase 2 reads that trace id through a pluggable source, so the
core needs no OTel dependency and the bridge stays opt-in — complementing
go/observability, never competing with it.