otel-bootstrap
One-call OpenTelemetry bootstrap for Rust services — traces, metrics, and logs over OTLP with sensible defaults.
The standard opentelemetry + opentelemetry-otlp + tracing-subscriber wiring is the same in every service. This crate does it once: call init_telemetry("my-service"), keep the handle alive, and drop it to flush.
Features
- One call to wire it all up — traces, metrics, and logs over OTLP,
tracing-subscriberconfigured, W3C TraceContext + Baggage propagators registered. - gRPC or HTTP/protobuf — select transport via cargo feature.
- Env-var configuration — follows the OpenTelemetry spec (
OTEL_EXPORTER_OTLP_ENDPOINT,OTEL_SERVICE_NAME,OTEL_TRACES_SAMPLER, …). - Builder API — version, environment, sampler, custom meter setup, extra subscriber layers.
- Optional axum middleware —
OtelTraceLayerfor inbound HTTP trace propagation. - Generic
enduser.*span enrichment — implementEnrichSpanon your context type and plug it intoSpanEnricherLayer<T>with no brefwiz dependencies required. - Graceful shutdown — drop
TelemetryHandlesto flush and shut down both providers.
Quick start
[]
= "2"
= { = "1", = ["full"] }
use init_telemetry;
async
For more control, use the builder:
use Telemetry;
let _telemetry = builder
.version
.environment
.init?;
Cargo features
| Feature | Default | Description |
|---|---|---|
grpc |
✅ | OTLP/gRPC transport via tonic |
http |
❌ | OTLP/HTTP-protobuf transport via reqwest |
axum |
❌ | OtelTraceLayer + SpanEnricherLayer<T> for axum servers |
testing |
❌ | In-memory exporters for unit tests |
At least one of grpc or http must be enabled (enforced at compile time).
Configuration
| Variable | Default |
|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
http://localhost:4317 (gRPC) / http://localhost:4318 (HTTP) |
OTEL_EXPORTER_OTLP_PROTOCOL |
grpc |
OTEL_EXPORTER_OTLP_TIMEOUT |
10000 ms |
OTEL_SERVICE_NAME |
overridden by the service_name argument |
OTEL_TRACES_SAMPLER |
parentbased_always_on |
OTEL_TRACES_SAMPLER_ARG |
sampler-specific (e.g. ratio for traceidratio) |
Full reference: OTEL_TRACES_SAMPLER values.
Axum middleware
= { = "2", = ["axum"] }
use axum_layer;
let app = new
.route
.layer;
This layer reads the traceparent / tracestate headers, starts a server span, and propagates context to all child spans.
enduser.* span enrichment
= { = "2", = ["axum"] }
Implement EnrichSpan on your context type, then plug it into SpanEnricherLayer:
use ;
use Span;
let app = new
.route
.layer;
Routes with no MyContext extension are silently skipped.
Span-field log propagation
When with_logs(true) is set, otel-bootstrap replaces the upstream log bridge with SpanAwareLogBridge. It propagates span-level attributes into every OTLP log record so that request.id, enduser.*, and http.* fields appear as Loki labels alongside the trace context.
Two propagation paths are supported:
- Tracing fields declared at span creation (
info_span!("req", "request.id" = id)) — captured automatically for names in [PROPAGATED_SPAN_FIELDS]. - Post-creation attributes set via
record_span_log_attr_on— use this from middleware orEnrichSpan::enrich_spanfor fields that arrive after span creation.
use ;
use ;
// In your request handler / middleware:
emit_request_id; // → appears in traces + logs
// In EnrichSpan::enrich_span for enduser fields:
span.set_attribute;
record_span_log_attr_on;
Override the captured field set per service:
const FIELDS: & = &;
let _telemetry = builder
.with_logs
.with_propagated_span_fields
.init?;
Examples
basic_setup— minimal initshutdown_handling— explicit graceful flushcustom_config— builder API with version, environment, sampleraxum_span_enricher— axum + genericEnrichSpanenrichment
License
MIT — see LICENSE.