wasmcloud_provider_sdk/
otel.rs

1/// Instrument a given [`provider_sdk::Context`], injecting current `tracing`-generated metadata
2/// if one isn't present.
3///
4/// This functionality is exposed as a macro since the context for trace injection
5/// should be at the *call site* of this macro (ex. inside some method annotated with `#[instrument]`)
6///
7/// This macro requires `provider_sdk` and `wasmcloud_tracing` to be imported
8#[macro_export]
9macro_rules! propagate_trace_for_ctx {
10    ($ctx:ident) => {{
11        use $crate::wasmcloud_tracing::context::{attach_span_context, TraceContextInjector};
12        let trace_ctx = match $ctx {
13            Some(ref ctx) if !ctx.tracing.is_empty() => ctx
14                .tracing
15                .iter()
16                .map(|(k, v)| (k.to_string(), v.to_string()))
17                .collect::<Vec<(String, String)>>(),
18
19            _ => TraceContextInjector::default_with_span()
20                .iter()
21                .map(|(k, v)| (k.to_string(), v.to_string()))
22                .collect(),
23        };
24        attach_span_context(&trace_ctx);
25    }};
26}
27
28/// Initialize observability for a given provider with host-supplied data, via [`tracing`].
29///
30/// This functionality exists as a macro due to the requirement that `tracing` be initialized
31/// from *binary* code, rather than library code.
32///
33/// This macro loads host data and uses the provider-sdk to build a [`tracing_core::Dispatch`] and
34/// relevant guards/internal structures to configure it with information relevant to the host
35///
36/// This macro introduces the following variables into scope:
37/// - `__observability__guard`
38///
39/// # Arguments
40/// * `provider_name` - An expression that evaluates to a `&str` which is the name of your provider
41/// * `maybe_flamegraphs_path` - An expression that evaluates to a `Option<impl AsRef<Path>>` for flamegraph path
42#[macro_export]
43macro_rules! initialize_observability {
44    ($provider_name:expr, $maybe_flamegraphs_path:expr) => {
45        let __observability_guard = {
46            use $crate::anyhow::Context as _;
47            use $crate::tracing_subscriber::util::SubscriberInitExt as _;
48            let $crate::HostData {
49                config,
50                otel_config,
51                structured_logging,
52                log_level,
53                ..
54            } = $crate::provider::load_host_data().context("failed to load host data")?;
55
56            // Update OTEL configuration with overrides if provided via config to the provider
57            let mut otel_config = otel_config.clone();
58            for (k, v) in config.iter() {
59                match k.to_uppercase().as_str() {
60                    "OTEL_EXPORTER_OTLP_ENDPOINT" => {
61                        otel_config.observability_endpoint = Some(v.clone())
62                    }
63                    "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT" => {
64                        otel_config.traces_endpoint = Some(v.clone())
65                    }
66                    "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT" => {
67                        otel_config.metrics_endpoint = Some(v.clone())
68                    }
69                    "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT" => {
70                        otel_config.logs_endpoint = Some(v.clone())
71                    }
72                    "OTEL_TRACES_SAMPLER" => {
73                        otel_config.traces_sampler = Some(v.clone())
74                    }
75                    "OTEL_TRACES_SAMPLER_ARG" => {
76                        otel_config.traces_sampler_arg = Some(v.clone())
77                    }
78                    "OTEL_BSP_MAX_CONCURRENT_EXPORTS" => {
79                        let parsed = match v.parse::<usize>() {
80                            Ok(v) => v,
81                            Err(_) => {
82                                eprintln!(
83                                    "Failed to parse OTEL_BSP_MAX_CONCURRENT_EXPORTS as usize, using previously set value or default"
84                                );
85                                continue
86                            }
87                        };
88                        otel_config.concurrent_exports = Some(parsed)
89                    }
90                    "OTEL_BSP_MAX_QUEUE_SIZE" => {
91                        let parsed = match v.parse::<usize>() {
92                            Ok(v) => v,
93                            Err(_) => {
94                                eprintln!(
95                                    "Failed to parse OTEL_BSP_MAX_QUEUE_SIZE as usize, using previously set value or default"
96                                );
97                                continue
98                            }
99                        };
100                        otel_config.max_batch_queue_size = Some(parsed)
101                    }
102                    _ => {}
103                }
104            }
105
106            // Init logging
107            //
108            // NOTE: this *must* be done on the provider binary side, to avoid
109            // colliding with the in-process observability setup that happens in the host.
110            let (dispatch, _guard) = $crate::wasmcloud_tracing::configure_observability(
111                $provider_name,
112                &otel_config,
113                *structured_logging,
114                $maybe_flamegraphs_path,
115                log_level.as_ref(),
116                Some(&otel_config.trace_level),
117            )
118            .context("failed to configure observability")?;
119            dispatch
120                .try_init()
121                .context("failed to initialize observability")?;
122            _guard
123        };
124    };
125}