Metrics
Inject Arc<OpenTelemetryMeter> and build counters, histograms or gauges; readings batch on a PeriodicReader and export through the same OTLP endpoint.
Metrics are the aggregate counterpart to traces — reach for them when you need a number over time (signups per minute, job duration percentiles, queue depth) rather than the story of one request.
OpenTelemetryModule provides Arc<OpenTelemetryMeter> — the OTel global
meter, wrapped so it flows through the DI graph. Inject it like any other
dependency.
A first counter
Section titled “A first counter”Inject the meter, then build the instrument from it. Arc<OpenTelemetryMeter>
derefs to the inner Meter; the meter caches instruments by name, so building
the counter at the call site is cheap and needs no extra field.
use std::sync::Arc;
use nest_rs::core::injectable;use nest_rs::opentelemetry::OpenTelemetryMeter;
#[injectable]pub struct SignupMetrics { #[inject] meter: Arc<OpenTelemetryMeter>,}
impl SignupMetrics { /// Call this after a successful signup. pub fn record_signup(&self) { self.meter.u64_counter("users.created").build().add(1, &[]); }}Counters, histograms, gauges — anything the
opentelemetry meter exposes works. Inject
Arc<OpenTelemetryMeter> into the service that owns the event and build the
instrument lazily inside the method, as above.
Attributes
Section titled “Attributes”The second argument to .add(...) / .record(...) is a slice of OTel
attributes — the metric’s labels at the OTel/Prometheus boundary.
use opentelemetry::KeyValue;
self.meter.u64_counter("users.created").build().add(1, &[ KeyValue::new("org_id", org_id.to_string()), KeyValue::new("source", "api"),]);Stick to a small, bounded cardinality on the label set — every distinct attribute combination becomes a separate time series.
Export
Section titled “Export”Metrics batch on a
PeriodicReader and export through the
same OTLP endpoint as the traces (set
NESTRS_OPENTELEMETRY__OTLP_ENDPOINT). Without an endpoint, instrument
construction still works — counter() and friends return real handles, so
wiring compiles and runs — but the meter comes from a NoopMeterProvider and
every reading is dropped. The SDK says so once, at Meter was obtained from a NoopMeterProvider. No metrics will be recorded. A test that needs to assert
on an increment has to hold its own counter; there is nothing to read back
here.
The interval is 60 seconds by default, and that will look broken. Traces and logs reach a collector immediately; metrics wait for the reader’s next flush. The natural first check — wire a meter, hit the route, look at the collector — shows spans and log records with zero metrics, for a full minute. Nothing is wrong; shorten the interval for a local run:
NESTRS_OPENTELEMETRY__METRIC_INTERVAL_SECS=5 nestrs run dev apior pin it in code, as with any other field:
OpenTelemetryConfig::from_env("api").with_metric_interval(Duration::from_secs(5))A value of 0 is the sentinel for “keep the 60 s default” — a PeriodicReader
on a zero period is a tight export loop, which is worse than a slow one. A
non-numeric value is a typo, not a sentinel: the default is kept and the
variable is named on stderr (nestrs: WARNING — unparseable NESTRS_OPENTELEMETRY__METRIC_INTERVAL_SECS=…). It is reported rather than
boot-fatal because this config is what installs the subscriber, so it is built
before there is anywhere to log or anything to propagate an error to.
Resource attributes
Section titled “Resource attributes”Every exported metric carries the service-level resource attributes built from
OpenTelemetryConfig:
service.name— argument toOpenTelemetry::initservice.version—NESTRS_OPENTELEMETRY__SERVICE_VERSIONdeployment.environment—NESTRS_OPENTELEMETRY__SERVICE_ENVIRONMENTservice.instance_id—NESTRS_OPENTELEMETRY__SERVICE_INSTANCE_ID(a fresh UUIDv7 per process by default, so restarts get distinct identities in the backend)
These come from the
opentelemetry-semantic-conventions
schema, so any backend that understands OTel semconv (Grafana Cloud, Honeycomb,
Datadog OTLP, …) keys on them out of the box.
Going further
Section titled “Going further”- Traces — the per-request story metrics aggregate.
- Logs — structured events on the same OTLP endpoint.
- OpenTelemetry — the meter provider and OTLP export.