lambda_otel_utils/
http_meter_provider.rs

1//! This module provides utilities for configuring and building an OpenTelemetry MeterProvider
2//! specifically tailored for use in AWS Lambda environments.
3//!
4//! It includes:
5//! - `HttpMeterProviderBuilder`: A builder struct for configuring and initializing a MeterProvider.
6//!
7//! The module supports various configuration options, including:
8//! - Custom HTTP clients for exporting metrics
9//! - Setting custom meter names
10//! - Configuring periodic exporters
11//! - Integration with Lambda resource attributes
12
13use std::time::Duration;
14
15use opentelemetry_http::HttpClient;
16use opentelemetry_otlp::{WithExportConfig, WithHttpConfig};
17use opentelemetry_sdk::{
18    error::OTelSdkError,
19    metrics::{PeriodicReader, SdkMeterProvider},
20};
21use otlp_stdout_client::StdoutClient;
22
23/// Builder for configuring and initializing a MeterProvider.
24///
25/// This struct provides a fluent interface for configuring various aspects of the
26/// OpenTelemetry metrics setup, including the exporter configuration and meter names.
27///
28/// # Examples
29///
30/// ```
31/// use lambda_otel_utils::HttpMeterProviderBuilder;
32/// use std::time::Duration;
33///
34/// #[tokio::main]
35/// async fn main() -> Result<(), opentelemetry_sdk::error::Error> {
36///     let meter_provider = HttpMeterProviderBuilder::default()
37///         .with_stdout_client()
38///         .with_meter_name("my-service")
39///         .with_export_interval(Duration::from_secs(60))
40///         .build()?;
41///     Ok(())
42/// }
43/// ```
44#[derive(Debug)]
45pub struct HttpMeterProviderBuilder<C: HttpClient + 'static = StdoutClient> {
46    client: Option<C>,
47    meter_name: Option<&'static str>,
48    export_interval: Duration,
49    export_timeout: Duration,
50    install_global: bool,
51}
52
53impl Default for HttpMeterProviderBuilder {
54    fn default() -> Self {
55        Self::new()
56    }
57}
58
59impl HttpMeterProviderBuilder {
60    /// Creates a new `HttpMeterProviderBuilder` with default settings.
61    pub fn new() -> Self {
62        Self {
63            client: None,
64            meter_name: None,
65            export_interval: Duration::from_secs(60),
66            export_timeout: Duration::from_secs(10),
67            install_global: false,
68        }
69    }
70
71    /// Configures the builder to use a stdout client for exporting metrics.
72    pub fn with_stdout_client(mut self) -> Self {
73        self.client = Some(StdoutClient::new());
74        self
75    }
76
77    /// Sets the meter name.
78    ///
79    /// # Arguments
80    ///
81    /// * `meter_name` - A static string reference (string literal)
82    pub fn with_meter_name(mut self, meter_name: &'static str) -> Self {
83        self.meter_name = Some(meter_name);
84        self
85    }
86
87    /// Sets the export interval for periodic metric collection.
88    pub fn with_export_interval(mut self, interval: Duration) -> Self {
89        self.export_interval = interval;
90        self
91    }
92
93    /// Sets the export timeout for metric collection.
94    pub fn with_export_timeout(mut self, timeout: Duration) -> Self {
95        self.export_timeout = timeout;
96        self
97    }
98
99    /// Enables or disables global installation of the meter provider.
100    ///
101    /// # Examples
102    ///
103    /// ```
104    /// use lambda_otel_utils::HttpMeterProviderBuilder;
105    ///
106    /// let builder = HttpMeterProviderBuilder::default().enable_global(true);
107    /// ```
108    pub fn enable_global(mut self, set_global: bool) -> Self {
109        self.install_global = set_global;
110        self
111    }
112
113    /// Builds the `MeterProvider` with the configured settings.
114    pub fn build(self) -> Result<SdkMeterProvider, OTelSdkError> {
115        let mut exporter_builder = opentelemetry_otlp::MetricExporter::builder()
116            .with_http()
117            .with_protocol(crate::protocol::get_protocol())
118            .with_timeout(self.export_timeout);
119
120        if let Some(client) = self.client {
121            exporter_builder = exporter_builder.with_http_client(client);
122        }
123
124        let exporter = exporter_builder
125            .build()
126            .map_err(|e| OTelSdkError::InternalFailure(e.to_string()))?;
127
128        let reader = PeriodicReader::builder(exporter)
129            .with_interval(self.export_interval)
130            .build();
131
132        let provider = SdkMeterProvider::builder()
133            .with_reader(reader)
134            .with_resource(crate::resource::get_lambda_resource())
135            .build();
136
137        // Optionally install global provider
138        if self.install_global {
139            opentelemetry::global::set_meter_provider(provider.clone());
140        }
141
142        Ok(provider)
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    #[test]
151    fn test_http_meter_provider_builder_default() {
152        let builder = HttpMeterProviderBuilder::default();
153        assert!(builder.client.is_none());
154        assert!(builder.meter_name.is_none());
155        assert_eq!(builder.export_interval, Duration::from_secs(60));
156        assert_eq!(builder.export_timeout, Duration::from_secs(10));
157        assert!(!builder.install_global);
158    }
159
160    #[test]
161    fn test_http_meter_provider_builder_customization() {
162        let builder = HttpMeterProviderBuilder::new()
163            .with_stdout_client()
164            .with_meter_name("test-meter")
165            .with_export_interval(Duration::from_secs(30))
166            .with_export_timeout(Duration::from_secs(5))
167            .enable_global(true);
168
169        assert!(builder.client.is_some());
170        assert_eq!(builder.meter_name, Some("test-meter"));
171        assert_eq!(builder.export_interval, Duration::from_secs(30));
172        assert_eq!(builder.export_timeout, Duration::from_secs(5));
173        assert!(builder.install_global);
174    }
175}