Get started with OpenTelemetry Rust in minutes with a complete working example
This guide walks you through creating a simple Rust application that emits traces, metrics, and logs using OpenTelemetry. We’ll use the stdout exporter so you can see the telemetry data directly in your console.
This example demonstrates all three telemetry signals:
src/main.rs
use once_cell::sync::Lazy;use opentelemetry::{global, KeyValue};use opentelemetry::trace::Tracer;use opentelemetry_sdk::metrics::SdkMeterProvider;use opentelemetry_sdk::trace::SdkTracerProvider;use opentelemetry_sdk::Resource;// Define a shared resource that identifies your servicestatic RESOURCE: Lazy<Resource> = Lazy::new(|| { Resource::builder() .with_service_name("quickstart-service") .build()});// Initialize the tracer provider with stdout exporterfn init_traces() -> SdkTracerProvider { let exporter = opentelemetry_stdout::SpanExporter::default(); let provider = SdkTracerProvider::builder() .with_simple_exporter(exporter) .with_resource(RESOURCE.clone()) .build(); global::set_tracer_provider(provider.clone()); provider}// Initialize the meter provider with stdout exporterfn init_metrics() -> SdkMeterProvider { let exporter = opentelemetry_stdout::MetricExporter::default(); let provider = SdkMeterProvider::builder() .with_periodic_exporter(exporter) .with_resource(RESOURCE.clone()) .build(); global::set_meter_provider(provider.clone()); provider}// Initialize the logger provider with stdout exporterfn init_logs() -> opentelemetry_sdk::logs::SdkLoggerProvider { use opentelemetry_appender_tracing::layer; use opentelemetry_sdk::logs::SdkLoggerProvider; use tracing_subscriber::prelude::*; let exporter = opentelemetry_stdout::LogExporter::default(); let provider = SdkLoggerProvider::builder() .with_simple_exporter(exporter) .with_resource(RESOURCE.clone()) .build(); // Create a tracing bridge to route tracing logs to OpenTelemetry let layer = layer::OpenTelemetryTracingBridge::new(&provider); tracing_subscriber::registry().with(layer).init(); provider}#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { // Initialize all telemetry providers let tracer_provider = init_traces(); let meter_provider = init_metrics(); let logger_provider = init_logs(); // Get a tracer for creating spans let tracer = global::tracer("quickstart"); // Get a meter for recording metrics let meter = global::meter("quickstart"); let counter = meter.u64_counter("requests").build(); let histogram = meter.f64_histogram("request_duration").build(); // Create a span to represent some work tracer.in_span("process_request", |cx| { let span = cx.span(); // Add attributes to the span span.set_attribute(KeyValue::new("http.method", "GET")); span.set_attribute(KeyValue::new("http.route", "/api/users")); // Add an event to the span span.add_event( "Processing request", vec![KeyValue::new("user_id", "12345")], ); // Emit a log within the span context tracing::info!( user_id = "12345", action = "fetch_user", "User data retrieved successfully" ); // Record metrics counter.add( 1, &[ KeyValue::new("method", "GET"), KeyValue::new("status", "200"), ], ); histogram.record( 0.245, &[ KeyValue::new("method", "GET"), KeyValue::new("route", "/api/users"), ], ); // Simulate nested work with a child span tracer.in_span("database_query", |cx| { let span = cx.span(); span.set_attribute(KeyValue::new("db.system", "postgresql")); span.set_attribute(KeyValue::new("db.statement", "SELECT * FROM users WHERE id = $1")); tracing::debug!("Executing database query"); }); }); // Shutdown providers to flush any pending telemetry tracer_provider.shutdown()?; meter_provider.shutdown()?; logger_provider.shutdown()?; Ok(())}
use opentelemetry::trace::TraceContextExt;tracer.in_span("outer", |cx| { // Context is automatically propagated to child spans let trace_id = cx.span().span_context().trace_id(); println!("Trace ID: {}", trace_id); tracer.in_span("inner", |_cx| { // This span is a child of "outer" });});