ic-analytics-sdk 0.2.2

IC Analytics SDK
Documentation

ic-analytics-sdk

Rust SDK for sending metrics from any Internet Computer canister to an ICAnalytics analytics canister.

Installation

Add the crate to your canister's Cargo.toml:

[dependencies]
ic-analytics-sdk = "0.2.2"

Or with cargo add:

cargo add ic-analytics-sdk

Quick start

1. Initialise the client

AnalyticsClient holds the principal of your analytics canister. Create it once and store it in a thread_local!.

use ic_analytics_sdk::AnalyticsClient;
use candid::Principal;

thread_local! {
    static ANALYTICS: AnalyticsClient = AnalyticsClient::new(
        Principal::from_text("YOUR-ANALYTICS-CANISTER-ID").unwrap()
    );
}

Find your analytics canister ID on the ICAnalytics dashboard after creating an analytics canister for your Dapp.

2. Record a metric

All recording is fire-and-forgetrecord_metric enqueues a one-way inter-canister call that does not block your canister's execution.

use ic_analytics_sdk::{Metric, MetricValue};

ANALYTICS.with(|a| {
    a.record_metric(Metric {
        key:   "user_signups".to_string(),
        name:  "User Sign-ups".to_string(),
        value: MetricValue::Counter(1),
    })
    .expect("record_metric failed");
});

Metric types

Variant Payload Behaviour
Counter(i64) Delta (positive or negative) Accumulated running total
Gauge(f64) Absolute value Overwritten on each call
Histogram { x: f64, y: f64 } (x, y) data point Appended with each call; use the canister timestamp as x for time-series data
Log(String) Text entry Appended with canister timestamp

The key field is the stable identifier for a metric. The name field is the human-readable label shown in the dashboard. The key is fixed on first write — changing the metric type for an existing key will trap.


Examples

Counter

Increment a running total. Pass a negative delta to decrement.

a.record_metric(Metric {
    key:   "transfers_total".to_string(),
    name:  "Total Transfers".to_string(),
    value: MetricValue::Counter(1),
})?;

Gauge

Store the latest absolute value. Useful for memory usage, queue depth, etc.

let heap_mb = (ic_cdk::api::stable_size() * 65536) as f64 / 1_048_576.0;

a.record_metric(Metric {
    key:   "heap_usage_mb".to_string(),
    name:  "Heap Usage (MB)".to_string(),
    value: MetricValue::Gauge(heap_mb),
})?;

Histogram

Append an (x, y) data point. For time-series use, pass the canister timestamp (nanoseconds) as x and the measured value as y. Useful for request latency, throughput, or any trended data.

let now = ic_cdk::api::time() as f64; // nanoseconds since epoch

a.record_metric(Metric {
    key:   "response_latency_ms".to_string(),
    name:  "Response Latency (ms)".to_string(),
    value: MetricValue::Histogram { x: now, y: 42.5 },
})?;

Log

Append a timestamped text entry. Useful for events, errors, or audit trails.

a.record_metric(Metric {
    key:   "canister_events".to_string(),
    name:  "Canister Events".to_string(),
    value: MetricValue::Log("[INFO] Upgrade completed to v2.1.0".to_string()),
})?;

Full example

use candid::Principal;
use ic_analytics_sdk::{AnalyticsClient, Metric, MetricValue};
use ic_cdk_macros::{init, update};
use std::cell::RefCell;

thread_local! {
    static ANALYTICS: RefCell<Option<AnalyticsClient>> = RefCell::new(None);
}

#[init]
fn init(analytics_canister_id: Principal) {
    ANALYTICS.with(|a| {
        *a.borrow_mut() = Some(AnalyticsClient::new(analytics_canister_id));
    });
}

#[update]
fn transfer(to: Principal, amount: u64) -> Result<(), String> {
    // ... your transfer logic ...

    ANALYTICS.with(|a| {
        if let Some(client) = a.borrow().as_ref() {
            let _ = client.record_metric(Metric {
                key:   "transfers_total".to_string(),
                name:  "Transfers".to_string(),
                value: MetricValue::Counter(1),
            });
            let _ = client.record_metric(Metric {
                key:   "transfer_log".to_string(),
                name:  "Transfer Log".to_string(),
                value: MetricValue::Log(format!("Transfer of {} to {}", amount, to)),
            });
        }
    });

    Ok(())
}

API Reference

AnalyticsClient

pub struct AnalyticsClient {
    pub backend_canister_id: Principal,
}

impl AnalyticsClient {
    /// Creates a new client pointing at the given analytics canister.
    pub fn new(backend_canister_id: Principal) -> Self;

    /// Enqueues a one-way call to record a metric. Non-blocking.
    pub fn record_metric(&self, metric: Metric) -> Result<(), String>;
}

Metric

pub struct Metric {
    /// Stable identifier used to look up this metric. Fixed on first write.
    pub key: String,
    /// Human-readable label shown in the dashboard.
    pub name: String,
    /// The value and its storage semantics.
    pub value: MetricValue,
}

MetricValue

pub enum MetricValue {
    Counter(i64),      // Delta applied to a running total
    Gauge(f64),        // Absolute value; replaces previous
    Histogram { x: f64, y: f64 }, // (x, y) data point; appended with each call
    Log(String),       // Appended with canister timestamp
}