tracing-opentelemetry

repository·v0.1.x·Indexed 19 days ago

https://github.com/tokio-rs/tracing-opentelemetry

OpenTelemetry integration for the Rust tracing ecosystem. It provides a subscriber (OpenTelemetryLayer) that connects tracing spans into traces for export to distributed tracing and metrics systems, and the OpenTelemetrySpanExt trait for managing span data, attributes, and status. Supports context activation for propagation, automatic error status mapping, and optional metrics export via a feature flag.

Tokens
7.3K
Snippets
14
Records
31
Agent score
62%

What's inside tracing-opentelemetry

  1. Overview of tracing-opentelemetry

    v0.1.x
    The tracing-opentelemetry crate provides utilities to add OpenTelemetry interoperability to the tracing framework. It provides a subscriber that connects tracing spans into traces and emits them to OpenTelemetry-compatible distributed tracing systems for processing and visualization.
  2. Visualize traces with Jaeger

    v0.1.x

    You can visualize your traces by running a collector like Jaeger in the background and then running an example to produce spans.

    1. Run Jaeger via Docker:

      docker run -d -p4317:4317 -p16686:16686 jaegertracing/all-in-one:latest
    2. Produce spans (using the provided example):

      cargo run --example opentelemetry-otlp
    3. View spans in the Jaeger UI at http://localhost:16686/.

    # Run a supported collector like jaeger in the background
    $ docker run -d -p4317:4317 -p16686:16686 jaegertracing/all-in-one:latest
    
    # Run example to produce spans (from parent examples directory)
    $ cargo run --example opentelemetry-otlp
    
    # View spans
    $ firefox http://localhost:16686/
  3. Record errors as OpenTelemetry exceptions

    v0.1.x

    By default, the layer can record tracing error fields as OpenTelemetry exceptions. This includes capturing the error message and the error chain (source errors).

    If .with_error_records_to_exceptions(false) is called, error fields are treated as standard attributes rather than being promoted to the OpenTelemetry exception semantic conventions.

    When errors are recorded as exceptions, the following attributes are typically populated:

    • exception.message: The primary error message.
    • exception.stacktrace: A collection of error messages from the error chain.
  4. How `FilteredOpenTelemetryLayer` handles event counts

    v0.1.x

    The FilteredOpenTelemetryLayer uses a specialized mechanism to track event volume without bloating the exported data.

    • Counting: Every time an event occurs on a span, the layer increments an internal EventCount stored in the span's extensions.
    • Filtering: The layer checks the provided Filter against the event metadata. If filter.enabled(...) returns false, the event is not passed to the inner OpenTelemetryLayer.
    • Exporting: When the span is closed (on_close), the layer retrieves the accumulated count and attaches it as an attribute to the OpenTelemetry span using the key otel.tracing_event_count (defined by the constant SPAN_EVENT_COUNT_FIELD).
  5. How OpenTelemetry context propagation works with tracing spans

    v0.1.x

    The OpenTelemetryLayer supports propagating OpenTelemetry context (values and spans) through tracing spans. This is controlled by .with_context_activation(bool).

    • When enabled (true): When you enter a tracing span, the current OpenTelemetry context is activated. If you create a new OpenTelemetry span while inside a tracing span, the OpenTelemetry span will correctly identify the tracing span as its parent. This allows for seamless interoperability between tracing and native OpenTelemetry instrumentation.
    • When disabled (false): OpenTelemetry context is not propagated when entering tracing spans. Spans created via tracing will not automatically inherit the OpenTelemetry context from the surrounding environment.

    You can manage the context manually using OtelContext to attach and detach values or spans.

    // Example of manual context management for propagation
    let _outer_guard = OtelContext::attach(OtelContext::default().with_value(ValueA("outer")));
    
    let root = span!(tracing::Level::TRACE, "tokio-tracing-span-parent");
    let _enter_root = root.enter();
    
    // Inside the span, the context is active
    assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
  6. Rules for mixing data types in metrics

    v0.1.x

    To ensure compatibility with the OpenTelemetry backend, follow these rules when emitting metrics:

    Floating-point numbers

    Do not mix floating-point and non-floating-point numbers for the same metric name. If a metric uses floating-point values, ensure all subsequent calls for that metric name also use floating-point types.

    // Correct: consistent use of f64
    info!(monotonic_counter.foo = 1_f64);
    info!(monotonic_counter.foo = 1.1);

    Integers

    Positive and negative integers can be mixed freely. The instrumentation assumes i64 by default. If you provide a u64, the layer will attempt to cast it to i64 internally.

    Warning: If a u64 is provided that exceeds i64::MAX, the metric will be dropped and an error will be printed to stderr to prevent overflow issues in the OpenTelemetry backend.

    // The subscriber receives an i64
    info!(counter.baz = 1);
    info!(counter.baz = -1);
    
    // The subscriber receives a u64, but casts it to i64 internally
    info!(counter.baz = 1_u64);
    
    // This will be dropped and print an error to stderr:
    info!(counter.baz = (i64::MAX as u64) + 1);
  7. Enable metrics export with the metrics feature flag

    v0.1.x
    By enabling the metrics feature flag, you gain access to the MetricsLayer type. This layer exports OpenTelemetry metrics from specifically-named events and enables the metrics feature flag on the underlying opentelemetry crate.
  8. Use special `otel.` fields to control span metadata

    v0.1.x

    You can influence how spans are exported to OpenTelemetry by adding specific fields with the otel. prefix to your tracing spans. These are treated as ordinary fields by other layers but are intercepted by tracing-opentelemetry to set OpenTelemetry-specific attributes:

    • otel.name: Overrides the span name sent to exporters. Useful for including dynamic information in the span name.
    • otel.kind: Sets the span kind. Must be a string such as "client" or "server". Defaults to internal if not specified.
    • otel.status_code: Sets the OpenTelemetry span status code.
    • otel.status_description: Sets the span description for the status. Use this only if otel.status_code is also set.
  9. How OpenTelemetryLayer handles span hierarchy and context

    v0.1.x

    The OpenTelemetryLayer manages the relationship between tracing spans and OpenTelemetry spans using two primary modes:

    1. Standard Hierarchy: By default, the layer looks for a parent tracing span. If that parent span has associated OpenTelemetry data, the new span is linked to that parent's OpenTelemetry context.
    2. Context Activation: When with_context_activation(true) is configured, the layer uses the current OpenTelemetry Context (e.g., from opentelemetry::Context::current()) to establish the parentage, rather than relying solely on the tracing span hierarchy. This is useful for integrating with non-tracing code that already manages OpenTelemetry contexts.

    If a span is marked as contextual in tracing, the layer will attempt to use the current OTel context if context activation is enabled.

  10. Automatic Error Status mapping

    v0.1.x

    The OpenTelemetryLayer automatically maps tracing events to OpenTelemetry span statuses:

    • Error Events: When a tracing event with Level::ERROR is recorded, the layer sets the OpenTelemetry span status to StatusCode::Error.
    • Error Fields: If the event contains specific error fields (handled via SpanEventVisitor), these are recorded as OpenTelemetry event attributes.

    This ensures that failures in your application are correctly reflected as errors in your observability backend (like Jaeger).

  11. Set up OpenTelemetry tracing with `layer()`

    v0.1.x

    To connect tracing spans to an OpenTelemetry-compatible system, use the layer() function to create an OpenTelemetryLayer. This layer is then integrated into a tracing_subscriber::Registry or any other subscriber that implements LookupSpan.

    Note: This crate does not support OpenTelemetry Logging; for logs, use opentelemetry-appender-tracing.

    use opentelemetry_sdk::trace::SdkTracerProvider;
    use opentelemetry::trace::{Tracer, TracerProvider as _};
    use tracing::{error, span};
    use tracing_subscriber::layer::SubscriberExt;
    use tracing_subscriber::Registry;
    
    // 1. Create an OpenTelemetry trace pipeline (e.g., printing to stdout)
    let provider = SdkTracerProvider::builder()
        .with_simple_exporter(opentelemetry_stdout::SpanExporter::default())
        .build();
    let tracer = provider.tracer("readme_example");
    
    // 2. Create the tracing layer with the configured tracer
    let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
    
    // 3. Register the layer with a subscriber
    let subscriber = Registry::default().with(telemetry);
    
    // 4. Execute code within the subscriber context
    tracing::subscriber::with_default(subscriber, || {
        let root = span!(tracing::Level::TRACE, "app_start", work_units = 2);
        let _enter = root.enter();
    
        error!("This event will be logged in the root span.");
    });