# Rust

> Axum, Actix and Tokio services through the tracing ecosystem

Rust has no runtime agent. In practice you keep the `tracing` crate you already have and
bridge it to OpenTelemetry, so existing `#[instrument]` attributes start being exported
without rewriting them.

## Add the crates

```bash
cargo add opentelemetry opentelemetry_sdk
cargo add opentelemetry-otlp --features http-proto,reqwest-client
cargo add tracing tracing-subscriber tracing-opentelemetry
```

> **Warning:** The `http-proto` feature matters. Without it the exporter is built for gRPC and will not
  talk to the HTTP ingress.

## Initialise

```rust
use opentelemetry::{global, KeyValue};
use opentelemetry_otlp::{Protocol, WithExportConfig};
use opentelemetry_sdk::{trace::SdkTracerProvider, Resource};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

fn init_telemetry() -> SdkTracerProvider {
    let exporter = opentelemetry_otlp::SpanExporter::builder()
        .with_http()
        .with_protocol(Protocol::HttpBinary)
        .with_endpoint("https://ingress.rocketlog.io/v1/traces")
        .with_headers(std::collections::HashMap::from([(
            "Authorization".to_string(),
            "Bearer rg_live_xxxxxx".to_string(),
        )]))
        .build()
        .expect("build OTLP exporter");

    let provider = SdkTracerProvider::builder()
        .with_batch_exporter(exporter)
        .with_resource(
            Resource::builder()
                .with_attributes([KeyValue::new("service.name", "my-rust-service")])
                .build(),
        )
        .build();

    global::set_tracer_provider(provider.clone());

    tracing_subscriber::registry()
        .with(tracing_subscriber::fmt::layer())
        .with(tracing_opentelemetry::layer().with_tracer(
            opentelemetry::trace::TracerProvider::tracer(&provider, "my-rust-service"),
        ))
        .init();

    provider
}
```

> **Note:** The Rust builder API has changed shape more than once across releases. If this does not
  compile, check the version Cargo resolved — the docs for that exact version are
  authoritative.

## Use it, and shut it down

```rust
#[tokio::main]
async fn main() {
    let provider = init_telemetry();

    run_server().await;

    // Not optional: the batch exporter holds spans in memory and nothing
    // flushes them for you on exit.
    provider.shutdown().expect("flush telemetry");
}

#[tracing::instrument(fields(transfer.id = %transfer_id))]
async fn settle_transfer(transfer_id: &str) -> Result<(), Error> {
    Ok(())
}
```
