Skip to main content
The Tracer trait is the interface for constructing spans. Tracers are responsible for creating new spans and managing the active span context.

Tracer Interface

The core Tracer trait is defined in opentelemetry/src/trace/tracer.rs:121:
pub trait Tracer {
    type Span: Span;

    fn start<T>(&self, name: T) -> Self::Span
    where
        T: Into<Cow<'static, str>>;

    fn start_with_context<T>(&self, name: T, parent_cx: &Context) -> Self::Span
    where
        T: Into<Cow<'static, str>>;

    fn build_with_context(&self, builder: SpanBuilder, parent_cx: &Context) -> Self::Span;
}

Getting a Tracer

Using the Global TracerProvider

The simplest way to get a tracer is through the global provider:
use opentelemetry::global;

// Get a tracer with just a name
let tracer = global::tracer("my-component");

// Get a tracer with version
let tracer = global::tracer_with_version(
    "my-component",
    env!("CARGO_PKG_VERSION")
);

Using InstrumentationScope

For library instrumentation, use InstrumentationScope to provide version and schema information:
use opentelemetry::{global, InstrumentationScope};

let scope = InstrumentationScope::builder("my-library")
    .with_version(env!("CARGO_PKG_VERSION"))
    .with_schema_url("https://opentelemetry.io/schemas/1.17.0")
    .build();

let tracer = global::tracer_provider().tracer_with_scope(scope);

Creating Spans

Simple Span Creation

The start method creates a span with the current context as its parent:
use opentelemetry::trace::{Tracer, Span};

let tracer = global::tracer("my-component");
let mut span = tracer.start("operation_name");

// Do work...

span.end();

Explicit Parent Context

Use start_with_context to specify the parent context:
use opentelemetry::{Context, trace::{Tracer, TraceContextExt}};

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

// Create a parent span
let parent = tracer.start("parent");
let parent_cx = Context::current_with_span(parent);

// Create a child span
let child = tracer.start_with_context("child", &parent_cx);

Using SpanBuilder

For advanced span configuration, use SpanBuilder:
use opentelemetry::trace::{Tracer, SpanKind};
use opentelemetry::KeyValue;

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

let span = tracer
    .span_builder("http_request")
    .with_kind(SpanKind::Client)
    .with_attributes(vec![
        KeyValue::new("http.method", "GET"),
        KeyValue::new("http.url", "https://example.com"),
    ])
    .start(&tracer);

Active Span Management

Using in_span

The in_span method automatically manages span activation and cleanup:
use opentelemetry::{global, trace::Tracer};

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

tracer.in_span("operation", |cx| {
    // Span is active within this closure
    // Span ends automatically when closure returns

    // Nested spans work automatically
    tracer.in_span("sub_operation", |_cx| {
        // This is a child span
    });
});

Manual Span Activation

For more control, manually activate spans:
use opentelemetry::trace::{Tracer, Span, mark_span_as_active};

let tracer = global::tracer("my-component");
let span = tracer.start("operation");

// Mark as active and get a guard
let _guard = mark_span_as_active(span);

// Span is active until guard is dropped

Accessing the Active Span

Get a reference to the currently active span:
use opentelemetry::trace::{get_active_span, Tracer};
use opentelemetry::KeyValue;

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

tracer.in_span("parent", |_cx| {
    // Access active span from anywhere
    get_active_span(|span| {
        span.add_event(
            "Something happened",
            vec![KeyValue::new("detail", "important")]
        );
    });
});

Advanced Configuration with SpanBuilder

The SpanBuilder struct (defined in opentelemetry/src/trace/tracer.rs:356) provides full control over span creation:
pub struct SpanBuilder {
    pub span_kind: Option<SpanKind>,
    pub name: Cow<'static, str>,
    pub start_time: Option<SystemTime>,
    pub attributes: Option<Vec<KeyValue>>,
    pub events: Option<Vec<Event>>,
    pub links: Option<Vec<Link>>,
}

Builder Methods

use opentelemetry::trace::{Tracer, SpanKind, Link};
use opentelemetry::KeyValue;
use std::time::SystemTime;

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

let span = tracer
    .span_builder("complex_operation")
    .with_kind(SpanKind::Server)
    .with_start_time(SystemTime::now())
    .with_attributes(vec![
        KeyValue::new("service.name", "my-service"),
        KeyValue::new("environment", "production"),
    ])
    .with_links(vec![
        // Link to another span context
    ])
    .start(&tracer);

Working with Context

Creating Context with Spans

use opentelemetry::{Context, trace::{Tracer, TraceContextExt}};

let tracer = global::tracer("my-component");
let span = tracer.start("operation");

// Create a context containing this span
let cx = Context::current_with_span(span);

// Use this context to create child spans
let child = tracer.start_with_context("child", &cx);

Using Contexts with in_span

use opentelemetry::trace::{Tracer, SpanKind};

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

// Use in_span_with_builder for full control
tracer.in_span_with_builder(
    tracer.span_builder("http_request")
        .with_kind(SpanKind::Client),
    |cx| {
        // Span is active here
        let span = cx.span();
        span.set_attribute(KeyValue::new("key", "value"));
    }
);

Async/Await Support

The standard span activation methods do not work correctly with async/await. Use FutureExt instead.

Correct Async Pattern

use opentelemetry::{Context, trace::FutureExt};

let cx = Context::current();

let my_future = async {
    // Async work here
};

// Attach context to the future
my_future.with_context(cx).await;

Why Guards Don’t Work in Async

From opentelemetry/src/trace/tracer.rs:79-95:
// This will NOT work correctly:
async {
    let _g = mark_span_as_active(span);
    // The guard stays active for the entire future lifetime,
    // not just when it's being polled!
};
The context guard _g will not exit until the future completes. Since futures can be entered and exited multiple times without completing, the span remains active for as long as the future exists, leading to incorrect traces.

Real-World Example

Here’s a complete example from examples/tracing-grpc/src/client.rs:41-84:
use opentelemetry::{global, trace::{Tracer, SpanKind, TraceContextExt}, KeyValue};

async fn greet() -> Result<(), Box<dyn std::error::Error>> {
    let tracer = global::tracer("example/client");

    let span = tracer
        .span_builder("Greeter/client")
        .with_kind(SpanKind::Client)
        .with_attributes([
            KeyValue::new("rpc.system", "grpc"),
            KeyValue::new("server.port", 50052),
            KeyValue::new("rpc.method", "say_hello"),
        ])
        .start(&tracer);

    let cx = Context::current_with_span(span);

    // Make gRPC call...

    let span = cx.span();
    span.add_event(
        "Got response!",
        vec![KeyValue::new("status", "OK")]
    );

    Ok(())
}

Best Practices

Span names should be general enough to group similar operations but specific enough to be useful. Use get_user instead of get_user/314159.
The in_span method handles activation and cleanup automatically, reducing the risk of leaking spans or incorrect parent relationships.
Always provide version information when instrumenting libraries to help with debugging and understanding trace data.
Use the correct SpanKind (Client, Server, Internal, Producer, Consumer) to help tracing backends understand the relationships between spans.

Next Steps

Spans

Learn about span lifecycle, attributes, and events

Context

Understand context propagation

Build docs developers (and LLMs) love