Expand description
§OpenTelemetry-Appender-Tracing
This crate provides a bridge between the tracing crate and OpenTelemetry logs.
It converts tracing events into OpenTelemetry LogRecords, allowing applications using tracing to seamlessly integrate
with OpenTelemetry logging backends.
§Background
Unlike traces and metrics, OpenTelemetry does not provide a dedicated logging API for end-users. Instead, it recommends using
existing logging libraries and bridging them to OpenTelemetry logs. This crate serves as such a bridge for tracing users.
§Features
- Converts
tracingevents into OpenTelemetryLogRecords - Integrates as a
Layerfromtracing-subscriber, allowing to be used alongside othertracinglayers, such asfmt - Automatically attaches OpenTelemetry trace context (
TraceId,SpanId,TraceFlags) to logs - Automatically associates OpenTelemetry Resource to logs
- Supports exporting logs to OpenTelemetry-compatible backends (OTLP, stdout, etc.)
§Getting Started
§1. Install Dependencies
Add the following dependencies to your Cargo.toml:
[dependencies]
tracing = ">=0.1.40"
tracing-core = { version = ">=0.1.33" }
tracing-subscriber = { version = "0.3", features = ["registry", "std", "fmt"] }
opentelemetry = { version = "0.31", features = ["logs"] }
opentelemetry-sdk = { version = "0.31", features = ["logs"] }
opentelemetry-appender-tracing = { version = "0.31.1" }§2. Set Up the OpenTelemetry Logger Provider
Before integrating with tracing, create an OpenTelemetry SdkLoggerProvider:
use opentelemetry_sdk::logs::SdkLoggerProvider;
use opentelemetry_stdout::LogExporter;
let exporter = LogExporter::default();
let provider = SdkLoggerProvider::builder()
.with_simple_exporter(exporter)
.build();In this example, SdkLoggerProvider is configured to use the opentelemetry_stdout crate to export logs to stdout. You can replace it with any other OpenTelemetry-compatible exporter.
Any additional OpenTelemetry configuration (e.g., setting up a resource, additional processors etc.) can be done at this stage.
§3. Create the OpenTelemetry-Tracing Bridge
Create OpenTelemetryTracingBridge layer using the SdkLoggerProvider created in the previous step.
let otel_layer = OpenTelemetryTracingBridge::new(&provider);§4. Register the tracing Subscriber
Since this crate provides a Layer for tracing, you can register it with the tracing subscriber as shown below.
use tracing_subscriber::prelude::*;
tracing_subscriber::registry()
.with(otel_layer)
.with(tracing_subscriber::fmt::layer()) // In this example, `fmt` layer is also added.
.init();§5. Log Events Using tracing
use tracing::error;
error!(name: "my-event-name1", target: "my-system", event_id = 10, user_name = "otel", user_email = "otel@opentelemetry.io", message = "This is an example message");§Mapping details
Since OpenTelemetry and tracing have their own data models, this bridge performs the following mappings:
tracing | OpenTelemetry | Notes |
|---|---|---|
| name of the event | EventName | OpenTelemetry defines logs with name as Events, so every tracing Event is actually an OTel Event |
| target | target | Groups logs from the same module/crate. At recording time, target is stored in a top-level field. But exporters treat this information as OpenTelemetry InstrumentationScope |
| level of the event | Severity, SeverityText | |
| Fields | Attributes | Converted into OpenTelemetry log attributes. Field with “message” as key is specially treated and stored as LogRecord::Body |
| Message | Body | The body/message of the log. This is done only if body was not already populated from “message” field above |
§Data Type Mapping
The data types supported by tracing and OpenTelemetry are different and the following conversions are applied:
tracing Type | OpenTelemetry AnyValue Type |
|---|---|
i64 | Int |
f32, f64 | Double |
u64,u128 ,i128 | Int (if convertible to i64 without loss) else String |
&str | String |
bool | Bool |
&[u8] | Bytes |
&dyn Debug | String (via Debug formatting) |
&dyn Error | String (via Debug formatting). This is stored into an attribute with key “exception.message”, following OTel conventions |
In future, additional types may be supported.
§Tracing Span Attribute Enrichment
By default, only the fields on the tracing event itself are captured. Optionally,
attributes from active tracing::span!
scopes can be copied onto each emitted log record. “Span” here refers to a tracing span,
not an opentelemetry::trace::Span.
Gated behind the experimental_span_attributes cargo feature.
Enrichment is disabled by default (zero per-span overhead) and must be opted into via the builder:
use opentelemetry_appender_tracing::layer::{OpenTelemetryTracingBridge, TracingSpanAttributes};
// Copy ALL tracing-span attributes onto log records:
let layer = OpenTelemetryTracingBridge::builder(&provider)
.with_tracing_span_attributes(TracingSpanAttributes::all())
.build();
// Or copy only specific attributes:
let layer = OpenTelemetryTracingBridge::builder(&provider)
.with_tracing_span_attributes(TracingSpanAttributes::allowlist(["session.id"]))
.build();When enrichment is enabled, attributes from all ancestor spans (root to leaf) are collected and added to the log record before the event’s own fields.
Note: This crate does not convert
tracingspans into OpenTelemetry spans. Usetracing-opentelemetryfor that. The span enrichment feature here only reads tracing-span fields to copy them onto log records — it does not create or manage OpenTelemetry spans.
§Feature Flags
experimental_span_attributes: Enables tracing-span attribute enrichment (TracingSpanAttributes,with_tracing_span_attributes).experimental_metadata_attributes: Adds source code metadata (code.filepath,code.filename,code.namespace,code.lineno) as log record attributes.
§Limitations
- There is no support for
Valuablecrate. 2819
§Stability Guarantees
// TODO
§Further Reading
- OpenTelemetry Rust: opentelemetry-rust
- Tracing: tracing
- OpenTelemetry Logs: OpenTelemetry Logging Specification