> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/open-telemetry/opentelemetry-rust/llms.txt
> Use this file to discover all available pages before exploring further.

# Spans

> 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.

## Span Interface

The `Span` trait is defined in `opentelemetry/src/trace/span.rs:50`:

```rust theme={null}
pub trait Span {
    fn add_event<T>(&mut self, name: T, attributes: Vec<KeyValue>)
    where T: Into<Cow<'static, str>>;

    fn add_event_with_timestamp<T>(
        &mut self,
        name: T,
        timestamp: SystemTime,
        attributes: Vec<KeyValue>,
    ) where T: Into<Cow<'static, str>>;

    fn span_context(&self) -> &SpanContext;
    fn is_recording(&self) -> bool;
    fn set_attribute(&mut self, attribute: KeyValue);
    fn set_status(&mut self, status: Status);
    fn update_name<T>(&mut self, new_name: T)
    where T: Into<Cow<'static, str>>;
    fn add_link(&mut self, span_context: SpanContext, attributes: Vec<KeyValue>);
    fn end(&mut self);
    fn end_with_timestamp(&mut self, timestamp: SystemTime);
}
```

## Span Lifecycle

### Creating Spans

Spans are created through a `Tracer`:

```rust theme={null}
use opentelemetry::{global, trace::{Tracer, Span}};

let tracer = global::tracer("my-component");

// Create and start a span
let mut span = tracer.start("operation_name");

// Perform work...

// End the span
span.end();
```

### Automatic Span Management

The `in_span` method handles the lifecycle automatically:

```rust theme={null}
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
});
```

### Recording State

Check if a span is recording before adding expensive data:

```rust theme={null}
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.

## Span Attributes

Attributes are key-value pairs that provide additional context about the operation.

### Setting Attributes

```rust theme={null}
use opentelemetry::{trace::Span, KeyValue};

// Set a single attribute
span.set_attribute(KeyValue::new("http.method", "GET"));
span.set_attribute(KeyValue::new("http.status_code", 200));

// Set multiple attributes
span.set_attributes(vec![
    KeyValue::new("user.id", "12345"),
    KeyValue::new("user.role", "admin"),
]);
```

### Attributes at Span Creation

Set attributes when creating the span:

```rust theme={null}
use opentelemetry::trace::{Tracer, SpanKind};
use opentelemetry::KeyValue;

let span = tracer
    .span_builder("http_request")
    .with_attributes(vec![
        KeyValue::new("http.method", "POST"),
        KeyValue::new("http.url", "https://api.example.com/users"),
        KeyValue::new("http.target", "/users"),
    ])
    .start(&tracer);
```

### Attribute Limits

Spans have configurable limits on the number of attributes. From `opentelemetry-sdk/src/trace/span.rs:139-147`:

```rust theme={null}
fn set_attribute(&mut self, attribute: KeyValue) {
    let span_attribute_limit = self.span_limits.max_attributes_per_span as usize;
    self.with_data(|data| {
        if data.attributes.len() < span_attribute_limit {
            data.attributes.push(attribute);
        } else {
            data.dropped_attributes_count += 1;
        }
    });
}
```

<Info>
  Attributes beyond the limit are dropped, and the drop count is tracked. Configure limits via `SpanLimits`.
</Info>

## Span Events

Events are timestamped annotations that record something happening during the span's lifetime.

### Adding Events

```rust theme={null}
use opentelemetry::{trace::Span, KeyValue};

// Simple event
span.add_event("cache_miss", vec![]);

// Event with attributes
span.add_event(
    "item_processed",
    vec![
        KeyValue::new("item.id", "12345"),
        KeyValue::new("item.type", "product"),
    ]
);
```

### Events with Custom Timestamps

```rust theme={null}
use std::time::SystemTime;

let timestamp = SystemTime::now();
span.add_event_with_timestamp(
    "database_query",
    timestamp,
    vec![KeyValue::new("query.duration_ms", 45)]
);
```

### Recording Errors

The `record_error` method is a convenience for recording exceptions:

```rust theme={null}
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`:

```rust theme={null}
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);
    }
}
```

<Warning>
  `record_error` only adds an event. To mark the span as failed, you must also call `set_status`.
</Warning>

## Span Status

The status indicates whether the operation succeeded or failed.

### Status Values

From `opentelemetry/src/trace/span.rs:281-296`:

```rust theme={null}
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,
}
```

### Setting Status

```rust theme={null}
use opentelemetry::trace::{Span, Status};

// Mark as successful
span.set_status(Status::Ok);

// Mark as error with description
span.set_status(Status::error("Database connection failed"));

// Or with String
span.set_status(Status::error(format!("Invalid user: {}", user_id)));
```

### Status Precedence

Statuses form a total order: `Ok > Error > Unset`

```rust theme={null}
// Once set to Ok, status cannot be changed
span.set_status(Status::Ok);
span.set_status(Status::error("too late")); // Ignored

// Error overrides Unset
span.set_status(Status::error("failed"));
span.set_status(Status::Unset); // Ignored
```

<Info>
  Instrumentation libraries should generally not set `Status::Ok`. Only application code should mark operations as explicitly successful.
</Info>

## Span Context

The `SpanContext` contains immutable information about a span that can be propagated.

### SpanContext Structure

From `opentelemetry/src/trace/span_context.rs:294-300`:

```rust theme={null}
pub struct SpanContext {
    trace_id: TraceId,
    span_id: SpanId,
    trace_flags: TraceFlags,
    is_remote: bool,
    trace_state: TraceState,
}
```

### Accessing SpanContext

```rust theme={null}
use opentelemetry::trace::Span;

let span_context = span.span_context();

let trace_id = span_context.trace_id();
let span_id = span_context.span_id();
let is_sampled = span_context.is_sampled();
let is_valid = span_context.is_valid();
```

## Span Links

Links connect spans across different traces or within the same trace in a non-parent-child relationship.

### Adding Links

```rust theme={null}
use opentelemetry::trace::{Span, SpanContext};
use opentelemetry::KeyValue;

// Link to another span
span.add_link(
    other_span.span_context().clone(),
    vec![KeyValue::new("link.type", "related")]
);
```

### Links at Span Creation

Links are typically added when creating the span:

```rust theme={null}
use opentelemetry::trace::{Tracer, Link};

let span = tracer
    .span_builder("batch_process")
    .with_links(vec![
        Link::new(
            span_context_1.clone(),
            vec![KeyValue::new("batch.index", 0)]
        ),
        Link::new(
            span_context_2.clone(),
            vec![KeyValue::new("batch.index", 1)]
        ),
    ])
    .start(&tracer);
```

<Info>
  Links added via `add_link` after span creation are not accessible to samplers. Add links at creation time when possible.
</Info>

## Span Kind

SpanKind describes the relationship between spans in a trace.

From `opentelemetry/src/trace/span.rs:226-255`:

```rust theme={null}
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,
}
```

### Setting Span Kind

```rust theme={null}
use opentelemetry::trace::{Tracer, SpanKind};

// HTTP client request
let span = tracer
    .span_builder("GET /users")
    .with_kind(SpanKind::Client)
    .start(&tracer);

// HTTP server handler
let span = tracer
    .span_builder("POST /users")
    .with_kind(SpanKind::Server)
    .start(&tracer);

// Message producer
let span = tracer
    .span_builder("send_message")
    .with_kind(SpanKind::Producer)
    .start(&tracer);
```

## Updating Span Name

Span names can be updated after creation:

```rust theme={null}
use opentelemetry::trace::Span;

let mut span = tracer.start("unknown_route");

// Later, when the route is known:
span.update_name("/api/v1/users");
```

<Info>
  Sampling decisions based on the name depend on implementation and may not be re-evaluated after name changes.
</Info>

## Complete Example

Here's a realistic example combining multiple span features:

```rust theme={null}
use opentelemetry::{global, trace::{Tracer, Span, SpanKind, Status}, KeyValue};
use std::time::SystemTime;

fn process_order(order_id: &str) -> Result<(), Box<dyn std::error::Error>> {
    let tracer = global::tracer("order-service");

    let mut span = tracer
        .span_builder("process_order")
        .with_kind(SpanKind::Internal)
        .with_attributes(vec![
            KeyValue::new("order.id", order_id.to_string()),
            KeyValue::new("service.name", "order-processor"),
        ])
        .start(&tracer);

    span.add_event("validation_started", vec![]);

    match validate_order(order_id) {
        Ok(_) => {
            span.add_event(
                "validation_succeeded",
                vec![KeyValue::new("validation.duration_ms", 42)]
            );
        }
        Err(e) => {
            span.record_error(&*e);
            span.set_status(Status::error(e.to_string()));
            span.end();
            return Err(e);
        }
    }

    // Process the order...
    span.add_event("order_processed", vec![
        KeyValue::new("items.count", 3),
        KeyValue::new("total.amount", 99.99),
    ]);

    span.set_status(Status::Ok);
    span.end();
    Ok(())
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Use descriptive span names">
    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.
  </Accordion>

  <Accordion title="Check is_recording for expensive operations">
    Before computing expensive attribute values, check if the span is recording to avoid unnecessary work.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="Use events for significant moments">
    Record events for important moments in the span's lifecycle, like cache hits/misses, retries, or state changes.
  </Accordion>

  <Accordion title="Add links at span creation">
    Links added after span creation are not available to samplers. Include links in the SpanBuilder when possible.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Context" icon="arrow-right-arrow-left" href="/traces/context">
    Learn about context propagation
  </Card>

  <Card title="Span Processors" icon="gears" href="/traces/span-processors">
    Configure how spans are processed and exported
  </Card>
</CardGroup>
