Skip to content
Open icLens

Metrics from Motoko

mo:metra-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 package ships with icLens’s source and isn’t on Mops yet. Vendor it into your project.

Create one client state when your canister initialises, and keep it across upgrades. Then register each metric once. This one is a gauge of queue depth:

import Client "mo:metra-client";
import Result "mo:core/Result";
module {
public func registerQueueDepth(
client : Client.State
) : Result.Result<Nat64, Client.Error> {
client.registerInstrument({
description = "Items waiting for processing";
identity = {
name = "app.queue.depth";
unit = "1";
pointKind = #gauge;
monotonic = null;
temporality = null;
};
metadata = [];
resource = {
schemaUrl = null;
attributes = [
{ key = "service.name"; value = #text("worker") }
];
};
scope = {
name = "worker.metrics";
version = ?"1";
schemaUrl = null;
attributes = [];
};
});
};
};

Create the state with Client.initialize(Client.defaultPolicy()).

Pass the metric’s ID, the value, and the current time in nanoseconds:

import Client "mo:metra-client";
import Result "mo:core/Result";
module {
public func recordQueueDepth(
client : Client.State,
instrumentId : Nat64,
depth : Nat,
nowNanos : Nat64
) : Result.Result<(), Client.Error> {
client.recordGauge(
instrumentId,
[],
#number(#nat128(depth)),
null,
nowNanos,
nowNanos
);
};
};

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

  1. Open a lane to the Agent with configureLane, then prepareOpen.

  2. Register your metrics with the Agent: prepareRegistration.

  3. Flush on a timer. Ask flushStatus; when a flush is due, call prepareAppend for one batch.

  4. Save, send, settle. Save the attempt with beginAttempt before Client.execute, then settle the reply with settleAppend.

A timeout doesn’t mean the batch was lost. Call markUnknown, then reconcileRemote 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 clear a pending batch to restart sending.