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}