# Rust SDK

Use the `cronitor-sdk` crate to report job runs, heartbeats, and metrics to Cronitor from Rust 1.75 or later.

## Install

```bash
cargo add cronitor-sdk
```

The crate is published as `cronitor-sdk`. Import it as `cronitor`.

## Configure

Set the `CRONITOR_API_KEY` environment variable to your SDK Integration key. Copy the key from Settings → API Keys at https://cronitor.io/app/settings/api. Keep the key in the environment or a secret store of the runtime. Do not commit it.

Make the client with `Client::from_env()`. It reads `CRONITOR_API_KEY`. `Config::new()` does not read environment variables.

```rust
use cronitor::Client;

let client = Client::from_env();
```

The API is blocking. In an async runtime, call it through `tokio::task::spawn_blocking` or the equivalent of your runtime.

## Monitor a job

Wrap the main function of the job with `client.job`. The function must return a `Result`:

```rust
use cronitor::Client;

fn send_invoices() -> Result<usize, String> {
    // the work of the job
    Ok(42)
}

fn main() {
    let client = Client::from_env();
    if let Err(err) = client.job("send-invoices", send_invoices) {
        eprintln!("send-invoices failed: {err}");
        std::process::exit(1);
    }
}
```

- Before the function starts, the SDK sends a `run` event.
- When the function returns `Ok`, the SDK sends a `complete` event with the elapsed time as the `duration` metric.
- When the function returns `Err` or panics, the SDK sends a `fail` event with the error text and the `duration` metric. `client.job` gives back the result unchanged, or panics again.
- The events go to the monitor with the key `send-invoices`. If no monitor has this key, Cronitor creates one on the first event.

The SDK sends the `Ok` value as the message of the `complete` event. To not send it, use `client.job_with(JobOptions::new("send-invoices").log_output(false), send_invoices)`.

To send the events yourself, use a monitor:

```rust
use cronitor::{PingOptions, State};

let monitor = client.monitor("send-invoices");
monitor.ping(PingOptions::new().state(State::Run))?;
monitor.ping(PingOptions::new().state(State::Complete))?; // or State::Fail
```

## Send a heartbeat

Send a ping each time the process completes a unit of useful work:

```rust
use cronitor::{Client, PingOptions};

fn main() -> cronitor::Result<()> {
    let client = Client::from_env();
    let monitor = client.monitor("queue-worker");
    monitor.ping(PingOptions::new().message("Alive!"))?;
    Ok(())
}
```

To attach metrics, call `.metric(name, value)` once for each metric. The SDK sends each one as `metric=name:value`:

```rust
monitor.ping(PingOptions::new().metric("count", 100).metric("error_count", 3))?;
```

If you only want to record custom metrics, use a heartbeat monitor. See [Custom Metrics](https://cronitor.io/docs/custom-metrics.md).

## Verify

After one real run, check the state of the monitor. A successful telemetry response shows only that Cronitor received the request. It does not show that Cronitor stored the event or matched it to a monitor.

With the [MCP server](https://cronitor.io/docs/mcp-server.md):

```text
get_status({"key": "send-invoices"})
```

With [CronitorCLI](https://cronitor.io/docs/using-cronitor-cli.md):

```bash
cronitor status send-invoices
```

If `CRONITOR_API_KEY` is not set, `ping` sends no events and returns `Ok(())`. It logs the error through the [`log`](https://docs.rs/log) facade, so you see it only when the program installs a logger.

## Reference

- [cronitor-rust on GitHub](https://github.com/cronitorio/cronitor-rust): the full README, including monitor attributes and YAML configuration.
- [Telemetry API](https://cronitor.io/docs/telemetry-api.md): the events and parameters that the SDK sends.
