Skip to main content

Metrics

Trait Metrics 

Source
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 by Metrics::with_label): prefix applied to metrics you register on this context; also populates the name field of runtime-internal task metrics (runtime_tasks_spawned, runtime_tasks_running).
  • attributes (set by Metrics::with_attribute): Prometheus label dimensions on metrics you register on this context; also emitted as OpenTelemetry attributes on the per-task tracing span when, and only when, Metrics::with_span is set on the spawn. Runtime task metrics intentionally ignore attributes to keep their cardinality bounded.
  • scope (set by Metrics::with_scope): routes metrics you register on 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 by Metrics::with_span): wraps the next spawned task in a tracing span populated from the current name and attributes. It never touches metrics. The flag is consumed by the next spawn and is not inherited by child contexts.
BuilderRegistered metric nameRegistered metric labelsRegistry isolationRuntime task metricsTracing span
with_labelprefix--namename field when with_span
with_attribute-label dimension--OTel attribute when with_span
with_scope--sub-registry--
with_span----enables span creation

Required Methods§

Source

fn label(&self) -> String

Get the current label of the context.

Source

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).

Source

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 5

Then 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"}

Source

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.
Source

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.

Source

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_].

Source

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.

Implementors§

Source§

impl Metrics for commonware_runtime::deterministic::Context

Source§

impl Metrics for commonware_runtime::tokio::Context

Source§

impl<C> Metrics for Cell<C>
where C: Metrics,