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}