Skip to content

Repository files navigation

otel-kit

Axum request tracing and opinionated OpenTelemetry bootstrap for Rust.

Crates.io Documentation License

Repository guide · axum-otel · tracing-otel · otel-init · Examples

otel-kit is a workspace of three crates with separate observability responsibilities:

  • axum-otel provides tower-http::TraceLayer callbacks for inbound Axum request spans;
  • tracing-otel provides shared HTTP tracing utilities and an optional, opinionated logging/bootstrap facade;
  • otel-init initializes OpenTelemetry providers and tracing subscribers and owns their shutdown lifecycle.

Choose the smallest crate that owns the behavior your application needs. Use axum-otel with an existing subscriber for focused request tracing, or combine it with tracing-otel when you want tracing, metrics, structured logs, and provider lifecycle management configured together.

📦 Crates

Crate Responsibility Use it when
axum-otel Axum-specific request span creation, response events, and failure status You need inbound Axum request tracing
tracing-otel HTTP tracing helpers, dynamic tracing macros, console/file logging, and application bootstrap You need shared tracing behavior or the Logger facade
otel-init OTLP tracer, meter, and logger providers; subscriber setup; provider shutdown You own subscriber setup or need provider-level initialization

The feature-dependent dependency direction is:

axum-otel -> tracing-otel -> otel-init

tracing-otel has no default features. Applications must explicitly enable the capabilities they use; see the Cargo feature guide.

🚀 Quick start

This example combines the Axum callbacks with the opinionated Logger facade:

[dependencies]
anyhow = "1"
axum = "0.8"
axum-otel = "0.33.1"
tokio = { version = "1", features = ["macros", "net", "rt-multi-thread"] }
tower-http = { version = "0.6", features = ["trace"] }
tracing-otel = { version = "0.33.1", features = ["logger"] }
use axum::{Router, routing::get};
use axum_otel::{
    AxumOtelOnFailure, AxumOtelOnResponse, AxumOtelSpanCreator, Level,
};
use tower_http::trace::TraceLayer;
use tracing_otel::{LogFormat, Logger};

async fn health() -> &'static str {
    "OK"
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let _guard = Logger::new("web-api")
        .with_format(LogFormat::Json)
        .with_ansi(false)
        .init()?;

    let app = Router::new().route("/health", get(health)).layer(
        TraceLayer::new_for_http()
            .make_span_with(AxumOtelSpanCreator::new().level(Level::INFO))
            .on_response(AxumOtelOnResponse::new().level(Level::INFO))
            .on_failure(AxumOtelOnFailure::new().level(Level::ERROR)),
    );

    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
    axum::serve(listener, app).await?;
    Ok(())
}

Keep the returned LoggerGuard alive for the application lifetime. It owns the OpenTelemetry providers and any non-blocking file writer guard, and shuts them down in the required order.

OTLP export is opt-in. Without a configured endpoint, providers remain local and no connection to localhost:4317 is attempted:

export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc

See Getting started, Logger configuration, and OTLP export and lifecycle for the complete setup.

🔍 Comparison with axum-tracing-opentelemetry

Both axum-otel and axum-tracing-opentelemetry help Axum applications create OpenTelemetry-aware HTTP request spans. The difference is mainly the intended scope.

axum-tracing-opentelemetry

axum-tracing-opentelemetry is a focused choice when you:

  • want request tracing and context propagation with a small middleware surface;
  • already own the tracing subscriber, exporters, metrics, and logging setup;
  • prefer to assemble the observability stack from independent components.

For applications with an established observability bootstrap, that narrower scope can be exactly what is needed.

axum-otel and this workspace

axum-otel itself stays focused on three TraceLayer callbacks:

  • AxumOtelSpanCreator creates and enriches request spans;
  • AxumOtelOnResponse records response status and emits completion events;
  • AxumOtelOnFailure marks classified server failures as OpenTelemetry errors.

The broader, opinionated setup comes from composing it with the other workspace crates. tracing-otel adds structured console/file logging and application bootstrap, while otel-init adds trace, metric, and optional log providers plus RAII lifecycle management.

Choose axum-tracing-opentelemetry when you primarily need focused middleware and already own the rest of the stack. Choose this workspace when you want the same HTTP tracing boundary plus reusable HTTP span helpers or a cohesive tracing, metrics, logging, and shutdown setup.

🔭 HTTP request spans

axum-otel builds on tower-http::TraceLayer and records OpenTelemetry-aligned HTTP attributes including method, route, URL parts, protocol, server.address, client.address when ConnectInfo<SocketAddr> is available, user_agent.original, request_id, and trace_id.

The shared span schema lives in tracing-otel; the Axum adapter owns matched route, transport peer, span name, and server span kind. See HTTP span attributes before writing dashboards, alerts, or sampling rules.

📚 Examples

  • examples/otel demonstrates one Axum service with request IDs and environment-based logger configuration.
  • examples/microservices demonstrates cross-service propagation with Grafana, Loki, and Tempo-oriented local infrastructure.

Run the basic service with:

cargo run -p axum-otel-demo

🔄 Migrating from 0.33.0

Version 0.33.1 introduced new crates.io package names. Cargo does not migrate package names automatically:

# Before
tracing-otel-extra = "0.33.0"
tracing-opentelemetry-extra = "0.33.0"

# After
tracing-otel = "0.33.1"
otel-init = "0.33.1"

Update Rust imports from tracing_otel_extra to tracing_otel and from tracing_opentelemetry_extra to otel_init. Also select explicit tracing-otel features: logger for code configuration, env for Logger::from_env, or the narrower HTTP features listed in the feature guide.

See the complete migration guide for downstream dashboard and HTTP attribute changes.

🧩 Compatibility

  • Rust 1.96.0 is the workspace compatibility baseline.
  • The repository toolchain may use a newer compiler for local development.
  • OpenTelemetry export supports gRPC, HTTP/Protobuf, and HTTP/JSON according to the configured endpoint and protocol environment variables.

📄 License

About

OpenTelemetry tracing and logging for Rust and Axum.

Topics

Resources

Stars

12 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages