Understand span lifecycle, attributes, events, and how to record operations in OpenTelemetry Rust
A Span represents a single operation within a trace. Spans can be nested to form a trace tree, where each trace contains a root span and zero or more child spans representing sub-operations.
use opentelemetry::{global, trace::{Tracer, Span}};let tracer = global::tracer("my-component");// Create and start a spanlet mut span = tracer.start("operation_name");// Perform work...// End the spanspan.end();
The in_span method handles the lifecycle automatically:
use opentelemetry::{global, trace::Tracer};let tracer = global::tracer("my-component");tracer.in_span("operation", |cx| { // Span is automatically started and active // Span ends automatically when closure returns});
Check if a span is recording before adding expensive data:
use opentelemetry::trace::Span;if span.is_recording() { // Only compute expensive attributes if span is recording let expensive_value = compute_expensive_data(); span.set_attribute(KeyValue::new("data", expensive_value));}
From opentelemetry/src/trace/span.rs:98-108:
This flag may be true despite the entire trace being sampled out. This allows recording and processing of information about individual spans without sending it to the backend.
use opentelemetry::{trace::Span, KeyValue};// Set a single attributespan.set_attribute(KeyValue::new("http.method", "GET"));span.set_attribute(KeyValue::new("http.status_code", 200));// Set multiple attributesspan.set_attributes(vec![ KeyValue::new("user.id", "12345"), KeyValue::new("user.role", "admin"),]);
The record_error method is a convenience for recording exceptions:
use opentelemetry::trace::Span;use std::error::Error;fn process_data(span: &mut impl Span) -> Result<(), Box<dyn Error>> { match do_something() { Ok(result) => Ok(result), Err(err) => { span.record_error(&*err); // Note: You must also set status to error span.set_status(Status::error(err.to_string())); Err(err) } }}
From opentelemetry/src/trace/span.rs:72-77:
fn record_error(&mut self, err: &dyn Error) { if self.is_recording() { let attributes = vec![KeyValue::new("exception.message", err.to_string())]; self.add_event("exception", attributes); }}
record_error only adds an event. To mark the span as failed, you must also call set_status.
pub enum Status { /// The default status (operation has not been evaluated) Unset, /// The operation contains an error Error { description: Cow<'static, str> }, /// The operation completed successfully Ok,}
use opentelemetry::trace::{Span, Status};// Mark as successfulspan.set_status(Status::Ok);// Mark as error with descriptionspan.set_status(Status::error("Database connection failed"));// Or with Stringspan.set_status(Status::error(format!("Invalid user: {}", user_id)));
// Once set to Ok, status cannot be changedspan.set_status(Status::Ok);span.set_status(Status::error("too late")); // Ignored// Error overrides Unsetspan.set_status(Status::error("failed"));span.set_status(Status::Unset); // Ignored
Instrumentation libraries should generally not set Status::Ok. Only application code should mark operations as explicitly successful.
use opentelemetry::trace::{Span, SpanContext};use opentelemetry::KeyValue;// Link to another spanspan.add_link( other_span.span_context().clone(), vec![KeyValue::new("link.type", "related")]);
SpanKind describes the relationship between spans in a trace.From opentelemetry/src/trace/span.rs:226-255:
pub enum SpanKind { /// Client span - a request to a remote service Client, /// Server span - handling of a synchronous RPC or request Server, /// Producer span - initiator of an asynchronous request Producer, /// Consumer span - handler of an asynchronous request Consumer, /// Internal span - operation within the application Internal,}
Choose span names that represent a general operation, not specific instances. Use get_user instead of get_user_12345. Put the specific ID in an attribute.
Check is_recording for expensive operations
Before computing expensive attribute values, check if the span is recording to avoid unnecessary work.
Set status on errors
Always set span status to error when an operation fails. Use record_error for the exception details and set_status to mark the span as failed.
Use events for significant moments
Record events for important moments in the span’s lifecycle, like cache hits/misses, retries, or state changes.
Add links at span creation
Links added after span creation are not available to samplers. Include links in the SpanBuilder when possible.