Search code examples
rustopen-telemetryjaeger

Export OTLP traces to Jaeger


When I attempt to export OTLP traces to Jaeger, I get this error:

OpenTelemetry trace error occurred. Exporter otlp encountered the following error(s): the grpc server returns error (Unknown error): , detailed error message: Service was not ready: transport error

My understanding is that might be an interaction between tokio and tonic, but I don't control the creation of this client. I couldn't get grpc to work either, and http is basically undocumented.

Reproducible sample

use opentelemetry::trace::Tracer;
use opentelemetry_otlp::WithExportConfig;

#[tokio::main]
async fn main() {
    let otlp_exporter = opentelemetry_otlp::new_exporter().tonic().with_endpoint("http://jaeger:4317/");
    let tracer = opentelemetry_otlp::new_pipeline()
        .tracing()
        .with_exporter(otlp_exporter)
        .install_simple()
        .unwrap();

    tracer.in_span("xyz", |_| {});
}
[package]
name = "sample"
version = "0.1.0"
edition = "2021"

[dependencies]
opentelemetry = "0.18.0"
opentelemetry-otlp = "0.11"
tokio = { version = "1.28", features = ["rt-multi-thread"] }

Jaeger is running in an adjacent container with the DNS name 'jaeger', which is resolvable. Here's the compose file (app is the devcontainer where the Rust code runs):

version: '3.8'
services:
  app:
    image: mcr.microsoft.com/devcontainers/base:jammy
    volumes:
      - ../..:/workspaces:cached
    command: sleep infinity
    network_mode: service:jaeger
  jaeger:
    image: jaegertracing/all-in-one:1.44
    restart: unless-stopped

Solution

  • Thanks to Pierre who pointed me along to the reference implementation, I was able to find the root cause was that Jaeger by default does not accept OTLP (the docs don't make this very clear). If you set the environment variable COLLECTOR_OTLP_ENABLED=true on Jaeger, it will start accepting on 4317.

    My updated sample looks like this:

    use opentelemetry::sdk::propagation::TraceContextPropagator;
    use opentelemetry_otlp::WithExportConfig;
    use tracing::metadata::LevelFilter;
    use tracing_subscriber::{Registry, Layer};
    use tracing_subscriber::layer::SubscriberExt;
    
    #[tokio::main]
    async fn main() {
        opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new());
        let otlp_exporter = opentelemetry_otlp::new_exporter().tonic().with_endpoint("http://jaeger:4317/v1/traces");
        let tracer = opentelemetry_otlp::new_pipeline()
            .tracing()
            .with_exporter(otlp_exporter)
            .with_trace_config(opentelemetry::sdk::trace::config().with_resource(
                opentelemetry::sdk::Resource::new(vec![opentelemetry::KeyValue::new("service.name", "sample")]),
            ))
            .install_simple()
            .unwrap();
    
        let telemetry = tracing_opentelemetry::layer().with_tracer(tracer).with_filter(LevelFilter::INFO);
        let subscriber = Registry::default().with(telemetry);
        tracing::subscriber::set_global_default(subscriber).unwrap();
    
        // ... do some tracing!
    }
    
    
    [package]
    name = "sample"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    opentelemetry = "0.18.0"
    opentelemetry-otlp = "0.11"
    tokio = { version = "1.28", features = ["rt-multi-thread"] }
    tracing = "0.1.37"
    tracing-opentelemetry = "0.18"
    tracing-subscriber = "0.3.17"