All Integrations
LanguagesCargo crate

Axum Integration

Instrument Tokio-based Rust Axum applications with one Cargo crate. Tower middleware tracing, extractor spans, Tokio runtime metrics — zero unsafe code required.

Setup

How It Works

01

Add the Cargo Crate

Add tigerops = { version = "0.1", features = ["axum"] } to your Cargo.toml. The crate integrates with tower::ServiceBuilder, the tracing crate, and opentelemetry-rust to provide a zero-overhead instrumentation layer for Axum.

02

Add Tower Middleware Layer

Build your Router with .layer(tigerops::axum::layer()). The layer uses tower::Layer and implements tower::Service to wrap every handler with trace context propagation and automatic span creation without touching handler signatures.

03

Set Environment Variables

Set TIGEROPS_API_KEY, TIGEROPS_SERVICE_NAME, and TIGEROPS_ENVIRONMENT before starting your Tokio runtime. Call tigerops::init() at the top of your main function. The dotenvy crate is supported for .env file loading in development.

04

Traces, Extractors & Tokio Metrics

Within seconds TigerOps receives Axum route spans, extractor deserialization times, tower middleware stack timing, Tokio task metrics, and outbound reqwest HTTP client spans from your Rust Axum service.

Capabilities

What You Get Out of the Box

Tower Middleware Layer

The TigerOps tower Layer wraps every Axum handler with a root span containing the matched route pattern, HTTP method, response status, peer address, and latency. Nested routers and merge() routers are both correctly attributed.

Extractor Span Support

Axum extractors (Json<T>, Form<T>, Path<T>, Query<T>, Extension<T>) can be wrapped with tigerops::extract::Traced<E> to measure deserialization time. Rejection errors are recorded as span events with the rejection reason.

tracing Crate Integration

TigerOps installs an OpenTelemetry tracing subscriber layer so #[tracing::instrument] annotations on async handler functions automatically create child spans. Structured fields from instrument macros become typed span attributes.

Tokio Runtime Metrics

Via tokio-metrics, TigerOps collects task poll duration, task schedule delay, worker steal count, overflow queue depth, and IO driver ready events. These are emitted as OTLP metrics and surfaced in TigerOps dashboards.

reqwest & hyper Client Tracing

Outbound HTTP requests via reqwest or hyper made inside Axum handlers are auto-traced as child spans. W3C traceparent headers are injected via a reqwest middleware, enabling distributed traces across downstream services.

SQLx & SeaORM Query Spans

SQL queries issued via sqlx or SeaORM from within handler tasks become child spans with normalized SQL, target database, execution time, and row count. sqlx query cache hit rates are tracked as counters per query hash.

Configuration

Install & Initialize

One Cargo crate. One tower layer. Full Axum observability.

Cargo.toml + main.rs
# Cargo.toml
[dependencies]
axum     = "0.8"
tigerops = { version = "0.1", features = ["axum", "sqlx"] }
tokio    = { version = "1", features = ["full"] }
tracing  = "0.1"
tower    = "0.5"

# Set environment variables
# export TIGEROPS_API_KEY="your-api-key"
# export TIGEROPS_SERVICE_NAME="my-axum-app"
# export TIGEROPS_ENVIRONMENT="production"

// main.rs
use axum::{Router, routing::post, extract::Json};
use tigerops::axum::layer as tigerops_layer;
use tracing::instrument;

#[instrument(fields(items = body.items.len()))]
async fn create_order(Json(body): Json<OrderRequest>) -> Json<Order> {
    let order = process_order(body).await;
    Json(order)
}

#[tokio::main]
async fn main() {
    tigerops::init().expect("TigerOps init failed");

    let app = Router::new()
        .route("/orders", post(create_order))
        .route("/health", axum::routing::get(|| async { "ok" }))
        .layer(tigerops_layer());  // <-- single layer call

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .unwrap();
    axum::serve(listener, app).await.unwrap();
}
FAQ

Common Questions

Which Axum and Tokio versions are supported?

Axum 0.7.x and 0.8.x are fully supported with Tokio 1.x on Rust stable 1.75+. The crate is compatible with axum-extra, axum-login, and any tower-compatible middleware. Axum 0.6.x requires the tigerops-axum06 compatibility shim.

How does TigerOps handle Axum state extraction with State<T>?

State<T> extractors are not traced by default since state cloning is expected to be cheap. For expensive state operations, use tigerops::extract::Traced<State<T>> to measure the clone and any lazy initialization inside the state type.

Does TigerOps support Axum WebSocket upgrades?

Yes. Import tigerops::axum::ws::traced_ws and use it as a drop-in wrapper around axum::extract::WebSocketUpgrade. Connect, disconnect, send, and receive events create span events on a long-lived WebSocket span.

Can I use TigerOps with Axum running in a Lambda function via lambda_http?

Yes. The crate detects the Lambda execution environment and switches to a synchronous OTLP flush before each invocation ends. Cold start duration is tagged on the first request span in each Lambda worker instance.

How do I propagate trace context into background tasks with tokio::spawn?

Use tigerops::spawn(async move { ... }) which captures the current OpenTelemetry context and restores it inside the spawned task. The task span is linked to the parent span rather than being a child to correctly model asynchronous fan-out.

Get Started

Full Axum Observability in One Cargo Crate

Tower middleware traces, extractor spans, Tokio metrics, and SQLx query visibility — no code changes required.