opentelemetry-xray is a Rust library that provides a flexible interface for AWS X‑Ray distributed tracing using OpenTelemetry. It simplifies the integration of AWS X‑Ray with your applications by setting up a global tracer provider, mapping tracing contexts into AWS X‑Ray–compatible formats, and supporting rich span information such as annotations, metadata, exceptions, and HTTP context.
- AWS X‑Ray Integration:
- Formats trace IDs according to AWS X‑Ray standards.
- Transforms the standard
traceparent
header into the AWS X‑Ray header (x-amzn-trace-id
). - Exports span data as JSON segments that AWS X‑Ray can understand.
- OpenTelemetry & Tracing Support:
- Leverages the robust OpenTelemetry ecosystem.
- Integrates seamlessly with the
tracing
crate for structured logging and span management.
- Span Enrichment:
- Annotations: Indexed fields (e.g. use the prefix
annotation.
) for filtering and searching. - Metadata: Additional non-indexed data (using the
metadata.
prefix). - Exceptions: Detailed error information (with keys like
exception.type
andexception.message
). - HTTP Context: Record HTTP request and response details (using keys such as
http.request.method
andhttp.response.status
).
- Annotations: Indexed fields (e.g. use the prefix
Note: In these examples we show keys as dot‑separated (e.g.
annotation.otel.kind
) without extra quotation marks. In your actual Rust code, if a field name isn’t a valid identifier (because it includes a period), you may need to use a string literal (for example,"annotation.otel.kind" = "server"
)—the important part is that the exported key appears asannotation.otel.kind
(and similarly forhttp.request
,http.response
, etc).
Add the following to your Cargo.toml
:
[dependencies]
opentelemetry-xray = "0.1.0"
opentelemetry = { version = "0.27.1", features = ["trace"] }
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter", "json"] }
Initialize your telemetry and create a span enriched with annotations, metadata, and exception details:
use opentelemetry_xray::{Telemetry, TelemetryConfig};
use tracing::{info_span, error, Instrument};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = TelemetryConfig {
service_name: "my-service".into(),
service_version: env!("CARGO_PKG_VERSION").into(),
deployment_env: "production".into(),
log_level: "info".into(),
};
// Initialize the propagator, tracer provider, and subscriber.
let telemetry = Telemetry::init(config)?;
// Create a span with enriched attributes. In these examples, we conceptually use keys like:
// annotation.user_id, metadata.request_id, http.request.method, etc.
let span = info_span!(
main_operation,
annotation.user_id = 42,
metadata.request_id = "abc123",
// HTTP-related attributes:
http.request.method = "GET",
http.request.url = "https://example.com/api"
);
async {
if let Err(e) = do_something().await {
// Record exception details:
error!(
exception.type = "OperationError",
exception.message = %e,
"Operation failed"
);
}
}
.instrument(span)
.await;
telemetry.shutdown();
Ok(())
}
async fn do_something() -> Result<(), &'static str> {
Err("Something went wrong")
}
Annotations are meant to be indexed and are useful for filtering and searching in AWS X‑Ray. Specify annotation attributes using the annotation.
prefix:
use tracing::info_span;
let span = info_span!(
processing_order,
annotation.order_id = 1001,
annotation.priority = "high"
);
Metadata provides additional context but is not indexed. Use the metadata.
prefix:
let span = info_span!(
user_session,
metadata.session_id = "xyz789",
metadata.user_agent = "Mozilla/5.0"
);
When errors occur, capture detailed error information by using the exception.
prefix:
use tracing::error;
let err = "Null Pointer Exception";
error!(
exception.type = "NullPointerException",
exception.message = err,
"An error occurred"
);
Extract incoming trace contexts from HTTP requests and create spans enriched with HTTP attributes:
use axum::{Router, routing::get};
use tower_http::trace::TraceLayer;
use opentelemetry_xray::{extract_headers, Telemetry};
use axum::http::Request;
use axum::body::Body;
async fn handler() {
// Your request handling logic.
}
async fn build_router() -> Router {
Router::new()
.route("/", get(handler))
.layer(
TraceLayer::new_for_http().make_span_with(|request: &Request<Body>| {
let span = tracing::info_span!(
http_request,
http.request.method = ?request.method(),
http.request.url = ?request.uri()
);
// Extract and set the incoming trace context:
let parent_context = extract_headers(request.headers());
span.set_parent(parent_context);
span
})
)
}
Inject the current trace context into outgoing HTTP requests so downstream services continue the distributed trace:
use reqwest::Client;
use http::HeaderMap;
use opentelemetry_xray::inject_headers;
async fn make_request(client: &Client, url: &str) -> Result<(), reqwest::Error> {
let mut headers = HeaderMap::new();
// Inject the current OpenTelemetry context (this converts the standard
// `traceparent` header to the AWS X‑Ray header, e.g. `x-amzn-trace-id`).
inject_headers(&mut headers);
client.get(url)
.headers(headers)
.send()
.await?;
Ok(())
}
Configure telemetry using the TelemetryConfig
struct:
let config = TelemetryConfig {
service_name: "my-service".into(),
service_version: env!("CARGO_PKG_VERSION").into(),
deployment_env: "production".into(),
log_level: "info".into(),
};
- Initialization:
- Use
Telemetry::init()
for a standardized setup of the propagator, tracer provider, and subscriber. - Always call
telemetry.shutdown()
at application exit to ensure all spans are flushed.
- Use
- Span Enrichment:
- Use
tracing
macros (e.g.,info_span!
,error!
) to create spans. - Use meaningful dot‑separated keys (e.g.,
annotation.
,metadata.
,http.request.
,exception.
) to indicate the purpose of each attribute.
- Use
- Error Handling:
- Capture and report exceptions using the designated keys to facilitate troubleshooting.
- HTTP Instrumentation:
- For server-side tracing, extract HTTP headers via
extract_headers
. - For client-side requests, inject the trace context with
inject_headers
.
- For server-side tracing, extract HTTP headers via
Run the test suite:
cargo test
This project is licensed under the MIT License – see the LICENSE file for details.
Contributions are welcome! Please open an issue or submit a pull request with your improvements or bug fixes.
For issues and feature requests, please use the GitHub issue tracker.