Skip to main content

Overview

Propagators are used to extract and inject context data across service boundaries. They enable distributed tracing by passing trace context and baggage between services using standard formats like W3C Trace Context.

Injector

Injects context into outgoing requests

Extractor

Extracts context from incoming requests

TextMapPropagator

The TextMapPropagator trait defines the interface for context propagation using string key-value pairs:
pub trait TextMapPropagator {
    /// Injects context into a carrier
    fn inject_context(&self, cx: &Context, injector: &mut dyn Injector);
    
    /// Extracts context from a carrier
    fn extract_with_context(&self, cx: &Context, extractor: &dyn Extractor) -> Context;
    
    /// Returns the fields set by this propagator
    fn fields(&self) -> Vec<String>;
}

Built-in propagators

OpenTelemetry Rust provides several standard propagators:

W3C Trace Context

The default propagator implementing the W3C Trace Context specification:
use opentelemetry::global;
use opentelemetry_sdk::propagation::TraceContextPropagator;

// Set the global propagator
global::set_text_map_propagator(TraceContextPropagator::new());

W3C Baggage

Propagates baggage according to the W3C Baggage specification:
use opentelemetry_sdk::propagation::BaggagePropagator;

global::set_text_map_propagator(BaggagePropagator::new());

Composite propagator

Combine multiple propagators to support different formats:
use opentelemetry::global;
use opentelemetry::propagation::TextMapCompositePropagator;
use opentelemetry_sdk::propagation::{TraceContextPropagator, BaggagePropagator};

let propagator = TextMapCompositePropagator::new(vec![
    Box::new(TraceContextPropagator::new()),
    Box::new(BaggagePropagator::new()),
]);

global::set_text_map_propagator(propagator);

Jaeger propagator

For Jaeger-specific propagation format, use the separate opentelemetry-jaeger-propagator crate:
use opentelemetry_jaeger_propagator::Propagator as JaegerPropagator;

global::set_text_map_propagator(JaegerPropagator::new());

Injector and Extractor traits

Injector

The Injector trait is implemented by carriers that can have context injected into them:
pub trait Injector {
    /// Add a key and value to the underlying data
    fn set(&mut self, key: &str, value: String);
    
    /// Hint to reserve capacity for additional entries
    fn reserve(&mut self, additional: usize) {}
}
HashMap implementation:
use std::collections::HashMap;
use opentelemetry::propagation::Injector;

let mut headers = HashMap::new();
headers.set("traceparent", "00-trace-id-span-id-01".to_string());

Extractor

The Extractor trait is implemented by carriers that can have context extracted from them:
pub trait Extractor {
    /// Get a value from a key
    fn get(&self, key: &str) -> Option<&str>;
    
    /// Collect all keys
    fn keys(&self) -> Vec<&str>;
    
    /// Get all values from a key
    fn get_all(&self, key: &str) -> Option<Vec<&str>>;
}

HTTP propagation example

Client-side injection

Inject context into HTTP headers:
use opentelemetry::global;
use opentelemetry::Context;
use std::collections::HashMap;

// Get the current context with active span
let cx = Context::current();

// Prepare headers
let mut headers = HashMap::new();

// Inject context into headers
global::get_text_map_propagator(|propagator| {
    propagator.inject_context(&cx, &mut headers);
});

// Use headers in HTTP request
for (key, value) in headers {
    // request.header(key, value);
}

Server-side extraction

Extract context from incoming HTTP headers:
use opentelemetry::global;
use opentelemetry::Context;
use std::collections::HashMap;

// Extract headers from incoming request
let headers: HashMap<String, String> = get_request_headers();

// Extract context from headers
let parent_cx = global::get_text_map_propagator(|propagator| {
    propagator.extract(&headers)
});

// Use the extracted context to create child spans
let tracer = global::tracer("my-service");
let span = tracer
    .span_builder("handle_request")
    .start_with_context(&tracer, &parent_cx);

Custom propagator

Implement a custom propagator for non-standard formats:
use opentelemetry::propagation::{Extractor, Injector, TextMapPropagator};
use opentelemetry::Context;

struct CustomPropagator;

impl TextMapPropagator for CustomPropagator {
    fn inject_context(&self, cx: &Context, injector: &mut dyn Injector) {
        // Extract trace context from cx
        // Format according to custom protocol
        // Inject into carrier
        injector.set("custom-trace-id", "...".to_string());
    }
    
    fn extract_with_context(
        &self,
        cx: &Context,
        extractor: &dyn Extractor,
    ) -> Context {
        // Extract custom headers
        if let Some(trace_id) = extractor.get("custom-trace-id") {
            // Parse trace context
            // Create new context with extracted data
        }
        cx.clone()
    }
    
    fn fields(&self) -> Vec<String> {
        vec!["custom-trace-id".to_string()]
    }
}

Best practices

Always use composite propagator - Combine TraceContext and Baggage propagators to support both trace context and custom attributes.
Case-insensitive extraction - The HashMap implementations handle case-insensitive header names automatically by converting to lowercase.
Security consideration - Be cautious with baggage content as it propagates across all services. Avoid putting sensitive data in baggage.

Context propagation

Learn about context flow

Baggage

Add custom attributes to context

Build docs developers (and LLMs) love