Logs
At a glance
- Builds a logger provider that writes log records to stdout and connects tracing events to it.
- Uses
opentelemetry0.33.1,opentelemetry_sdk0.33.1,opentelemetry-stdout0.33.1,opentelemetry-appender-tracing0.33.1,tracing-opentelemetry0.34.0,tracing0.1.44, andtracing-subscriber0.3.23. Other guide versions:opentelemetry-otlp0.33.1,tokio1.54.1,rusqlite0.42.0, andlibsql0.10.0. - Run with
cargo run -p logs. - The last line is
ok: log records emitted and providers shut down.
Dependencies
The example uses Rust edition 2024. The versions below are pinned in its manifest.
[package]
name = "logs"
version = "0.1.0"
edition = "2024"
publish = false
[dependencies]
opentelemetry = { version = "=0.33.1", default-features = false, features = ["trace"] }
opentelemetry_sdk = { version = "=0.33.1", default-features = false, features = ["logs", "trace"] }
opentelemetry-stdout = { version = "=0.33.1", default-features = false, features = ["logs"] }
opentelemetry-appender-tracing = "=0.33.1"
tracing-opentelemetry = "=0.34.0"
tracing = "=0.1.44"
tracing-subscriber = { version = "=0.3.23", default-features = false, features = ["registry"] }
Code
use std::io::{self, Write};
use opentelemetry::trace::TracerProvider;
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_sdk::{logs::SdkLoggerProvider, trace::SdkTracerProvider};
use tracing_subscriber::layer::SubscriberExt;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let logger_provider = SdkLoggerProvider::builder()
.with_simple_exporter(opentelemetry_stdout::LogExporter::default())
.build();
let tracer_provider = SdkTracerProvider::builder().build();
let tracer = tracer_provider.tracer("logs-example");
let subscriber = tracing_subscriber::registry()
.with(tracing_opentelemetry::layer().with_tracer(tracer))
.with(OpenTelemetryTracingBridge::new(&logger_provider));
tracing::subscriber::set_global_default(subscriber)?;
tracing::info!(operation = "start", message = "starting log example");
tracing::warn!(retry_count = 2, message = "retry scheduled");
let span = tracing::info_span!("request", request_id = "request-42");
let entered = span.enter();
tracing::error!(error = "simulated failure", message = "request failed");
drop(entered);
logger_provider.shutdown()?;
tracer_provider.shutdown()?;
writeln!(
io::stdout().lock(),
"ok: log records emitted and providers shut down"
)?;
Ok(())
}
Run it
Run:
cargo run -p logs
The stdout exporter prints the records; their formatting can vary. The final line is:
ok: log records emitted and providers shut down
How it works
The logger provider owns the log pipeline. Its simple exporter writes each record to stdout.
OpenTelemetryTracingBridge is a tracing-subscriber layer. It turns tracing events into OpenTelemetry log records. The event levels map to log severity, and event fields become record attributes.
The example also adds a tracing-opentelemetry layer backed by an SDK tracer provider. When the error event occurs inside the active span, the bridge can add that span’s trace context to the log record. This lets a backend correlate the log with a trace when both signals are available.
Finally, the example shuts down both providers before returning.
Pitfalls
- A span alone does not create OpenTelemetry trace context. Install the
tracing-opentelemetrylayer and emit the event while the span is entered. - Install one global tracing subscriber. Setting a second global subscriber fails.
- The stdout exporter writes locally; it does not send records to an OTLP endpoint.
- Shut down providers before exit so buffered telemetry can be flushed.