Rust OpenTelemetry instrumentation
Instrument a Rust service with OpenTelemetry by bridging the tracing crate to an OTLP exporter. Spans you record with tracing — and spans that libraries like Axum and Actix Web emit through it — are exported to Osuite. This guide uses opentelemetry-otlp with the Tonic (gRPC) transport and tracing-opentelemetry as the bridge.
Prerequisites
Section titled “Prerequisites”- A Rust project using the
tracingcrate for instrumentation. - A Tokio async runtime (Axum, Actix Web, and most async services provide one).
- Your Osuite ingest token and region.
Install and configure
Section titled “Install and configure”-
Add the OpenTelemetry crates to
Cargo.toml. These versions are the current 0.32 release line paired withtracing-opentelemetry0.33.[dependencies]axum = "0.8"tower-http = { version = "0.6", features = ["trace"] }tokio = { version = "1", features = ["full"] }tracing = "0.1"tracing-subscriber = { version = "0.3", features = ["env-filter"] }tracing-opentelemetry = "0.33"opentelemetry = "0.32"opentelemetry_sdk = { version = "0.32", features = ["rt-tokio"] }opentelemetry-otlp = { version = "0.32", features = ["grpc-tonic"] } -
Add a telemetry init function that builds an OTLP tracer provider and registers it with the
tracingsubscriber. Callinit_telemetry()at the top ofmain, and callprovider.shutdown()before exit so buffered spans are flushed.use opentelemetry::trace::TracerProvider as _;use opentelemetry::KeyValue;use opentelemetry_otlp::WithExportConfig;use opentelemetry_sdk::{propagation::TraceContextPropagator, trace::SdkTracerProvider, Resource};use tracing_subscriber::layer::SubscriberExt;use tracing_subscriber::util::SubscriberInitExt;fn init_telemetry() -> Result<SdkTracerProvider, Box<dyn std::error::Error>> {opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new());let exporter = opentelemetry_otlp::SpanExporter::builder().with_tonic().build()?;let provider = SdkTracerProvider::builder().with_batch_exporter(exporter).with_resource(Resource::builder_empty().with_attribute(KeyValue::new("service.name", "<service-name>")).with_attribute(KeyValue::new("service.environment", "production")).build(),).build();let tracer = provider.tracer("<service-name>");tracing_subscriber::registry().with(tracing_subscriber::EnvFilter::from_default_env()).with(tracing_opentelemetry::layer().with_tracer(tracer)).with(tracing_subscriber::fmt::layer()).init();Ok(provider)} -
Set the exporter environment variables. The Tonic exporter reads its endpoint and headers from these.
Terminal window export OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.<region>.osuite.io:443export OTEL_EXPORTER_OTLP_HEADERS=x-osuite-ingest-token=<your-ingest-token>The
https://scheme is required — the exporter takes a URL and Osuite only accepts OTLP over TLS.
Wire it into your web framework
Section titled “Wire it into your web framework”Call init_telemetry first, then add the framework’s tracing middleware so every request opens a span. For Axum, use tower-http’s TraceLayer:
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let provider = init_telemetry()?;
let app = axum::Router::new() .route("/", axum::routing::get(|| async { "ok" })) .layer(tower_http::trace::TraceLayer::new_for_http());
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?; axum::serve(listener, app).await?;
provider.shutdown()?; Ok(())}Verify in Osuite
Section titled “Verify in Osuite”What you should see
Send a request to any route. The service appears in APM within a minute with a trace per request, showing the request span and any nested spans you record with #[tracing::instrument] or tracing::span!.
Troubleshooting
Section titled “Troubleshooting”If no traces appear, confirm init_telemetry runs before any request is served and that provider.shutdown() is called on exit — without it, short-lived processes can exit before the batch exporter flushes. Check that the endpoint includes the https:// scheme and port 443.
No data in Osuite after a few minutes? Work through these checks.
- Endpoint — confirm the exporter targets exactly
ingest.<region>.osuite.io:443for your region, over TLS. - Token — confirm the ingest header carries a valid
<your-ingest-token>and has not been rotated. - Pipeline — confirm the signal you expect (traces, logs, or metrics) is wired into an active pipeline with the
otlp/osuiteexporter attached. - Export errors — check the application or Collector logs for OTLP export failures (auth, DNS, TLS, connection refused).
- Network — confirm the host has outbound access to the ingest endpoint on port
443. - Timing — allow up to a minute for the first data to appear before assuming a failure.
Still stuck? Ask the Investigation Agent or contact support.