aws_runtime/
observability_detection.rs1use aws_smithy_observability_otel::meter::OtelMeterProvider;
14use aws_smithy_runtime::client::sdk_feature::SmithySdkFeature;
15use aws_smithy_runtime_api::box_error::BoxError;
16use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextRef;
17use aws_smithy_runtime_api::client::interceptors::Intercept;
18use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
19use aws_smithy_types::config_bag::ConfigBag;
20
21#[derive(Debug, Default)]
24#[non_exhaustive]
25pub struct ObservabilityDetectionInterceptor;
26
27impl ObservabilityDetectionInterceptor {
28 pub fn new() -> Self {
30 Self
31 }
32}
33
34impl Intercept for ObservabilityDetectionInterceptor {
35 fn name(&self) -> &'static str {
36 "ObservabilityDetectionInterceptor"
37 }
38
39 fn read_before_signing(
40 &self,
41 _context: &BeforeTransmitInterceptorContextRef<'_>,
42 _runtime_components: &RuntimeComponents,
43 cfg: &mut ConfigBag,
44 ) -> Result<(), BoxError> {
45 if let Ok(provider) = aws_smithy_observability::global::get_telemetry_provider() {
47 if provider
51 .meter_provider()
52 .as_any()
53 .downcast_ref::<OtelMeterProvider>()
54 .is_some()
55 {
56 cfg.interceptor_state()
58 .store_append(SmithySdkFeature::ObservabilityMetrics);
59 }
60 }
61
62 Ok(())
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::sdk_feature::AwsSdkFeature;
70 use aws_smithy_observability::TelemetryProvider;
71 use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext};
72 use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
73 use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder;
74 use aws_smithy_types::config_bag::ConfigBag;
75
76 #[test]
77 fn test_detects_noop_provider() {
78 let mut context = InterceptorContext::new(Input::doesnt_matter());
79 context.enter_serialization_phase();
80 context.set_request(HttpRequest::empty());
81 let _ = context.take_input();
82 context.enter_before_transmit_phase();
83
84 let rc = RuntimeComponentsBuilder::for_tests().build().unwrap();
85 let mut cfg = ConfigBag::base();
86
87 let _ = aws_smithy_observability::global::set_telemetry_provider(TelemetryProvider::noop());
89
90 let interceptor = ObservabilityDetectionInterceptor::new();
91 let ctx = Into::into(&context);
92 interceptor
93 .read_before_signing(&ctx, &rc, &mut cfg)
94 .unwrap();
95
96 let smithy_features: Vec<_> = cfg.load::<SmithySdkFeature>().collect();
98 assert_eq!(smithy_features.len(), 0);
99
100 let aws_features: Vec<_> = cfg.load::<AwsSdkFeature>().collect();
101 assert_eq!(aws_features.len(), 0);
102 }
103
104 #[test]
105 fn test_custom_provider_not_detected_as_otel() {
106 use aws_smithy_observability::meter::{Meter, ProvideMeter};
107 use aws_smithy_observability::noop::NoopMeterProvider;
108 use aws_smithy_observability::Attributes;
109 use std::sync::Arc;
110
111 #[derive(Debug)]
114 struct CustomMeterProvider {
115 inner: NoopMeterProvider,
116 }
117
118 impl ProvideMeter for CustomMeterProvider {
119 fn get_meter(&self, scope: &'static str, attributes: Option<&Attributes>) -> Meter {
120 self.inner.get_meter(scope, attributes)
122 }
123
124 fn as_any(&self) -> &dyn std::any::Any {
125 self
126 }
127 }
128
129 let mut context = InterceptorContext::new(Input::doesnt_matter());
130 context.enter_serialization_phase();
131 context.set_request(HttpRequest::empty());
132 let _ = context.take_input();
133 context.enter_before_transmit_phase();
134
135 let rc = RuntimeComponentsBuilder::for_tests().build().unwrap();
136 let mut cfg = ConfigBag::base();
137
138 let custom_provider = Arc::new(CustomMeterProvider {
140 inner: NoopMeterProvider,
141 });
142 let telemetry_provider = TelemetryProvider::builder()
143 .meter_provider(custom_provider)
144 .build();
145 let _ = aws_smithy_observability::global::set_telemetry_provider(telemetry_provider);
146
147 let interceptor = ObservabilityDetectionInterceptor::new();
148 let ctx = Into::into(&context);
149 interceptor
150 .read_before_signing(&ctx, &rc, &mut cfg)
151 .unwrap();
152
153 let smithy_features: Vec<_> = cfg
156 .interceptor_state()
157 .load::<SmithySdkFeature>()
158 .cloned()
159 .collect();
160 assert!(
161 !smithy_features.iter().any(|f| *f == SmithySdkFeature::ObservabilityMetrics),
162 "Should not detect custom provider as having observability metrics (only OTel is tracked)"
163 );
164
165 let aws_features: Vec<_> = cfg
167 .interceptor_state()
168 .load::<AwsSdkFeature>()
169 .cloned()
170 .collect();
171 assert_eq!(
172 aws_features.len(),
173 0,
174 "Should not track any AWS-specific features for custom provider"
175 );
176 }
177}