Skip to main content

telemetry_rust/middleware/aws/instrumentation/
future.rs

1use aws_smithy_types::error::metadata::ProvideErrorMetadata;
2use aws_types::request_id::RequestId;
3use std::{error::Error, future::Future};
4
5use crate::{
6    future::{InstrumentedFuture, InstrumentedFutureContext},
7    middleware::aws::{AwsSpan, AwsSpanBuilder},
8};
9
10impl<T, E> InstrumentedFutureContext<Result<T, E>> for AwsSpan
11where
12    T: RequestId,
13    E: RequestId + ProvideErrorMetadata + Error,
14{
15    fn on_result(self, result: &Result<T, E>) {
16        self.end(result);
17    }
18}
19
20/// Trait for instrumenting AWS futures with automatic span management.
21///
22/// This trait provides a convenient way to wrap AWS SDK futures with OpenTelemetry
23/// instrumentation, automatically handling span creation, error recording, and cleanup.
24///
25/// For automatic span attributes extraction, use [`AwsBuilderInstrument`][`super::AwsBuilderInstrument`] trait.
26///
27/// # Example
28///
29/// ```rust
30/// use aws_sdk_dynamodb::{Client as DynamoClient, types::AttributeValue};
31/// use telemetry_rust::{
32///     KeyValue,
33///     middleware::aws::{AwsInstrument, DynamodbSpanBuilder},
34///     semconv,
35/// };
36///
37/// async fn query_table() -> Result<i32, Box<dyn std::error::Error>> {
38///     let config = aws_config::load_from_env().await;
39///     let dynamo_client = DynamoClient::new(&config);
40///     let resp =
41///         dynamo_client
42///             .query()
43///             .table_name("table_name")
44///             .index_name("my_index")
45///             .key_condition_expression("PK = :pk")
46///             .expression_attribute_values(":pk", AttributeValue::S("Test".to_string()))
47///             .send()
48///             .instrument(DynamodbSpanBuilder::query("table_name").attribute(
49///                 KeyValue::new(semconv::AWS_DYNAMODB_INDEX_NAME, "my_index"),
50///             ))
51///             .await?;
52///     println!("DynamoDB items: {:#?}", resp.items());
53///     Ok(resp.count())
54/// }
55/// ```
56pub trait AwsInstrument<T, E, F>
57where
58    T: RequestId,
59    E: RequestId + ProvideErrorMetadata + Error,
60    F: Future<Output = Result<T, E>>,
61{
62    /// Instruments the future with an AWS span.
63    ///
64    /// Creates an instrumented future that will automatically start a span when polled
65    /// and properly handle the result when the future completes.
66    ///
67    /// # Arguments
68    ///
69    /// * `span` - A span builder or span to use for instrumentation
70    ///
71    /// # Returns
72    ///
73    /// An instrumented future that will record AWS operation details
74    fn instrument<'a>(
75        self,
76        span: impl Into<AwsSpanBuilder<'a>>,
77    ) -> InstrumentedFuture<F, AwsSpan>;
78}
79
80impl<T, E, F> AwsInstrument<T, E, F> for F
81where
82    T: RequestId,
83    E: RequestId + ProvideErrorMetadata + Error,
84    F: Future<Output = Result<T, E>>,
85{
86    fn instrument<'a>(
87        self,
88        span: impl Into<AwsSpanBuilder<'a>>,
89    ) -> InstrumentedFuture<F, AwsSpan> {
90        let span = span.into().start();
91        InstrumentedFuture::new(self, span)
92    }
93}