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.
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.
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.
Installation
Add the dependency to your Cargo.toml:
[ dependencies ]
opentelemetry = "0.31"
opentelemetry_sdk = "0.31"
opentelemetry-stdout = "0.31"
Feature Flags
The stdout exporter supports selective signal features:
# 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
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
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
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:
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 (())
}
The stdout exporter prints telemetry in a human-readable JSON-like format:
Trace Output Example
Span {
name : "handle_request" ,
trace_id : 5 b 8 aa 5 a 2 d 2 c 872e8137 a 28 db 9 d 321 b 50 ,
span_id : 5 fb 397 be 34 d 26 b 51 ,
parent_span_id : 0000000000000000 ,
start_time : 2026-03-02 T 12 : 00 : 00.123456 Z ,
end_time : 2026-03-02 T 12 : 00 : 00.234567 Z ,
attributes : {
"user.id" : "12345" ,
"request.path" : "/api/users"
},
events : [
Event {
name : "Request validated" ,
timestamp : 2026-03-02 T 12 : 00 : 00.150000 Z ,
attributes : { "validation.time_ms" : 5 }
}
],
status : Ok
}
Metric Output Example
Metric {
name : "http_requests_total" ,
description : "Total HTTP requests" ,
unit : "" ,
data : Sum {
value : 1 ,
attributes : {
"method" : "GET" ,
"endpoint" : "/api/users"
}
}
}
Log Output Example
LogRecord {
timestamp : 2026-03-02 T 12 : 00 : 00.123456 Z ,
severity : Info ,
body : "Processing user request" ,
attributes : {},
trace_id : 5 b 8 aa 5 a 2 d 2 c 872e8137 a 28 db 9 d 321 b 50 ,
span_id : 5 fb 397 be 34 d 26 b 51
}
Configuration
Custom Writer
By default, output goes to stdout. You can customize the destination:
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:
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:
let exporter = opentelemetry_stdout :: SpanExporter :: default ();
let provider = SdkTracerProvider :: builder ()
. with_simple_exporter ( exporter )
. build ();
Testing
Validate telemetry data in tests:
#[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:
// Students can see exactly what data is being collected
let exporter = opentelemetry_stdout :: SpanExporter :: default ();
Debugging Production Issues Locally
Reproduce issues with realistic data:
// 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 () ? ;
The stdout exporter writes synchronously and can block your application. It is not suitable for production use.
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:
Piping to jq
Format output with jq for better readability:
Filtering Output
Filter specific telemetry:
# 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:
cargo run > telemetry.log 2>&1
Troubleshooting
No Output Appearing
Ensure shutdown is called:
provider . shutdown () ? ; // Flushes all pending data
Check feature flags:
opentelemetry-stdout = { version = "0.31" , features = [ "trace" ] }
Verify exporter is set:
let provider = SdkTracerProvider :: builder ()
. with_simple_exporter ( exporter ) // Must set exporter
. build ();
Output Mixed with Application Logs
Redirect stderr separately:
cargo run 2> telemetry.log 1> app.log
Or use a custom writer:
let file = std :: fs :: File :: create ( "telemetry.log" ) ? ;
let exporter = opentelemetry_stdout :: SpanExporter :: new ( file );
The stdout exporter is not optimized for high-volume telemetry. For production:
// 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
OTLP Exporter Production-ready exporter for all signals
Tracing Guide Learn how to instrument your application