ic-analytics-sdk 0.2.1

IC Analytics SDK
Documentation
# ic-analytics-sdk

Rust SDK for sending metrics from any Internet Computer canister to an
[ICAnalytics](https://icanalytics.io) analytics canister.

## Installation

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

```toml
[dependencies]
ic-analytics-sdk = "0.2.1"
```

Or with `cargo add`:

```bash
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!`.

```rust
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-forget** — `record_metric` enqueues a one-way
inter-canister call that does not block your canister's execution.

```rust
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 |
| `TimeSeries(f64)` | Data point | Appended with canister timestamp |
| `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.

```rust
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.

```rust
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),
})?;
```

### Time Series

Append a data point with the current canister timestamp. Useful for request
rates, latency, or any trend data.

```rust
a.record_metric(Metric {
    key:   "response_latency_ms".to_string(),
    name:  "Response Latency (ms)".to_string(),
    value: MetricValue::TimeSeries(42.5),
})?;
```

### Log

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

```rust
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

```rust
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`

```rust
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`

```rust
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`

```rust
pub enum MetricValue {
    Counter(i64),      // Delta applied to a running total
    Gauge(f64),        // Absolute value; replaces previous
    TimeSeries(f64),   // Appended with canister timestamp
    Log(String),       // Appended with canister timestamp
}
```