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

# Stdout Exporter

> Debug and inspect telemetry data by printing traces, metrics, and logs to standard output

The stdout exporter writes telemetry data to standard output, making it ideal for development, debugging, and learning. It supports all three OpenTelemetry signals: traces, metrics, and logs.

<Warning>
  The stdout exporter is designed for **development and debugging only**. It is not optimized for production use, and the output format may change between versions.
</Warning>

## Installation

Add the dependency to your `Cargo.toml`:

```toml theme={null}
[dependencies]
opentelemetry = "0.31"
opentelemetry_sdk = "0.31"
opentelemetry-stdout = "0.31"
```

### Feature Flags

The stdout exporter supports selective signal features:

```toml theme={null}
# Default (all signals)
opentelemetry-stdout = "0.31"

# Traces only
opentelemetry-stdout = { version = "0.31", features = ["trace"], default-features = false }

# Metrics only
opentelemetry-stdout = { version = "0.31", features = ["metrics"], default-features = false }

# Logs only
opentelemetry-stdout = { version = "0.31", features = ["logs"], default-features = false }
```

## Quick Start

### Traces

```rust theme={null}
use opentelemetry::global;
use opentelemetry::trace::Tracer;
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource;
use opentelemetry_stdout::SpanExporter;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create stdout exporter for traces
    let exporter = SpanExporter::default();

    // Create tracer provider
    let provider = SdkTracerProvider::builder()
        .with_simple_exporter(exporter)
        .with_resource(
            Resource::builder()
                .with_service_name("stdout-example")
                .build()
        )
        .build();

    global::set_tracer_provider(provider.clone());

    // Create and use a tracer
    let tracer = global::tracer("my-tracer");
    
    tracer.in_span("example-operation", |_cx| {
        println!("Doing some work...");
    });

    // Shutdown to flush spans
    provider.shutdown()?;
    Ok(())
}
```

### Metrics

```rust theme={null}
use opentelemetry::global;
use opentelemetry::KeyValue;
use opentelemetry_sdk::metrics::SdkMeterProvider;
use opentelemetry_sdk::Resource;
use opentelemetry_stdout::MetricExporter;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create stdout exporter for metrics
    let exporter = MetricExporter::default();

    // Create meter provider
    let provider = SdkMeterProvider::builder()
        .with_periodic_exporter(exporter)
        .with_resource(
            Resource::builder()
                .with_service_name("stdout-example")
                .build()
        )
        .build();

    global::set_meter_provider(provider.clone());

    // Create and use a meter
    let meter = global::meter("my-meter");
    let counter = meter.u64_counter("requests").build();
    
    counter.add(1, &[KeyValue::new("endpoint", "/api/users")]);

    // Shutdown to flush metrics
    provider.shutdown()?;
    Ok(())
}
```

### Logs

```rust theme={null}
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_sdk::logs::SdkLoggerProvider;
use opentelemetry_sdk::Resource;
use opentelemetry_stdout::LogExporter;
use tracing::info;
use tracing_subscriber::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create stdout exporter for logs
    let exporter = LogExporter::default();

    // Create logger provider
    let provider = SdkLoggerProvider::builder()
        .with_simple_exporter(exporter)
        .with_resource(
            Resource::builder()
                .with_service_name("stdout-example")
                .build()
        )
        .build();

    // Bridge tracing to OpenTelemetry
    let layer = OpenTelemetryTracingBridge::new(&provider);
    tracing_subscriber::registry().with(layer).init();

    // Use tracing macros
    info!("Application started");
    info!(user_id = "12345", "User logged in");

    // Shutdown to flush logs
    provider.shutdown()?;
    Ok(())
}
```

## Complete Example (All Signals)

Here's a comprehensive example that demonstrates all three signals:

```rust theme={null}
use once_cell::sync::Lazy;
use opentelemetry::{global, trace::{TraceContextExt, Tracer}, InstrumentationScope, KeyValue};
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_sdk::{
    logs::SdkLoggerProvider,
    metrics::SdkMeterProvider,
    trace::SdkTracerProvider,
    Resource,
};
use tracing::info;
use tracing_subscriber::prelude::*;

static RESOURCE: Lazy<Resource> = Lazy::new(|| {
    Resource::builder()
        .with_service_name("demo-app")
        .with_service_version("1.0.0")
        .build()
});

fn 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
}

fn 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
}

fn init_logs() -> SdkLoggerProvider {
    let exporter = opentelemetry_stdout::LogExporter::default();
    let provider = SdkLoggerProvider::builder()
        .with_simple_exporter(exporter)
        .with_resource(RESOURCE.clone())
        .build();
    let layer = OpenTelemetryTracingBridge::new(&provider);
    tracing_subscriber::registry().with(layer).init();
    provider
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize all providers
    let tracer_provider = init_traces();
    let meter_provider = init_metrics();
    let logger_provider = init_logs();

    // Create instrumentation scope
    let scope = InstrumentationScope::builder("demo-app")
        .with_version("1.0")
        .with_attributes([KeyValue::new("environment", "development")])
        .build();

    let tracer = global::tracer_with_scope(scope.clone());
    let meter = global::meter_with_scope(scope);

    // Create metrics
    let request_counter = meter
        .u64_counter("http_requests_total")
        .with_description("Total HTTP requests")
        .build();

    let request_duration = meter
        .f64_histogram("http_request_duration_seconds")
        .with_description("HTTP request duration")
        .build();

    // Record some metrics
    request_counter.add(1, &[
        KeyValue::new("method", "GET"),
        KeyValue::new("endpoint", "/api/users"),
    ]);
    request_duration.record(0.123, &[
        KeyValue::new("method", "GET"),
        KeyValue::new("status", "200"),
    ]);

    // Create traces with logs
    tracer.in_span("handle_request", |cx| {
        let span = cx.span();
        span.set_attribute(KeyValue::new("user.id", "12345"));
        span.set_attribute(KeyValue::new("request.path", "/api/users"));
        
        info!("Processing user request");
        
        span.add_event(
            "Request validated",
            vec![KeyValue::new("validation.time_ms", 5)],
        );
        
        tracer.in_span("database_query", |cx| {
            let span = cx.span();
            span.set_attribute(KeyValue::new("db.system", "postgresql"));
            info!("Querying database");
        });
        
        info!("Request completed successfully");
    });

    // Shutdown all providers
    tracer_provider.shutdown()?;
    meter_provider.shutdown()?;
    logger_provider.shutdown()?;

    Ok(())
}
```

## Output Format

The stdout exporter prints telemetry in a human-readable JSON-like format:

### Trace Output Example

```json theme={null}
Span {
    name: "handle_request",
    trace_id: 5b8aa5a2d2c872e8137a28db9d321b50,
    span_id: 5fb397be34d26b51,
    parent_span_id: 0000000000000000,
    start_time: 2026-03-02T12:00:00.123456Z,
    end_time: 2026-03-02T12:00:00.234567Z,
    attributes: {
        "user.id": "12345",
        "request.path": "/api/users"
    },
    events: [
        Event {
            name: "Request validated",
            timestamp: 2026-03-02T12:00:00.150000Z,
            attributes: {"validation.time_ms": 5}
        }
    ],
    status: Ok
}
```

### Metric Output Example

```json theme={null}
Metric {
    name: "http_requests_total",
    description: "Total HTTP requests",
    unit: "",
    data: Sum {
        value: 1,
        attributes: {
            "method": "GET",
            "endpoint": "/api/users"
        }
    }
}
```

### Log Output Example

```json theme={null}
LogRecord {
    timestamp: 2026-03-02T12:00:00.123456Z,
    severity: Info,
    body: "Processing user request",
    attributes: {},
    trace_id: 5b8aa5a2d2c872e8137a28db9d321b50,
    span_id: 5fb397be34d26b51
}
```

## Configuration

### Custom Writer

By default, output goes to stdout. You can customize the destination:

```rust theme={null}
use std::io::Write;
use opentelemetry_stdout::SpanExporter;

// Example: Write to a file
let file = std::fs::File::create("traces.log")?;
let exporter = SpanExporter::new(file);
```

### Resource Configuration

Add resource attributes that appear in all telemetry:

```rust theme={null}
use opentelemetry::KeyValue;
use opentelemetry_sdk::Resource;

let resource = Resource::builder()
    .with_service_name("my-service")
    .with_service_version("1.2.3")
    .with_attributes([
        KeyValue::new("environment", "development"),
        KeyValue::new("region", "local"),
        KeyValue::new("host.name", "laptop"),
    ])
    .build();

let provider = SdkTracerProvider::builder()
    .with_simple_exporter(exporter)
    .with_resource(resource)
    .build();
```

## Use Cases

### Local Development

Quickly verify instrumentation without setting up a backend:

```rust theme={null}
let exporter = opentelemetry_stdout::SpanExporter::default();
let provider = SdkTracerProvider::builder()
    .with_simple_exporter(exporter)
    .build();
```

### Testing

Validate telemetry data in tests:

```rust theme={null}
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_instrumentation() {
        let exporter = opentelemetry_stdout::SpanExporter::default();
        let provider = SdkTracerProvider::builder()
            .with_simple_exporter(exporter)
            .build();
        
        // Your test code that generates spans
        // Spans will be printed to stdout for inspection
    }
}
```

### Learning and Education

Understand the structure of OpenTelemetry data:

```rust theme={null}
// Students can see exactly what data is being collected
let exporter = opentelemetry_stdout::SpanExporter::default();
```

### Debugging Production Issues Locally

Reproduce issues with realistic data:

```rust theme={null}
// Switch to stdout exporter temporarily for debugging
#[cfg(debug_assertions)]
let exporter = opentelemetry_stdout::SpanExporter::default();

#[cfg(not(debug_assertions))]
let exporter = opentelemetry_otlp::SpanExporter::builder()
    .with_http()
    .build()?;
```

## Performance Considerations

<Warning>
  The stdout exporter writes synchronously and can block your application. It is **not suitable for production** use.
</Warning>

### Impact on Application Performance

* Each telemetry item is written immediately
* I/O operations block the calling thread
* Large volumes of telemetry can slow down your application
* No batching or buffering

### Alternatives for Production

**For production, use:**

* [OTLP Exporter](/exporters/otlp) - Production-ready, supports batching
* [Zipkin Exporter](/exporters/zipkin) - For Zipkin backends

## Integration with Other Tools

### Piping to jq

Format output with jq for better readability:

```bash theme={null}
cargo run | jq '.'
```

### Filtering Output

Filter specific telemetry:

```bash theme={null}
# Only show errors
cargo run 2>&1 | grep -i error

# Only show spans
cargo run 2>&1 | grep "Span {"
```

### Saving to File

Capture output for later analysis:

```bash theme={null}
cargo run > telemetry.log 2>&1
```

## Troubleshooting

### No Output Appearing

1. **Ensure shutdown is called:**
   ```rust theme={null}
   provider.shutdown()?;  // Flushes all pending data
   ```

2. **Check feature flags:**
   ```toml theme={null}
   opentelemetry-stdout = { version = "0.31", features = ["trace"] }
   ```

3. **Verify exporter is set:**
   ```rust theme={null}
   let provider = SdkTracerProvider::builder()
       .with_simple_exporter(exporter)  // Must set exporter
       .build();
   ```

### Output Mixed with Application Logs

Redirect stderr separately:

```bash theme={null}
cargo run 2>telemetry.log 1>app.log
```

Or use a custom writer:

```rust theme={null}
let file = std::fs::File::create("telemetry.log")?;
let exporter = opentelemetry_stdout::SpanExporter::new(file);
```

### Performance Degradation

The stdout exporter is not optimized for high-volume telemetry. For production:

```rust theme={null}
// Switch to OTLP for production
let exporter = opentelemetry_otlp::SpanExporter::builder()
    .with_http()
    .build()?;
```

## Comparison with Other Exporters

| Feature          | Stdout      | OTLP       | Zipkin          |
| ---------------- | ----------- | ---------- | --------------- |
| Traces           | ✓           | ✓          | ✓               |
| Metrics          | ✓           | ✓          | ✗               |
| Logs             | ✓           | ✓          | ✗               |
| Production Ready | ✗           | ✓          | ✓               |
| Batching         | ✗           | ✓          | ✓               |
| Backend Required | ✗           | ✓          | ✓               |
| Use Case         | Development | Production | Zipkin backends |

## Next Steps

<CardGroup cols={2}>
  <Card title="OTLP Exporter" icon="arrow-right" href="/exporters/otlp">
    Production-ready exporter for all signals
  </Card>

  <Card title="Tracing Guide" icon="diagram-project" href="/traces/overview">
    Learn how to instrument your application
  </Card>
</CardGroup>
