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}