Skip to content
Open icLens

Metrics from Rust

metra-ic-client records metrics in your canister and sends them to your Agent in small batches. Recording never makes a network call; sending happens when you flush.

  • Add your canister as a sender of the Agent’s Metrics connection. Send telemetry from your code.
  • The crate ships with icLens’s source and isn’t on crates.io yet. Vendor it into your workspace.

Create one ClientState and keep it across messages and upgrades:

use metra_ic_client::{ClientError, ClientState, CollectionPolicy};
fn new_metrics_client() -> Result<ClientState, ClientError> {
ClientState::new(CollectionPolicy::default())
}

Register each metric once. This one is a gauge of queue depth:

use metra_ic_client::{
ClientError, ClientState, Descriptor, DescriptorIdentity,
InstrumentationScope, PointKind, Resource,
};
fn register_queue_depth(client: &mut ClientState) -> Result<u64, ClientError> {
client.register_instrument(Descriptor {
description: "Items waiting for processing".into(),
identity: DescriptorIdentity {
name: "app.queue.depth".into(),
unit: "1".into(),
point_kind: PointKind::Gauge,
monotonic: None,
temporality: None,
},
metadata: vec![],
resource: Resource {
schema_url: None,
attributes: vec![],
},
scope: InstrumentationScope {
name: "worker.metrics".into(),
version: Some("1".into()),
schema_url: None,
attributes: vec![],
},
})
}
use metra_ic_client::{ClientError, ClientState, Number, PointValue};
fn record_queue_depth(
client: &mut ClientState,
instrument_id: u64,
depth: u128,
now_nanos: u64,
) -> Result<(), ClientError> {
client.record_gauge(
instrument_id,
vec![],
PointValue::Number(Number::Nat128(depth)),
None,
now_nanos,
now_nanos,
)
}

Use add_sum for counters and observe_histogram for durations. If the buffer is full, recording returns an error rather than dropping the value.

  1. Open a lane to the Agent and register your metrics with it.

  2. Flush on a timer. Read flush_status; when a flush is due, call prepare_append for one batch.

  3. Save, send, settle. Save the attempt with begin_attempt, release any borrow of your state, await execute, then settle the reply.

A timeout becomes TransportError::UnknownOutcome. Call mark_unknown, then reconcile_remote and reconcile: the client asks the Agent what it received before sending anything else. New values keep collecting in the meantime.

After an upgrade, restore the client state and start your timer again. Never rebuild a pending batch from newer values.