Skip to main content

opentelemetry-http

Version: 0.31.0 The opentelemetry-http crate provides HTTP-related utilities for OpenTelemetry, including context propagation helpers and HTTP client abstractions for exporting telemetry.

Installation

[dependencies]
opentelemetry-http = "0.31"

Feature Flags

HTTP Client Implementations:
  • reqwest: Async reqwest client
  • reqwest-blocking: Blocking reqwest client
  • reqwest-rustls: Reqwest with rustls TLS
  • reqwest-rustls-webpki-roots: Reqwest with webpki trust roots
  • hyper: Hyper client
Other:
  • internal-logs: Enable internal logging (enabled by default)

Context Propagation

Injecting Context into HTTP Requests

HeaderInjector
struct
Helper for injecting OpenTelemetry context into HTTP headers
use opentelemetry::{global, Context};
use opentelemetry_http::HeaderInjector;
use http::HeaderMap;

let mut headers = HeaderMap::new();
let cx = Context::current();

global::get_text_map_propagator(|propagator| {
    propagator.inject_context(&cx, &mut HeaderInjector(&mut headers));
});

// headers now contains propagated context (e.g., traceparent, tracestate)

Extracting Context from HTTP Requests

HeaderExtractor
struct
Helper for extracting OpenTelemetry context from HTTP headers
use opentelemetry::global;
use opentelemetry_http::HeaderExtractor;
use http::HeaderMap;

let headers: HeaderMap = /* incoming request headers */;

let parent_cx = global::get_text_map_propagator(|propagator| {
    propagator.extract(&HeaderExtractor(&headers))
});

// Use parent_cx as parent context for new spans

HTTP Client Trait

HttpClient
trait
Minimal interface for sending HTTP requests. Used by exporters to send telemetry data.
use opentelemetry_http::{HttpClient, HttpError};
use async_trait::async_trait;
use http::{Request, Response};
use bytes::Bytes;

#[derive(Debug)]
struct MyHttpClient;

#[async_trait]
impl HttpClient for MyHttpClient {
    async fn send_bytes(&self, request: Request<Bytes>) 
        -> Result<Response<Bytes>, HttpError> {
        // Your HTTP client implementation
        todo!()
    }
}

Built-in HTTP Clients

Reqwest Client (Async)

[dependencies]
opentelemetry-http = { version = "0.31", features = ["reqwest"] }
reqwest = "0.12"
use opentelemetry_http::HttpClient;

let client = reqwest::Client::new();
// client implements HttpClient trait

Reqwest Blocking Client

[dependencies]
opentelemetry-http = { version = "0.31", features = ["reqwest-blocking"] }
reqwest = { version = "0.12", features = ["blocking"] }
use opentelemetry_http::HttpClient;

let client = reqwest::blocking::Client::new();
// client implements HttpClient trait

Hyper Client

[dependencies]
opentelemetry-http = { version = "0.31", features = ["hyper"] }
hyper = "1.0"
tokio = { version = "1.0", features = ["full"] }
use opentelemetry_http::hyper::HyperClient;
use std::time::Duration;

let client = HyperClient::with_default_connector(
    Duration::from_secs(10), // timeout
    None, // optional authorization header
);

Complete Example

Context Propagation in HTTP Server

use opentelemetry::{global, trace::{Tracer, TraceContextExt}};
use opentelemetry_http::{HeaderExtractor, HeaderInjector};
use http::{Request, Response, HeaderMap};

fn handle_request(req: Request<Vec<u8>>) -> Response<Vec<u8>> {
    // Extract parent context from incoming headers
    let parent_cx = global::get_text_map_propagator(|propagator| {
        propagator.extract(&HeaderExtractor(req.headers()))
    });

    let tracer = global::tracer("http-server");
    
    // Start span with extracted parent context
    let span = tracer
        .span_builder("handle_request")
        .start_with_context(&tracer, &parent_cx);
    let cx = parent_cx.with_span(span);

    // Do work...

    // If making downstream request, inject context
    let mut headers = HeaderMap::new();
    global::get_text_map_propagator(|propagator| {
        propagator.inject_context(&cx, &mut HeaderInjector(&mut headers));
    });

    Response::new(vec![])
}

Using Custom HTTP Client with Exporter

use opentelemetry_http::{HttpClient, HttpError};
use opentelemetry_otlp::SpanExporter;
use async_trait::async_trait;
use http::{Request, Response};
use bytes::Bytes;

#[derive(Debug)]
struct CustomClient {
    // Your client implementation
}

#[async_trait]
impl HttpClient for CustomClient {
    async fn send_bytes(&self, request: Request<Bytes>) 
        -> Result<Response<Bytes>, HttpError> {
        // Custom HTTP logic
        todo!()
    }
}

let exporter = SpanExporter::builder()
    .with_http_client(CustomClient { /* ... */ })
    .build()?;

Response Extension Trait

ResponseExt
trait
Extension methods for http::Response
use opentelemetry_http::ResponseExt;
use http::Response;

let response: Response<Vec<u8>> = /* ... */;

// Convert 4xx/5xx responses to errors
let response = response.error_for_status()?;

Types

HttpError
type
Type alias for boxed error: Box<dyn std::error::Error + Send + Sync + 'static>
Bytes
re-export
Re-exported from bytes crate for convenience
Request
re-export
Re-exported from http crate: http::Request
Response
re-export
Re-exported from http crate: http::Response

Use Cases

Distributed Tracing

Propagate trace context across HTTP service boundaries

Custom Exporters

Implement custom HTTP-based telemetry exporters

Client Selection

Choose HTTP client based on async runtime and performance needs

Testing

Mock HTTP client for testing exporters

Example: Propagation in Web Framework

With Axum

use axum::{extract::Request, middleware::Next, response::Response};
use opentelemetry::{global, trace::{Tracer, TraceContextExt}};
use opentelemetry_http::HeaderExtractor;

async fn trace_middleware(req: Request, next: Next) -> Response {
    let parent_cx = global::get_text_map_propagator(|propagator| {
        propagator.extract(&HeaderExtractor(req.headers()))
    });

    let tracer = global::tracer("axum-server");
    let span = tracer
        .span_builder(format!("{} {}", req.method(), req.uri().path()))
        .start_with_context(&tracer, &parent_cx);
    
    let cx = parent_cx.with_span(span);
    let _guard = cx.attach();

    next.run(req).await
}

Documentation

Full API Documentation

View complete API reference on docs.rs

Context Propagation Example

Full working example on GitHub

Minimum Rust Version

MSRV: 1.75.0

Build docs developers (and LLMs) love