Skip to main content

Overview

The OTLP gRPC exporter provides a high-performance alternative to HTTP for sending traces, metrics, and logs. gRPC offers better throughput and lower latency, making it ideal for high-volume production environments.

Dependencies

Add these dependencies to your Cargo.toml:
Cargo.toml
[dependencies]
opentelemetry = { version = "*" }
opentelemetry_sdk = { version = "*", features = ["rt-tokio"] }
opentelemetry-otlp = { version = "*", features = ["grpc-tonic"] }
tonic = "0.12"
tokio = { version = "1", features = ["full"] }

Exporting Traces via gRPC

1

Configure OTLP gRPC Exporter

Set up the OTLP exporter with gRPC transport using Tonic.
use opentelemetry::global;
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_otlp::WithExportConfig;

fn init_tracer() -> SdkTracerProvider {
    let exporter = opentelemetry_otlp::SpanExporter::builder()
        .with_tonic()
        .with_endpoint("http://localhost:4317")
        .build()
        .expect("Failed to create OTLP gRPC exporter");

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

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

Configure TLS and Authentication

Add TLS and metadata for secure connections.
use opentelemetry_otlp::WithTonicConfig;
use tonic::metadata::{MetadataMap, MetadataValue};
use tonic::transport::ClientTlsConfig;

let mut metadata = MetadataMap::new();
metadata.insert(
    "authorization",
    MetadataValue::from_str("Bearer YOUR_API_TOKEN").unwrap(),
);

let tls_config = ClientTlsConfig::new()
    .domain_name("your-backend.com");

let exporter = opentelemetry_otlp::SpanExporter::builder()
    .with_tonic()
    .with_endpoint("https://your-backend.com:4317")
    .with_metadata(metadata)
    .with_tls_config(tls_config)
    .build()
    .expect("Failed to create OTLP gRPC exporter");
3

Use the Tracer

Create and export spans normally.
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("grpc_operation")
        .with_kind(SpanKind::Internal)
        .start(&tracer);

    // Your application logic

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

Exporting Metrics via gRPC

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

fn init_metrics() -> SdkMeterProvider {
    let exporter = opentelemetry_otlp::MetricExporter::builder()
        .with_tonic()
        .with_endpoint("http://localhost:4317")
        .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 gRPC

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_tonic()
        .with_endpoint("http://localhost:4317")
        .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
}

gRPC-Specific Configuration

Connection Settings

use std::time::Duration;
use tonic::transport::Channel;

let channel = Channel::from_static("http://localhost:4317")
    .timeout(Duration::from_secs(10))
    .connect_timeout(Duration::from_secs(5))
    .tcp_keepalive(Some(Duration::from_secs(60)));

let exporter = opentelemetry_otlp::SpanExporter::builder()
    .with_tonic()
    .with_channel(channel)
    .build()?;

Compression

use tonic::codec::CompressionEncoding;

let exporter = opentelemetry_otlp::SpanExporter::builder()
    .with_tonic()
    .with_endpoint("http://localhost:4317")
    .with_compression(CompressionEncoding::Gzip)
    .build()?;
gRPC compression can significantly reduce network bandwidth usage, especially for high-volume telemetry data.

HTTP vs gRPC Comparison

FeatureHTTPgRPC
PerformanceGoodExcellent
LatencyHigherLower
ThroughputGoodBetter
Ease of DebugEasier (curl, browsers)Harder (needs gRPC tools)
Firewall FriendlyMore (port 80/443)Less (custom ports)
CompressionOptionalBuilt-in, efficient
StreamingNoYes
For most production use cases, gRPC is recommended due to better performance. Use HTTP if you have strict firewall requirements or need easier debugging.

Environment Variables

The OTLP gRPC exporter respects standard OpenTelemetry environment variables:
# Endpoint
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

# Signal-specific endpoints
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://localhost:4317

# Headers (sent as gRPC metadata)
export OTEL_EXPORTER_OTLP_HEADERS="api-key=secret,x-custom-header=value"

# Protocol (should be 'grpc')
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc

# Timeout (milliseconds)
export OTEL_EXPORTER_OTLP_TIMEOUT=10000

Common Endpoints

Local OpenTelemetry Collector

// Default gRPC port
.with_endpoint("http://localhost:4317")

Secure Connection

use tonic::transport::ClientTlsConfig;

let tls_config = ClientTlsConfig::new();

let exporter = opentelemetry_otlp::SpanExporter::builder()
    .with_tonic()
    .with_endpoint("https://otel-collector.example.com:4317")
    .with_tls_config(tls_config)
    .build()?;

Complete Example: All Signals

Here’s a complete example exporting traces, metrics, and logs via gRPC:
use opentelemetry::global;
use opentelemetry_sdk::{trace::SdkTracerProvider, metrics::SdkMeterProvider, logs::SdkLoggerProvider};
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use tracing_subscriber::prelude::*;

fn init_telemetry() -> (SdkTracerProvider, SdkMeterProvider, SdkLoggerProvider) {
    let endpoint = "http://localhost:4317";

    // Traces
    let trace_exporter = opentelemetry_otlp::SpanExporter::builder()
        .with_tonic()
        .with_endpoint(endpoint)
        .build()
        .expect("Failed to create trace exporter");
    
    let tracer_provider = SdkTracerProvider::builder()
        .with_batch_exporter(trace_exporter, opentelemetry_sdk::runtime::Tokio)
        .build();
    global::set_tracer_provider(tracer_provider.clone());

    // Metrics
    let metric_exporter = opentelemetry_otlp::MetricExporter::builder()
        .with_tonic()
        .with_endpoint(endpoint)
        .build()
        .expect("Failed to create metric exporter");
    
    let meter_provider = SdkMeterProvider::builder()
        .with_periodic_exporter(metric_exporter, opentelemetry_sdk::runtime::Tokio)
        .build();
    global::set_meter_provider(meter_provider.clone());

    // Logs
    let log_exporter = opentelemetry_otlp::LogExporter::builder()
        .with_tonic()
        .with_endpoint(endpoint)
        .build()
        .expect("Failed to create log exporter");
    
    let logger_provider = SdkLoggerProvider::builder()
        .with_batch_exporter(log_exporter, opentelemetry_sdk::runtime::Tokio)
        .build();
    
    let otel_layer = OpenTelemetryTracingBridge::new(&logger_provider);
    tracing_subscriber::registry().with(otel_layer).init();

    (tracer_provider, meter_provider, logger_provider)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (tracer_provider, meter_provider, logger_provider) = init_telemetry();

    // Use telemetry in your application

    // Shutdown all providers
    tracer_provider.shutdown()?;
    meter_provider.shutdown()?;
    logger_provider.shutdown()?;
    
    Ok(())
}
Always shutdown all providers in the correct order (traces, metrics, logs) to ensure all telemetry data is flushed before the application exits.

Build docs developers (and LLMs) love