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 register<N: Into<String>, H: Into<String>>(
        &self,
        name: N,
        help: H,
        metric: impl Metric,
    );
    fn encode(&self) -> String;

    // Provided method
    fn scoped_label(&self, label: &str) -> String { ... }
}
Expand description

Interface to register and encode metrics.

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 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 to create new context instances (ensures all context instances are namespaced).

Provided Methods§

Source

fn scoped_label(&self, label: &str) -> String

Prefix the given label with the current context’s label.

Unlike with_label, this method does not create a new context.

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,