Skip to main content

opentelemetry-appender-tracing

Version: 0.31.1 The opentelemetry-appender-tracing crate bridges tracing events to OpenTelemetry logs, allowing applications using the tracing ecosystem to export structured logs via OpenTelemetry.
This is different from tracing-opentelemetry, which converts tracing spans into OpenTelemetry traces. This crate specifically handles tracing events as OpenTelemetry logs.

Installation

[dependencies]
tracing = ">=0.1.40"
tracing-subscriber = { version = "0.3", features = ["registry", "std"] }
opentelemetry = { version = "0.31", features = ["logs"] }
opentelemetry-sdk = { version = "0.31", features = ["logs"] }
opentelemetry-appender-tracing = "0.31"

Feature Flags

  • experimental_metadata_attributes: Include code metadata (file path, line number, module) as attributes
  • experimental_span_attributes: Include parent span information as attributes

Quick Start

Basic Setup

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

// 1. Set up OpenTelemetry logger provider
let exporter = LogExporter::default();
let provider = SdkLoggerProvider::builder()
    .with_simple_exporter(exporter)
    .build();

// 2. Create the OpenTelemetry-tracing bridge layer
let otel_layer = OpenTelemetryTracingBridge::new(&provider);

// 3. Register with tracing subscriber
tracing_subscriber::registry()
    .with(otel_layer)
    .with(tracing_subscriber::fmt::layer()) // Optional: also log to stdout
    .init();

// 4. Use tracing as normal
tracing::info!("Application started");
tracing::error!("Something went wrong");

Structured Events

use tracing::error;

error!(
    name: "my-event-name",
    target: "my-system",
    event_id = 10,
    user_name = "alice",
    user_email = "[email protected]",
    message = "This is an example message"
);

Core Types

layer::OpenTelemetryTracingBridge
struct
A tracing_subscriber::Layer that forwards tracing events to OpenTelemetry logs
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_sdk::logs::SdkLoggerProvider;

let provider: SdkLoggerProvider = /* ... */;
let layer = OpenTelemetryTracingBridge::new(&provider);

Field Mapping

How tracing events map to OpenTelemetry logs:

Event Name

tracing events can optionally have a name, which becomes the OpenTelemetry EventName:
tracing::info!(name: "user.login", user_id = 42);
// -> LogRecord.EventName = "user.login"

Target

The target field maps to OpenTelemetry InstrumentationScope:
tracing::info!(target: "http_server", "Request received");
// -> InstrumentationScope.name = "http_server"

Severity

tracing::Level maps to OpenTelemetry severity:
tracing::LevelSeverity TextSeverity Number
ERRORError17
WARNWarn13
INFOInfo9
DEBUGDebug5
TRACETrace1

Fields → Attributes

tracing event fields become OpenTelemetry attributes, except:
  • Field named "message"LogRecord::Body
  • If no "message" field exists, the event’s formatted message → LogRecord::Body
tracing::info!(
    user_id = 42,
    action = "login",
    message = "User logged in"
);
// -> Body = "User logged in"
// -> Attributes: { user_id: 42, action: "login" }

Type Conversion

tracing TypeOpenTelemetry TypeNotes
i64AnyValue::Int
u64, u128, i128AnyValue::Int or StringConverted if fits in i64, else stringified
f32, f64AnyValue::Double
boolAnyValue::Boolean
&strAnyValue::String
&[u8]AnyValue::BytesBinary data
&dyn DebugAnyValue::StringVia Debug formatting
&dyn ErrorAnyValue::StringStored in exception.message attribute

Automatic Context Attachment

The bridge automatically attaches OpenTelemetry trace context to logs:
use tracing::info;
use opentelemetry::trace::{Tracer, TraceContextExt};
use opentelemetry::Context;

let tracer = /* get tracer */;
tracer.in_span("my_operation", |cx| {
    info!("Processing item");
    // Log automatically includes:
    // - trace_id
    // - span_id
    // - trace_flags
});

Examples

Basic Logging

use tracing::{info, warn, error};

info!("Server started on port 8080");
warn!("High memory usage detected");
error!("Database connection failed");

Named Events

use tracing::error;

error!(
    name: "database.connection.failed",
    database = "users_db",
    retry_count = 3,
    "Failed to connect after retries"
);

With Explicit Message

use tracing::info;

info!(
    user_id = 42,
    action = "purchase",
    amount = 99.99,
    message = "User completed purchase"
);

Exception Logging

use tracing::error;
use std::error::Error;

fn handle_error(err: &dyn Error) {
    error!(
        error = err as &dyn Error,
        "Operation failed"
    );
}

With Parent Spans

use tracing::{info, info_span};

let span = info_span!("request", request_id = "123");
let _guard = span.enter();

info!("Processing request");
// Log includes parent span context automatically

Integration with OTLP

use opentelemetry_otlp::LogExporter;
use opentelemetry_sdk::logs::{BatchLogProcessor, SdkLoggerProvider};
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use tracing_subscriber::prelude::*;

let exporter = LogExporter::builder()
    .with_http()
    .build()?;

let provider = SdkLoggerProvider::builder()
    .with_log_processor(BatchLogProcessor::builder(exporter).build())
    .build();

let otel_layer = OpenTelemetryTracingBridge::new(&provider);

tracing_subscriber::registry()
    .with(otel_layer)
    .init();

Combining with Tracing Spans

For converting tracing spans to OpenTelemetry traces, use the third-party tracing-opentelemetry crate. Both can be used together:
  • tracing-opentelemetry: Spans → OpenTelemetry Traces
  • opentelemetry-appender-tracing: Events → OpenTelemetry Logs
use tracing_subscriber::prelude::*;

// Hypothetical combined setup
tracing_subscriber::registry()
    .with(tracing_opentelemetry::layer()) // Spans → Traces
    .with(OpenTelemetryTracingBridge::new(&logger_provider)) // Events → Logs
    .with(tracing_subscriber::fmt::layer()) // Also print to console
    .init();

Metadata Attributes (Experimental)

With experimental_metadata_attributes feature:
[dependencies]
opentelemetry-appender-tracing = { version = "0.31", features = ["experimental_metadata_attributes"] }
Automatically includes:
  • code.filepath: Source file path
  • code.line_number: Line number
  • code.function_name: Module path

Performance Considerations

  • Events are only processed if the level is enabled
  • Use BatchLogProcessor for better throughput
  • Field serialization has overhead; avoid excessive fields
  • The bridge integrates as a Layer, allowing composition with other layers

Differences from tracing-opentelemetry

Featureopentelemetry-appender-tracingtracing-opentelemetry
PurposeEvents → OpenTelemetry LogsSpans → OpenTelemetry Traces
Maintained byOpenTelemetry projectThird-party (Tokio)
Event handlingConverts to logsConverts to span events
Span handlingNot supportedConverts to traces

Documentation

Full API Documentation

View complete API reference on docs.rs

tracing Crate

Documentation for the tracing crate

Minimum Rust Version

MSRV: 1.75.0

Build docs developers (and LLMs) love