metrics
A lightweight, protocol-agnostic metrics facade for Rust, decoupling instrumentation from whatever exporter you plug in.
Repository Health
Technical Analysis
metrics is a facade crate for the Rust ecosystem, modeled directly on the log crate: it gives library and application authors a single, stable set of macros — counter!, gauge!, and histogram! — for emitting counters, gauges, and histograms without tying the calling code to any particular backend. A global Recorder trait is installed once, at the application’s entry point, and every instrumented crate in the dependency tree starts reporting through it automatically, with zero extra configuration on the library author’s side.
The project anchors a small ecosystem of companion crates published from the same repository — metrics-util for helper types, metrics-exporter-prometheus and metrics-exporter-tcp for shipping data out, and metrics-tracing-context for bridging tracing span fields into metric labels — plus a wide set of community-maintained exporters for StatsD, New Relic, Sentry, and more. The core crate itself stays intentionally minimal: it defines the metric primitives, the key/label model, and the recorder trait, and leaves everything about collection and export to whichever exporter a consumer chooses to install.
What You Get
- Three macro-driven metric primitives —
counter!,gauge!, andhistogram!— plus matchingdescribe_*!macros for attaching units and descriptions - A global
Recordertrait with install-once semantics, so every crate in the dependency tree reports through the same backend automatically - A thread-local recorder override for scoped instrumentation (useful in tests or multi-tenant contexts) alongside the global one
- An allocation-optimized
SharedString/Cowtype for metric keys and labels that supports zero-cost static construction as well as owned andArc-shared values - A
NoopRecorderdefault so instrumented code compiles and runs safely even before any exporter is installed - A companion crate ecosystem (
metrics-util,metrics-exporter-prometheus,metrics-exporter-tcp,metrics-tracing-context) covering the rest of the export pipeline
Common Use Cases
- Instrumenting a Rust web service or backend to expose request counts, latencies, and error rates to Prometheus via
metrics-exporter-prometheus - Adding library-level metrics to a crate you publish, so downstream applications get insight into it for free once they install any recorder
- Bridging
tracingspans into metric labels withmetrics-tracing-contextto correlate structured logs with counters and histograms - Recording histogram data for operation latencies or payload sizes to understand distribution shape, not just averages
- Swapping exporters (Prometheus, StatsD, TCP, Sentry) without touching any instrumentation call sites, since all of them implement the same
Recordertrait
Under The Hood
Architecture
The crate is organized around a small set of composable primitives rather than a monolithic client: key.rs and label.rs define the Key/Label model used to identify a metric plus its dimensions, handles.rs defines the Counter/Gauge/Histogram handle types and the CounterFn/GaugeFn/HistogramFn traits a backend implements, and recorder/mod.rs defines the central Recorder trait plus a RecorderOnceCell (in recorder/cell.rs) that enforces install-once semantics for a 'static global recorder, backed by a NoopRecorder (recorder/noop.rs) fallback and a thread-local override for scoped use. The public macros in macros.rs are thin, expanding at compile time into static Key/Metadata construction plus a lookup against the currently installed recorder — the core abstraction that would break the most if changed is the Recorder trait itself, since every exporter crate in the ecosystem implements it directly.
Tech Stack
This is a no_std-adjacent, dependency-light std crate targeting Rust edition 2018 with an MSRV of 1.71.1, enforced by CI. Its only runtime dependency is rapidhash for key hashing, with a portable-atomic fallback pulled in on 32-bit targets that lack native 64-bit atomics; dev-dependencies (criterion, trybuild, log, rand) are used purely for benchmarking and macro-expansion testing. It ships as one crate within a Cargo workspace alongside its own exporters (metrics-exporter-prometheus, metrics-exporter-tcp, metrics-exporter-dogstatsd), metrics-util, and metrics-tracing-context, with docs.rs as the documentation target and crates.io as the sole distribution channel.
Code Quality
Unit tests live alongside the modules they cover (key.rs, cow.rs, label.rs, metadata.rs, common.rs) using Rust’s built-in #[test] harness, with trybuild additionally used to assert compile-time failure messages for macro misuse. Error handling favors explicit, typed results (SetRecorderError) over panics for the one fallible global-install operation, and the crate is #![deny(missing_docs)]-style thorough in its public API documentation. CI (ci.yml) enforces the MSRV and runs the workspace’s clippy lints per clippy.toml.
What Makes It Unique
The crate’s distinguishing choice is committing fully to a facade pattern for metrics the way log did for logging: it ships zero opinions about aggregation, storage, or transport, and instead standardizes only the emission-time API and the trait boundary a recorder must satisfy. This lets an entire ecosystem of independently-versioned exporter crates interoperate with any instrumented library without coordination, and the copy-on-write SharedString design specifically optimizes for the hot path of macro-invoked, mostly-static metric names and labels.