Skip to main content

Overview

The OTLP (OpenTelemetry Protocol) HTTP exporter allows you to send traces, metrics, and logs to any OpenTelemetry-compatible backend over HTTP. This is the recommended protocol for production deployments.

Dependencies

Add these dependencies to your Cargo.toml:
Cargo.toml
[dependencies]
opentelemetry = { version = "*" }
opentelemetry_sdk = { version = "*", features = ["rt-tokio"] }
opentelemetry-otlp = { version = "*", features = ["http-proto", "reqwest-client"] }
tokio = { version = "1", features = ["full"] }

Exporting Traces via HTTP

1

Configure OTLP HTTP Exporter

Set up the OTLP exporter with HTTP transport.
use opentelemetry::global;
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_otlp::{Protocol, WithExportConfig};

fn init_tracer() -> SdkTracerProvider {
    let exporter = opentelemetry_otlp::SpanExporter::builder()
        .with_http()
        .with_protocol(Protocol::HttpBinary) // or Protocol::HttpJson
        .with_endpoint("http://localhost:4318/v1/traces")
        .build()
        .expect("Failed to create OTLP exporter");

    let provider = SdkTracerProvider::builder()
        .with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
        .build();

    global::set_tracer_provider(provider.clone());
    provider
}
2

Configure Authentication

Add headers for authentication if required by your backend.
use opentelemetry_otlp::WithHttpConfig;
use std::collections::HashMap;

let mut headers = HashMap::new();
headers.insert(
    "Authorization".to_string(),
    "Bearer YOUR_API_TOKEN".to_string(),
);

let exporter = opentelemetry_otlp::SpanExporter::builder()
    .with_http()
    .with_protocol(Protocol::HttpBinary)
    .with_endpoint("https://your-backend.com/v1/traces")
    .with_headers(headers)
    .build()
    .expect("Failed to create OTLP exporter");
3

Use the Tracer

Create and export spans as usual.
use opentelemetry::trace::{Tracer, SpanKind};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let tracer_provider = init_tracer();
    let tracer = global::tracer("my-service");

    let span = tracer
        .span_builder("operation")
        .with_kind(SpanKind::Internal)
        .start(&tracer);

    // Your application logic

    tracer_provider.shutdown()?;
    Ok(())
}

Exporting Metrics via HTTP

use opentelemetry::global;
use opentelemetry_sdk::metrics::SdkMeterProvider;
use opentelemetry_otlp::WithExportConfig;

fn init_metrics() -> SdkMeterProvider {
    let exporter = opentelemetry_otlp::MetricExporter::builder()
        .with_http()
        .with_protocol(Protocol::HttpBinary)
        .with_endpoint("http://localhost:4318/v1/metrics")
        .build()
        .expect("Failed to create OTLP metrics exporter");

    let provider = SdkMeterProvider::builder()
        .with_periodic_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
        .build();

    global::set_meter_provider(provider.clone());
    provider
}

Exporting Logs via HTTP

use opentelemetry_sdk::logs::SdkLoggerProvider;
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use tracing_subscriber::prelude::*;

fn init_logs() -> SdkLoggerProvider {
    let exporter = opentelemetry_otlp::LogExporter::builder()
        .with_http()
        .with_protocol(Protocol::HttpBinary)
        .with_endpoint("http://localhost:4318/v1/logs")
        .build()
        .expect("Failed to create OTLP log exporter");

    let provider = SdkLoggerProvider::builder()
        .with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
        .build();

    let otel_layer = OpenTelemetryTracingBridge::new(&provider);
    tracing_subscriber::registry().with(otel_layer).init();

    provider
}

Protocol Options

The OTLP HTTP exporter supports two protocols:
use opentelemetry_otlp::Protocol;

// More efficient, smaller payload size
.with_protocol(Protocol::HttpBinary)
Use Protocol::HttpBinary for production as it’s more efficient. Use Protocol::HttpJson for development and debugging.

Common Backend Endpoints

Local OpenTelemetry Collector

// Traces
.with_endpoint("http://localhost:4318/v1/traces")

// Metrics
.with_endpoint("http://localhost:4318/v1/metrics")

// Logs
.with_endpoint("http://localhost:4318/v1/logs")

Cloud Providers

Most cloud providers require custom endpoints and authentication headers:
let mut headers = HashMap::new();
headers.insert("X-API-Key".to_string(), env::var("OTEL_API_KEY")?);;

let exporter = opentelemetry_otlp::SpanExporter::builder()
    .with_http()
    .with_endpoint("https://otlp.your-provider.com/v1/traces")
    .with_headers(headers)
    .build()?;

Configuration via Environment Variables

The OTLP exporter respects standard OpenTelemetry environment variables:
# Endpoint (automatically appends signal-specific paths)
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318

# Signal-specific endpoints
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:4318/v1/metrics
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://localhost:4318/v1/logs

# Headers (comma-separated key=value pairs)
export OTEL_EXPORTER_OTLP_HEADERS="api-key=secret,x-custom-header=value"

# Protocol
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf  # or http/json

# Timeout (milliseconds)
export OTEL_EXPORTER_OTLP_TIMEOUT=10000
When using environment variables, you can create exporters without explicit configuration in code. The SDK will automatically read these values.

Batch Configuration

Configure batching behavior for better performance:
use opentelemetry_sdk::trace::BatchConfig;
use std::time::Duration;

let batch_config = BatchConfig::default()
    .with_max_queue_size(2048)
    .with_max_export_batch_size(512)
    .with_scheduled_delay(Duration::from_secs(5));

let provider = SdkTracerProvider::builder()
    .with_batch_exporter(
        exporter,
        opentelemetry_sdk::runtime::Tokio,
    )
    .with_batch_config(batch_config)
    .build();

Error Handling

use opentelemetry_otlp::Error;

let exporter_result = opentelemetry_otlp::SpanExporter::builder()
    .with_http()
    .with_endpoint("http://localhost:4318/v1/traces")
    .build();

match exporter_result {
    Ok(exporter) => {
        // Use exporter
    }
    Err(Error::NoValidEndpoint) => {
        eprintln!("Invalid OTLP endpoint");
    }
    Err(e) => {
        eprintln!("OTLP exporter error: {}", e);
    }
}
Always handle exporter initialization errors gracefully. In production, you may want to fall back to a stdout exporter or disable telemetry if the backend is unavailable.

Build docs developers (and LLMs) love