opentelemetry_lambda_tower/layer.rs
1//! Tower Layer implementation for OpenTelemetry tracing.
2
3use crate::service::OtelTracingService;
4use opentelemetry_sdk::logs::SdkLoggerProvider;
5use opentelemetry_sdk::trace::SdkTracerProvider;
6use std::sync::Arc;
7use std::time::Duration;
8use tower::Layer;
9
10/// Tower layer that adds OpenTelemetry tracing to Lambda handlers.
11///
12/// This layer wraps a service with tracing instrumentation that:
13/// - Extracts trace context from Lambda events
14/// - Creates spans with semantic attributes following OTel conventions
15/// - Handles cold start detection
16/// - Flushes the tracer provider after each invocation
17///
18/// # Type Parameters
19///
20/// * `E` - The trace context extractor type
21///
22/// # Example
23///
24/// ```ignore
25/// use opentelemetry_lambda_tower::{OtelTracingLayer, HttpEventExtractor};
26/// use tower::ServiceBuilder;
27///
28/// let layer = OtelTracingLayer::new(HttpEventExtractor::new());
29///
30/// let service = ServiceBuilder::new()
31/// .layer(layer)
32/// .service(my_handler);
33/// ```
34#[derive(Clone)]
35pub struct OtelTracingLayer<E> {
36 extractor: E,
37 tracer_provider: Option<Arc<SdkTracerProvider>>,
38 logger_provider: Option<Arc<SdkLoggerProvider>>,
39 flush_on_end: bool,
40 flush_timeout: Duration,
41}
42
43impl<E> OtelTracingLayer<E> {
44 /// Creates a new tracing layer with the given extractor.
45 ///
46 /// Uses default settings:
47 /// - Flush on end: enabled
48 /// - Flush timeout: 5 seconds
49 /// - No tracer provider (uses global provider)
50 /// - No logger provider (logs not flushed)
51 pub fn new(extractor: E) -> Self {
52 Self {
53 extractor,
54 tracer_provider: None,
55 logger_provider: None,
56 flush_on_end: true,
57 flush_timeout: Duration::from_secs(5),
58 }
59 }
60
61 /// Creates a builder for more detailed configuration.
62 pub fn builder(extractor: E) -> OtelTracingLayerBuilder<E> {
63 OtelTracingLayerBuilder::new(extractor)
64 }
65}
66
67impl<S, E> Layer<S> for OtelTracingLayer<E>
68where
69 E: Clone,
70{
71 type Service = OtelTracingService<S, E>;
72
73 fn layer(&self, inner: S) -> Self::Service {
74 OtelTracingService::new(
75 inner,
76 self.extractor.clone(),
77 self.tracer_provider.clone(),
78 self.logger_provider.clone(),
79 self.flush_on_end,
80 self.flush_timeout,
81 )
82 }
83}
84
85/// Builder for configuring an [`OtelTracingLayer`].
86///
87/// # Example
88///
89/// ```ignore
90/// use opentelemetry_lambda_tower::{OtelTracingLayer, HttpEventExtractor};
91/// use std::time::Duration;
92///
93/// let layer = OtelTracingLayer::builder(HttpEventExtractor::new())
94/// .tracer_provider(my_provider)
95/// .flush_timeout(Duration::from_secs(10))
96/// .build();
97/// ```
98#[must_use = "builders do nothing unless .build() is called"]
99pub struct OtelTracingLayerBuilder<E> {
100 extractor: E,
101 tracer_provider: Option<Arc<SdkTracerProvider>>,
102 logger_provider: Option<Arc<SdkLoggerProvider>>,
103 flush_on_end: bool,
104 flush_timeout: Duration,
105}
106
107impl<E> OtelTracingLayerBuilder<E> {
108 /// Creates a new builder with the given extractor.
109 pub fn new(extractor: E) -> Self {
110 Self {
111 extractor,
112 tracer_provider: None,
113 logger_provider: None,
114 flush_on_end: true,
115 flush_timeout: Duration::from_secs(5),
116 }
117 }
118
119 /// Sets the tracer provider to use for flushing.
120 ///
121 /// If not set, the layer will attempt to use the global tracer provider.
122 pub fn tracer_provider(mut self, provider: Arc<SdkTracerProvider>) -> Self {
123 self.tracer_provider = Some(provider);
124 self
125 }
126
127 /// Sets the logger provider to use for flushing.
128 ///
129 /// If not set, logs will not be explicitly flushed after each invocation.
130 /// This is required if you want to ensure logs are exported before Lambda
131 /// freezes the execution environment.
132 pub fn logger_provider(mut self, provider: Arc<SdkLoggerProvider>) -> Self {
133 self.logger_provider = Some(provider);
134 self
135 }
136
137 /// Sets whether to flush the tracer provider after each invocation.
138 ///
139 /// Default: `true`
140 ///
141 /// Flushing ensures spans are exported before Lambda freezes the
142 /// execution environment. Disable only if you're handling flushing
143 /// elsewhere (e.g., in an extension).
144 pub fn flush_on_end(mut self, flush: bool) -> Self {
145 self.flush_on_end = flush;
146 self
147 }
148
149 /// Sets the timeout for flush operations.
150 ///
151 /// Default: 5 seconds
152 ///
153 /// If the flush doesn't complete within this duration, it's abandoned
154 /// to prevent blocking the Lambda response.
155 pub fn flush_timeout(mut self, timeout: Duration) -> Self {
156 self.flush_timeout = timeout;
157 self
158 }
159
160 /// Builds the configured layer.
161 pub fn build(self) -> OtelTracingLayer<E> {
162 OtelTracingLayer {
163 extractor: self.extractor,
164 tracer_provider: self.tracer_provider,
165 logger_provider: self.logger_provider,
166 flush_on_end: self.flush_on_end,
167 flush_timeout: self.flush_timeout,
168 }
169 }
170}