Rust
This guide shows you how to instrument a Rust application with the upstream OpenTelemetry Rust SDK and send traces directly to Middleware. No Middleware Host Agent is required: the OTLP exporter in your application sends data straight to your Middleware account.
Prerequisites#
- Rust 1.75 or newer with Cargo. Check using:
1cargo --version - Middleware account & project: your Middleware UID (
https://<uid>.middleware.io) and API key (MW_API_KEY) - Network access from your application to
https://<uid>.middleware.io:443
For a deeper overview of Middleware's OTLP endpoints and headers, see the OpenTelemetry getting started guide.
Setup#
1 Set environment variables#
The OpenTelemetry SDK reads its exporter and resource configuration from standard environment variables. Set them wherever your application runs:
1export OTEL_EXPORTER_OTLP_ENDPOINT="https://<MW_UID>.middleware.io:443"
2export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
3export OTEL_EXPORTER_OTLP_HEADERS="authorization=<MW_API_KEY>"
4export OTEL_SERVICE_NAME="<service-name>"
5export OTEL_RESOURCE_ATTRIBUTES="service.version=<service-version>,deployment.environment=<environment>"Replace:
<uid>with your Middleware project UID.<MW_API_KEY>with your Middleware API key.<service-name>with a meaningful name for this service (for example,payments-api).
Set OTEL_EXPORTER_OTLP_ENDPOINT to the root URL only. The OTLP/HTTP exporter appends /v1/traces for you.
2 Add dependencies#
Add the OpenTelemetry crates to the [dependencies] section of your Cargo.toml:
1opentelemetry = "0.31"
2opentelemetry_sdk = "0.31"
3opentelemetry-otlp = { version = "0.31", features = ["reqwest-rustls"] }The default features of opentelemetry-otlp provide the OTLP/HTTP (protobuf) exporter and a blocking HTTP client that runs on the batch exporter's background thread. The reqwest-rustls feature adds TLS so the exporter can reach the HTTPS endpoint.
Do not enable the reqwest-client feature alongside the default features. When both the async and the blocking reqwest clients are enabled, the exporter cannot pick a client and SpanExporter::build() fails with NoHttpClient.
3 Initialize the tracer#
Create a tracer provider when your application starts, and shut it down before the process exits so buffered spans are flushed:
1use opentelemetry::global;
2use opentelemetry::trace::Tracer;
3use opentelemetry_otlp::SpanExporter;
4use opentelemetry_sdk::trace::SdkTracerProvider;
5use opentelemetry_sdk::Resource;
6
7fn init_tracer() -> SdkTracerProvider {
8 let exporter = SpanExporter::builder()
9 .with_http()
10 .build()
11 .expect("Failed to create span exporter");
12
13 SdkTracerProvider::builder()
14 .with_resource(Resource::builder().build())
15 .with_batch_exporter(exporter)
16 .build()
17}
18
19fn main() {
20 let tracer_provider = init_tracer();
21 global::set_tracer_provider(tracer_provider.clone());
22
23 let tracer = global::tracer("my-app");
24 tracer.in_span("main-operation", |_cx| {
25 // your application logic
26 });
27
28 // Flush pending spans before the application exits
29 tracer_provider.shutdown().expect("Failed to shut down tracer provider");
30}Resource::builder() picks up OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES, and the exporter picks up the endpoint and authorization header from step 1. The same code works inside an async runtime such as Tokio.
4 Run your application#
1cargo runAdd attributes to spans#
Use TraceContextExt to reach the active span and attach attributes that you can filter on in Middleware:
1use opentelemetry::global;
2use opentelemetry::trace::{TraceContextExt, Tracer};
3use opentelemetry::KeyValue;
4
5fn handle_order(order_id: &str) {
6 let tracer = global::tracer("my-app");
7 tracer.in_span("handle-order", |cx| {
8 let span = cx.span();
9 span.set_attribute(KeyValue::new("order.id", order_id.to_string()));
10 // ...
11 });
12}Other deployment targets#
The OpenTelemetry configuration is the same everywhere; only the way you declare environment variables changes.
1docker run \
2 -e OTEL_EXPORTER_OTLP_ENDPOINT="https://<MW_UID>.middleware.io:443" \
3 -e OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" \
4 -e OTEL_EXPORTER_OTLP_HEADERS="authorization=<MW_API_KEY>" \
5 -e OTEL_SERVICE_NAME="<service-name>" \
6 <your-image>1env:
2 - name: OTEL_EXPORTER_OTLP_ENDPOINT
3 value: "https://<MW_UID>.middleware.io:443"
4 - name: OTEL_EXPORTER_OTLP_PROTOCOL
5 value: "http/protobuf"
6 - name: OTEL_EXPORTER_OTLP_HEADERS
7 value: "authorization=<MW_API_KEY>"
8 - name: OTEL_SERVICE_NAME
9 value: "<service-name>"Store the API key in a Kubernetes Secret and reference it with valueFrom.secretKeyRef in production.
View your data#
After you start the application and send some traffic, wait 3–5 minutes, then open APM → Services or the Traces explorer in Middleware and filter by your service name.
Environment variables#
| Variable | Purpose | Example |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | Middleware OTLP endpoint for your project. | https://<uid>.middleware.io:443 |
OTEL_EXPORTER_OTLP_PROTOCOL | OTLP transport used by the exporter. | http/protobuf |
OTEL_EXPORTER_OTLP_HEADERS | Authenticates requests with your Middleware API key. | authorization=<MW_API_KEY> |
OTEL_SERVICE_NAME | Service name shown in Middleware APM. | payments-api |
OTEL_RESOURCE_ATTRIBUTES | Extra resource attributes attached to every span. | service.version=1.4.2,deployment.environment=prod |
Troubleshooting#
NoHttpClientpanic on startup: bothreqwest-clientand the defaultreqwest-blocking-clientfeatures are enabled. Use the dependencies from step 2.- No data in Middleware: confirm the environment variables are set in the process that runs your app (
echo $OTEL_EXPORTER_OTLP_ENDPOINT), and that the endpoint does not already include/v1/traces. - Spans missing for short-lived programs: call
tracer_provider.shutdown()before exiting. The batch exporter sends spans in the background, and anything still buffered is lost if the process exits first. - TLS or connection errors: make sure
reqwest-rustlsis enabled and that outbound HTTPS to<uid>.middleware.io:443is allowed.
Need assistance or want to learn more about Middleware? Get in touch with us via our Contact Us or join our Slack channel.