Traces track the progression of a single request as it flows through services in a distributed system. A trace is composed of one or more spans, which represent units of work.
OpenTelemetry provides utilities to manage the currently active span:
use opentelemetry::trace::{self, Tracer, get_active_span};let tracer = global::tracer("my_tracer");// Create and mark a span as activelet span = tracer.start("parent_span");let active = trace::mark_span_as_active(span);// Any span created here will be a child of `parent_span`// Access the active spantrace::get_active_span(|span| { span.set_attribute(KeyValue::new("custom.attribute", "value"));});// Drop the guard to deactivate the spandrop(active);
The in_span method provides a convenient way to create and manage spans:
use opentelemetry::{global, trace::Tracer};let tracer = global::tracer("my_tracer");tracer.in_span("parent_span", |cx| { // Spans created here will be children of `parent_span` tracer.in_span("child_span", |cx| { // Nested operation });});
For async code, use FutureExt to propagate span context:
use opentelemetry::{Context, global, trace::{FutureExt, TraceContextExt, Tracer}};async fn some_work() { // Async operations here}let tracer = global::tracer("my_tracer");let span = tracer.start("my_span");// Perform async work with this span as the currently active parentsome_work() .with_context(Context::current_with_span(span)) .await;
Gauges record independent measurements that represent the current state:
let gauge = meter .f64_gauge("cpu_temperature") .with_description("Current CPU temperature in Celsius") .build();gauge.record(72.5, &[KeyValue::new("core", "0")]);
Reuse Instruments: Create instruments once and reuse them. Avoid creating new instruments for each measurement.
// Good: Create once, reuselet counter = meter.u64_counter("requests").build();for request in requests { counter.add(1, &[]);}// Bad: Creating repeatedlyfor request in requests { let counter = meter.u64_counter("requests").build(); // Don't do this! counter.add(1, &[]);}
Clone for Sharing: Instruments are cheaply cloneable and can be shared across threads.
use std::sync::Arc;let counter = meter.u64_counter("operations").build();// Clone to share with another threadlet counter_clone = counter.clone();std::thread::spawn(move || { counter_clone.add(1, &[]);});
The OpenTelemetry Logs Bridge API provides integration between existing logging libraries and OpenTelemetry. It’s designed for logging library authors, not application developers.
Application developers should use familiar logging libraries like tracing:
use tracing::{error, info, warn};use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;use opentelemetry_sdk::logs::SdkLoggerProvider;use tracing_subscriber::prelude::*;// Set up OpenTelemetry logginglet provider = SdkLoggerProvider::builder() .with_simple_exporter(opentelemetry_stdout::LogExporter::default()) .build();let otel_layer = OpenTelemetryTracingBridge::new(&provider);tracing_subscriber::registry() .with(otel_layer) .init();// Use normal logginginfo!("Server started on port 8080");warn!(user_id = 123, "Rate limit approaching");error!( name: "database_error", error_code = "CONNECTION_FAILED", message = "Failed to connect to database");