pub trait Metrics:
Clone
+ Send
+ Sync
+ 'static {
// Required methods
fn label(&self) -> String;
fn with_label(&self, label: &str) -> Self;
fn with_attribute(&self, key: &str, value: impl Display) -> Self;
fn with_scope(&self) -> Self;
fn with_span(&self) -> Self;
fn register<N: Into<String>, H: Into<String>>(
&self,
name: N,
help: H,
metric: impl Metric,
);
fn encode(&self) -> String;
}Expand description
Interface to register and encode metrics.
§Mental Model: Metrics vs Tracing
A context carries multiple pieces of observability state, each applied via a different builder. They compose freely, but they do not all feed into the same sinks:
name(set byMetrics::with_label): prefix applied to metrics youregisteron this context; also populates thenamefield of runtime-internal task metrics (runtime_tasks_spawned,runtime_tasks_running).attributes(set byMetrics::with_attribute): Prometheus label dimensions on metrics youregisteron this context; also emitted as OpenTelemetry attributes on the per-task tracing span when, and only when,Metrics::with_spanis set on the spawn. Runtime task metrics intentionally ignore attributes to keep their cardinality bounded.scope(set byMetrics::with_scope): routes metrics youregisteron this context into a sub-registry whose entries are removed when the last clone of the scoped context is dropped. It does not affect runtime task metrics and has no effect on tracing.span(set byMetrics::with_span): wraps the next spawned task in atracingspan populated from the currentnameandattributes. It never touches metrics. The flag is consumed by the nextspawnand is not inherited by child contexts.
| Builder | Registered metric name | Registered metric labels | Registry isolation | Runtime task metrics | Tracing span |
|---|---|---|---|---|---|
with_label | prefix | - | - | name | name field when with_span |
with_attribute | - | label dimension | - | - | OTel attribute when with_span |
with_scope | - | - | sub-registry | - | - |
with_span | - | - | - | - | enables span creation |
Required Methods§
Sourcefn with_label(&self, label: &str) -> Self
fn with_label(&self, label: &str) -> Self
Create a new instance of Metrics with the given label appended to the end
of the current Metrics label.
This is commonly used to create a nested context for register.
Labels must start with [a-zA-Z] and contain only [a-zA-Z0-9_]. It is not permitted for
any implementation to use METRICS_PREFIX as the start of a label (reserved for metrics for the runtime).
Sourcefn with_attribute(&self, key: &str, value: impl Display) -> Self
fn with_attribute(&self, key: &str, value: impl Display) -> Self
Create a new instance of Metrics with an additional attribute (key-value pair) applied
to all metrics registered in this context and any child contexts.
Unlike Metrics::with_label which affects the metric name prefix, with_attribute adds
a key-value pair that appears as a separate dimension in the metric output. This is
useful for instrumenting n-ary data structures in a way that is easy to manage downstream.
Keys must start with [a-zA-Z] and contain only [a-zA-Z0-9_]. Values can be any string.
§Labeling Children
Attributes apply to the entire subtree of contexts. When you call with_attribute, the
label is automatically added to all metrics registered in that context and any child
contexts created via with_label:
context
|-- with_label("orchestrator")
|-- with_attribute("epoch", "5")
|-- counter: votes -> orchestrator_votes{epoch="5"}
|-- counter: proposals -> orchestrator_proposals{epoch="5"}
|-- with_label("engine")
|-- gauge: height -> orchestrator_engine_height{epoch="5"}This pattern avoids wrapping every metric in a Family and avoids polluting metric
names with dynamic values like orchestrator_epoch_5_votes.
Using attributes does not reduce cardinality (N epochs still means N time series). Attributes just make metrics easier to query, filter, and aggregate.
§Family Label Conflicts
When using Family metrics, avoid using attribute keys that match the Family’s label field names.
If a conflict occurs, the encoded output will contain duplicate labels (e.g., {env="prod",env="staging"}),
which is invalid Prometheus format and may cause scraping issues.
#[derive(EncodeLabelSet)]
struct Labels { env: String }
// BAD: attribute "env" conflicts with Family field "env"
let ctx = context.with_attribute("env", "prod");
let family: Family<Labels, Counter> = Family::default();
ctx.register("requests", "help", family);
// Produces invalid: requests_total{env="prod",env="staging"}
// GOOD: use distinct names
let ctx = context.with_attribute("region", "us_east");
// Produces valid: requests_total{region="us_east",env="staging"}§Example
// Instead of creating epoch-specific metric names:
let ctx = context.with_label(&format!("consensus_engine_{}", epoch));
// Produces: consensus_engine_5_votes_total, consensus_engine_6_votes_total, ...
// Use attributes to add epoch as a label dimension:
let ctx = context.with_label("consensus_engine").with_attribute("epoch", epoch);
// Produces: consensus_engine_votes_total{epoch="5"}, consensus_engine_votes_total{epoch="6"}, ...Multiple attributes can be chained:
let ctx = context
.with_label("engine")
.with_attribute("region", "us_east")
.with_attribute("instance", "i1");
// Produces: engine_requests_total{region="us_east",instance="i1"} 42§Querying The Latest Attribute
To query the latest attribute value dynamically, create a gauge to track the current value:
// Create a gauge to track the current epoch
let latest_epoch = Gauge::<i64>::default();
context.with_label("orchestrator").register("latest_epoch", "current epoch", latest_epoch.clone());
latest_epoch.set(current_epoch);
// Produces: orchestrator_latest_epoch 5Then create a dashboard variable $latest_epoch with query max(orchestrator_latest_epoch)
and use it in panel queries: consensus_engine_votes_total{epoch="$latest_epoch"}
Sourcefn with_scope(&self) -> Self
fn with_scope(&self) -> Self
Create a scoped context for metrics with a bounded lifetime (e.g., per-epoch
consensus engines). All metrics registered through the returned context (and
child contexts via Metrics::with_label/Metrics::with_attribute) go into
a separate registry that is automatically removed when all clones of the scoped
context are dropped.
If the context is already scoped, returns a clone with the same scope (scopes nest by inheritance, not by creating new independent scopes).
§Uniqueness
Scoped metrics share the same global uniqueness constraint as
Metrics::register: each (prefixed_name, attributes) pair must be unique.
Callers should use Metrics::with_attribute to distinguish metrics across
scopes (e.g., an “epoch” attribute) rather than re-registering identical keys.
§Example
let scoped = context
.with_label("engine")
.with_attribute("epoch", epoch)
.with_scope();
// Register metrics into the scoped registry
let counter = Counter::default();
scoped.register("votes", "vote count", counter.clone());
// Metrics are removed when all clones of `scoped` are dropped.Sourcefn with_span(&self) -> Self
fn with_span(&self) -> Self
Return a new context that wraps the next task spawned from it in a tracing
span whose name field and OpenTelemetry attributes are derived from the
context’s label and attributes.
The flag is consumed by the next Spawner::spawn call on the returned
context (and on any clone of it). It is not inherited by child contexts
produced via Metrics::with_label, Metrics::with_attribute,
Metrics::with_scope, or any of the Spawner builders: each such builder
resets the flag so the caller must opt in again for the next spawn.
Enabling the span only affects tracing; it does not change which metrics are registered, nor does it widen the cardinality of runtime task metrics.
Sourcefn register<N: Into<String>, H: Into<String>>(
&self,
name: N,
help: H,
metric: impl Metric,
)
fn register<N: Into<String>, H: Into<String>>( &self, name: N, help: H, metric: impl Metric, )
Register a metric with the runtime.
Any registered metric will include (as a prefix) the label of the current context.
Names must start with [a-zA-Z] and contain only [a-zA-Z0-9_].
Sourcefn encode(&self) -> String
fn encode(&self) -> String
Encode all metrics into a buffer.
To ensure downstream analytics tools work correctly, users must never duplicate metrics
(via the concatenation of nested with_label and register calls). This can be
avoided by using with_label and with_attribute to create new context instances
(ensures all context instances are namespaced).
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.