lambda_otel_utils/
subscriber.rs

1//! Utilities for configuring and managing OpenTelemetry tracing subscribers.
2//!
3//! This module provides a builder pattern for configuring tracing subscribers with
4//! OpenTelemetry support, along with utility functions for creating tracing and metrics layers.
5//!
6//! # Examples
7//!
8//! Basic usage with default configuration:
9//!
10//! ```rust,no_run
11//! use lambda_otel_utils::{
12//!     HttpTracerProviderBuilder,
13//!     HttpMeterProviderBuilder,
14//!     init_otel_subscriber
15//! };
16//! use std::time::Duration;
17//!
18//! # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
19//! let tracer_provider = HttpTracerProviderBuilder::default()
20//!     .with_stdout_client()
21//!     .build()?;
22//!
23//! let meter_provider = HttpMeterProviderBuilder::default()
24//!     .with_stdout_client()
25//!     .with_meter_name("my-service")
26//!     .with_export_interval(Duration::from_secs(30))
27//!     .build()?;
28//!
29//! // Initialize with default settings
30//! init_otel_subscriber(tracer_provider, meter_provider, "my-service")?;
31//! # Ok(())
32//! # }
33//! ```
34//!
35//! Custom configuration using the builder:
36//!
37//! ```rust,no_run
38//! use lambda_otel_utils::{
39//!     HttpTracerProviderBuilder,
40//!     HttpMeterProviderBuilder,
41//!     OpenTelemetrySubscriberBuilder
42//! };
43//! use std::time::Duration;
44//!
45//! # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
46//! let tracer_provider = HttpTracerProviderBuilder::default()
47//!     .with_stdout_client()
48//!     .build()?;
49//!
50//! let meter_provider = HttpMeterProviderBuilder::default()
51//!     .with_stdout_client()
52//!     .with_meter_name("my-service")
53//!     .with_export_interval(Duration::from_secs(30))
54//!     .build()?;
55//!
56//! // Custom configuration
57//! OpenTelemetrySubscriberBuilder::new()
58//!     .with_tracer_provider(tracer_provider)
59//!     .with_meter_provider(meter_provider)
60//!     .with_service_name("my-service")
61//!     .with_env_filter(true)
62//!     .with_json_format(true)
63//!     .init()?;
64//! # Ok(())
65//! # }
66//! ```
67
68use opentelemetry::trace::TracerProvider;
69use opentelemetry_sdk::{metrics::SdkMeterProvider, trace::TracerProvider as SdkTracerProvider};
70use tracing_subscriber::prelude::*;
71
72/// Returns a tracing layer configured with the given tracer provider and tracer name.
73///
74/// This function creates an OpenTelemetry tracing layer that can be used with a
75/// tracing subscriber to enable OpenTelemetry integration.
76///
77/// # Type Parameters
78///
79/// * `S` - The type of the subscriber that this layer will be applied to.
80///
81/// # Arguments
82///
83/// * `tracer_provider` - A reference to the tracer provider.
84/// * `tracer_name` - The name of the tracer to be used (must have static lifetime).
85///
86/// # Returns
87///
88/// A `tracing_opentelemetry::OpenTelemetryLayer` configured with the specified tracer.
89///
90/// # Examples
91///
92/// ```rust,no_run
93/// use tracing_subscriber::Registry;
94/// use lambda_otel_utils::{HttpTracerProviderBuilder, create_otel_tracing_layer};
95/// use tracing_subscriber::prelude::*;
96///
97/// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
98/// let tracer_provider = HttpTracerProviderBuilder::default()
99///     .with_stdout_client()
100///     .build()?;
101///
102/// let subscriber = Registry::default()
103///     .with(create_otel_tracing_layer(&tracer_provider, "my-service"));
104/// # Ok(())
105/// # }
106/// ```
107pub fn create_otel_tracing_layer<S>(
108    tracer_provider: &SdkTracerProvider,
109    tracer_name: &'static str,
110) -> tracing_opentelemetry::OpenTelemetryLayer<S, opentelemetry_sdk::trace::Tracer>
111where
112    S: tracing::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>,
113{
114    tracing_opentelemetry::OpenTelemetryLayer::new(tracer_provider.tracer(tracer_name))
115}
116
117/// Returns a metrics layer configured with the given meter provider.
118///
119/// This function creates an OpenTelemetry metrics layer that can be used with a
120/// tracing subscriber to enable metrics collection.
121///
122/// # Type Parameters
123///
124/// * `S` - The type of the subscriber that this layer will be applied to.
125///
126/// # Arguments
127///
128/// * `meter_provider` - A reference to the meter provider.
129///
130/// # Returns
131///
132/// A `tracing_opentelemetry::MetricsLayer` configured with the specified meter provider.
133///
134/// # Examples
135///
136/// ```rust,no_run
137/// use tracing_subscriber::Registry;
138/// use tracing_subscriber::prelude::*;
139/// use lambda_otel_utils::{HttpMeterProviderBuilder, create_otel_metrics_layer};
140/// use std::time::Duration;
141///
142/// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
143/// let meter_provider = HttpMeterProviderBuilder::default()
144///     .with_stdout_client()
145///     .with_meter_name("my-service")
146///     .with_export_interval(Duration::from_secs(30))
147///     .build()?;
148///
149/// let subscriber = Registry::default()
150///     .with(create_otel_metrics_layer(&meter_provider));
151/// # Ok(())
152/// # }
153/// ```
154pub fn create_otel_metrics_layer<S>(
155    meter_provider: &SdkMeterProvider,
156) -> tracing_opentelemetry::MetricsLayer<S>
157where
158    S: tracing::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>,
159{
160    tracing_opentelemetry::MetricsLayer::new(meter_provider.clone())
161}
162
163/// A builder for configuring and creating a tracing subscriber with OpenTelemetry support.
164///
165/// This builder provides a flexible way to configure various aspects of the tracing
166/// subscriber, including OpenTelemetry integration, environment filters, and JSON formatting.
167///
168/// # Examples
169///
170/// ```rust,no_run
171/// use lambda_otel_utils::{
172///     OpenTelemetrySubscriberBuilder,
173///     HttpTracerProviderBuilder,
174///     HttpMeterProviderBuilder
175/// };
176/// use std::time::Duration;
177///
178/// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
179/// let tracer_provider = HttpTracerProviderBuilder::default()
180///     .with_stdout_client()
181///     .build()?;
182///
183/// let meter_provider = HttpMeterProviderBuilder::default()
184///     .with_stdout_client()
185///     .with_meter_name("my-service")
186///     .with_export_interval(Duration::from_secs(30))
187///     .build()?;
188///
189/// OpenTelemetrySubscriberBuilder::new()
190///     .with_tracer_provider(tracer_provider)
191///     .with_meter_provider(meter_provider)
192///     .with_service_name("my-service")
193///     .with_env_filter(true)
194///     .with_json_format(true)
195///     .init()?;
196/// # Ok(())
197/// # }
198/// ```
199#[derive(Default)]
200pub struct OpenTelemetrySubscriberBuilder {
201    tracer_provider: Option<SdkTracerProvider>,
202    meter_provider: Option<SdkMeterProvider>,
203    service_name: Option<&'static str>,
204    with_env_filter: bool,
205    with_json_format: bool,
206}
207
208impl OpenTelemetrySubscriberBuilder {
209    /// Creates a new builder instance with default settings.
210    pub fn new() -> Self {
211        Self::default()
212    }
213
214    /// Sets the tracer provider for the subscriber.
215    pub fn with_tracer_provider(mut self, provider: SdkTracerProvider) -> Self {
216        self.tracer_provider = Some(provider);
217        self
218    }
219
220    /// Sets the meter provider for the subscriber.
221    pub fn with_meter_provider(mut self, provider: SdkMeterProvider) -> Self {
222        self.meter_provider = Some(provider);
223        self
224    }
225
226    /// Sets the service name for the subscriber.
227    pub fn with_service_name(mut self, name: &'static str) -> Self {
228        self.service_name = Some(name);
229        self
230    }
231
232    /// Enables or disables the environment filter.
233    ///
234    /// When enabled, the subscriber will use the `RUST_LOG` environment variable
235    /// to configure logging levels.
236    pub fn with_env_filter(mut self, enabled: bool) -> Self {
237        self.with_env_filter = enabled;
238        self
239    }
240
241    /// Enables or disables JSON formatting for log output.
242    pub fn with_json_format(mut self, enabled: bool) -> Self {
243        self.with_json_format = enabled;
244        self
245    }
246
247    /// Builds and sets the global default subscriber.
248    ///
249    /// # Returns
250    ///
251    /// Returns `Ok(())` if the subscriber was successfully set as the global default,
252    /// or an error if something went wrong.
253    pub fn init(self) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
254        let subscriber = tracing_subscriber::registry::Registry::default();
255
256        // Create optional layers
257        let otel_layer = self
258            .tracer_provider
259            .as_ref()
260            .zip(self.service_name)
261            .map(|(provider, name)| create_otel_tracing_layer(provider, name));
262
263        let metrics_layer = self.meter_provider.as_ref().map(create_otel_metrics_layer);
264
265        let env_filter = if self.with_env_filter {
266            Some(tracing_subscriber::EnvFilter::from_default_env())
267        } else {
268            None
269        };
270
271        let fmt_layer = if self.with_json_format {
272            Some(
273                tracing_subscriber::fmt::layer()
274                    .json()
275                    .with_target(false)
276                    .without_time(),
277            )
278        } else {
279            None
280        };
281
282        // Build the subscriber by conditionally adding layers
283        let subscriber = subscriber
284            .with(otel_layer)
285            .with(metrics_layer)
286            .with(env_filter)
287            .with(fmt_layer);
288
289        // Set the subscriber as the default
290        tracing::subscriber::set_global_default(subscriber)?;
291
292        Ok(())
293    }
294}
295
296/// Convenience function to create and initialize a default OpenTelemetry subscriber.
297///
298/// This function provides a simple way to set up a tracing subscriber with OpenTelemetry
299/// support using sensible defaults.
300///
301/// # Arguments
302///
303/// * `tracer_provider` - The tracer provider to use.
304/// * `meter_provider` - The meter provider to use.
305/// * `service_name` - The name of the service (must have static lifetime).
306///
307/// # Returns
308///
309/// Returns `Ok(())` if the subscriber was successfully initialized, or an error if
310/// something went wrong.
311///
312/// # Examples
313///
314/// ```rust,no_run
315/// use lambda_otel_utils::{
316///     init_otel_subscriber,
317///     HttpTracerProviderBuilder,
318///     HttpMeterProviderBuilder
319/// };
320/// use std::time::Duration;
321///
322/// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
323/// let tracer_provider = HttpTracerProviderBuilder::default()
324///     .with_stdout_client()
325///     .build()?;
326///
327/// let meter_provider = HttpMeterProviderBuilder::default()
328///     .with_stdout_client()
329///     .with_meter_name("my-service")
330///     .with_export_interval(Duration::from_secs(30))
331///     .build()?;
332///
333/// init_otel_subscriber(tracer_provider, meter_provider, "my-service")?;
334/// # Ok(())
335/// # }
336/// ```
337pub fn init_otel_subscriber(
338    tracer_provider: SdkTracerProvider,
339    meter_provider: SdkMeterProvider,
340    service_name: &'static str,
341) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
342    OpenTelemetrySubscriberBuilder::new()
343        .with_tracer_provider(tracer_provider)
344        .with_meter_provider(meter_provider)
345        .with_service_name(service_name)
346        .init()
347}