Skip to main content

ash_rpc/observability/
macros.rs

1//! Macros for simplified observability setup
2
3/// Simplifies observability stack initialization with a declarative syntax
4///
5/// # Examples
6///
7/// Basic setup with all features:
8/// ```rust,ignore
9/// use ash_rpc::observability::observable_setup;
10///
11/// let observability = observable_setup! {
12///     service_name: "my-service",
13///     metrics_prefix: "my_app",
14///     otlp_endpoint: "http://jaeger:4317",
15/// };
16/// ```
17///
18/// Minimal setup with just metrics:
19/// ```rust,ignore
20/// let observability = observable_setup! {
21///     metrics_prefix: "my_app",
22/// };
23/// ```
24///
25/// With custom logger:
26/// ```rust,ignore
27/// let observability = observable_setup! {
28///     service_name: "my-service",
29///     metrics_prefix: "my_app",
30///     otlp_endpoint: env!("JAEGER_ENDPOINT"),
31///     log_level: "debug",
32/// };
33/// ```
34#[macro_export]
35macro_rules! observable_setup {
36    // Full setup with all options
37    (
38        service_name: $service_name:expr,
39        metrics_prefix: $metrics_prefix:expr,
40        otlp_endpoint: $otlp_endpoint:expr
41        $(, log_level: $log_level:expr)?
42        $(,)?
43    ) => {{
44        #[allow(unused_imports)]
45        use $crate::logger::{Logger, TracingLogger};
46
47        // Setup logger
48        let logger: ::std::sync::Arc<dyn Logger> = ::std::sync::Arc::new(TracingLogger::new());
49        logger.info(
50            "Initializing observability stack",
51            &[
52                ("service", &$service_name),
53                ("metrics_prefix", &$metrics_prefix),
54            ],
55        );
56
57        // Initialize OpenTelemetry tracer
58        #[cfg(feature = "opentelemetry")]
59        {
60            use ::opentelemetry::global;
61            use ::opentelemetry_otlp::{SpanExporter, WithExportConfig};
62            use ::opentelemetry_sdk::Resource;
63            use ::opentelemetry_sdk::trace::TracerProvider;
64
65            logger.info(
66                "Initializing OpenTelemetry tracer",
67                &[("endpoint", &$otlp_endpoint)],
68            );
69
70            let exporter = SpanExporter::builder()
71                .with_tonic()
72                .with_endpoint($otlp_endpoint)
73                .build()
74                .expect("Failed to create OTLP exporter");
75
76            let tracer_provider = TracerProvider::builder()
77                .with_batch_exporter(exporter, ::opentelemetry_sdk::runtime::Tokio)
78                .with_resource(Resource::new(vec![::opentelemetry::KeyValue::new(
79                    "service.name",
80                    $service_name,
81                )]))
82                .build();
83
84            global::set_tracer_provider(tracer_provider);
85            logger.info("OpenTelemetry tracer initialized", &[]);
86        }
87
88        // Create Prometheus metrics
89        #[cfg(feature = "prometheus")]
90        let metrics = {
91            let m = ::std::sync::Arc::new(
92                $crate::observability::prometheus::PrometheusMetrics::with_prefix($metrics_prefix)
93                    .expect("Failed to create Prometheus metrics"),
94            );
95
96            // Register process metrics on Linux
97            #[cfg(all(target_os = "linux", feature = "prometheus"))]
98            {
99                let process_collector =
100                    $crate::prometheus::process_collector::ProcessCollector::for_self();
101                m.registry()
102                    .register(Box::new(process_collector))
103                    .expect("Failed to register process collector");
104                logger.info("Prometheus metrics initialized with process collector", &[]);
105            }
106
107            #[cfg(not(target_os = "linux"))]
108            logger.info("Prometheus metrics initialized", &[]);
109
110            m
111        };
112
113        #[cfg(not(feature = "prometheus"))]
114        let metrics = ();
115
116        $crate::observability::macros::ObservabilityStack {
117            #[cfg(feature = "prometheus")]
118            metrics,
119            logger,
120        }
121    }};
122
123    // Minimal setup - just metrics
124    (
125        metrics_prefix: $metrics_prefix:expr
126        $(,)?
127    ) => {{
128        #[allow(unused_imports)]
129        use $crate::logger::{Logger, TracingLogger};
130
131        let logger: ::std::sync::Arc<dyn Logger> = ::std::sync::Arc::new(TracingLogger::new());
132        logger.info(
133            "Initializing observability stack",
134            &[("metrics_prefix", &$metrics_prefix)],
135        );
136
137        #[cfg(feature = "prometheus")]
138        let metrics = {
139            let m = ::std::sync::Arc::new(
140                $crate::observability::prometheus::PrometheusMetrics::with_prefix($metrics_prefix)
141                    .expect("Failed to create Prometheus metrics"),
142            );
143
144            #[cfg(all(target_os = "linux", feature = "prometheus"))]
145            {
146                let process_collector =
147                    $crate::prometheus::process_collector::ProcessCollector::for_self();
148                m.registry()
149                    .register(Box::new(process_collector))
150                    .expect("Failed to register process collector");
151                logger.info("Prometheus metrics initialized with process collector", &[]);
152            }
153
154            #[cfg(not(target_os = "linux"))]
155            logger.info("Prometheus metrics initialized", &[]);
156
157            m
158        };
159
160        #[cfg(not(feature = "prometheus"))]
161        let metrics = ();
162
163        $crate::observability::macros::ObservabilityStack {
164            #[cfg(feature = "prometheus")]
165            metrics,
166            logger,
167        }
168    }};
169
170    // With service name and metrics only (no tracing)
171    (
172        service_name: $service_name:expr,
173        metrics_prefix: $metrics_prefix:expr
174        $(,)?
175    ) => {{
176        #[allow(unused_imports)]
177        use $crate::logger::{Logger, TracingLogger};
178
179        let logger: ::std::sync::Arc<dyn Logger> = ::std::sync::Arc::new(TracingLogger::new());
180        logger.info(
181            "Initializing observability stack",
182            &[
183                ("service", &$service_name),
184                ("metrics_prefix", &$metrics_prefix),
185            ],
186        );
187
188        #[cfg(feature = "prometheus")]
189        let metrics = {
190            let m = ::std::sync::Arc::new(
191                $crate::observability::prometheus::PrometheusMetrics::with_prefix($metrics_prefix)
192                    .expect("Failed to create Prometheus metrics"),
193            );
194
195            #[cfg(all(target_os = "linux", feature = "prometheus"))]
196            {
197                let process_collector =
198                    $crate::prometheus::process_collector::ProcessCollector::for_self();
199                m.registry()
200                    .register(Box::new(process_collector))
201                    .expect("Failed to register process collector");
202                logger.info("Prometheus metrics initialized with process collector", &[]);
203            }
204
205            #[cfg(not(target_os = "linux"))]
206            logger.info("Prometheus metrics initialized", &[]);
207
208            m
209        };
210
211        #[cfg(not(feature = "prometheus"))]
212        let metrics = ();
213
214        $crate::observability::macros::ObservabilityStack {
215            #[cfg(feature = "prometheus")]
216            metrics,
217            logger,
218        }
219    }};
220}
221
222/// Container for observability components returned by `observable_setup`!
223pub struct ObservabilityStack {
224    #[cfg(feature = "prometheus")]
225    pub metrics: ::std::sync::Arc<super::prometheus::PrometheusMetrics>,
226    pub logger: ::std::sync::Arc<dyn crate::logger::Logger>,
227}
228
229impl ObservabilityStack {
230    /// Get the metrics collector
231    #[cfg(feature = "prometheus")]
232    #[must_use]
233    pub fn metrics(&self) -> ::std::sync::Arc<super::prometheus::PrometheusMetrics> {
234        ::std::sync::Arc::clone(&self.metrics)
235    }
236
237    /// Get the logger
238    #[must_use]
239    pub fn logger(&self) -> ::std::sync::Arc<dyn crate::logger::Logger> {
240        ::std::sync::Arc::clone(&self.logger)
241    }
242}