1use crate::conmon_capnp::conmon;
6use anyhow::{Context, Result};
7use capnp::struct_list::Reader;
8use nix::unistd::gethostname;
9use opentelemetry::{KeyValue, global, propagation::Extractor};
10use opentelemetry_otlp::{SpanExporter, WithExportConfig};
11use opentelemetry_sdk::{Resource, propagation::TraceContextPropagator, trace::SdkTracerProvider};
12use std::{collections::HashMap, process};
13use tracing::Span;
14use tracing_opentelemetry::{OpenTelemetrySpanExt, SetParentError};
15
16const HOST_NAME: &str = "host.name";
19const PROCESS_PID: &str = "process.pid";
20
21pub(crate) fn tracer_provider(endpoint: &str) -> Result<SdkTracerProvider> {
23 global::set_text_map_propagator(TraceContextPropagator::new());
24
25 let exporter = SpanExporter::builder()
26 .with_tonic()
27 .with_endpoint(endpoint)
28 .build()
29 .context("build exporter")?;
30
31 let hostname = gethostname()
32 .context("get hostname")?
33 .to_str()
34 .context("convert hostname to string")?
35 .to_string();
36
37 let tracer = SdkTracerProvider::builder()
38 .with_batch_exporter(exporter)
39 .with_resource(
40 Resource::builder()
41 .with_service_name(env!("CARGO_PKG_NAME"))
42 .with_attributes([
43 KeyValue::new(PROCESS_PID, i64::from(process::id())),
44 KeyValue::new(HOST_NAME, hostname),
45 ])
46 .build(),
47 )
48 .build();
49
50 Ok(tracer)
51}
52
53pub(crate) fn set_parent_context(
56 span: &Span,
57 reader: Reader<conmon::text_text_map_entry::Owned>,
58) -> Result<()> {
59 if reader.is_empty() {
60 return Ok(());
62 }
63
64 let metadata = Metadata(into_map(reader)?);
65 let ctx = global::get_text_map_propagator(|prop| prop.extract(&metadata));
66 match span.set_parent(ctx) {
69 Ok(()) | Err(SetParentError::LayerNotFound | SetParentError::SpanDisabled) => Ok(()),
70 Err(e) => Err(e).context("set parent context"),
71 }
72}
73
74fn into_map<'a>(
77 reader: Reader<'a, conmon::text_text_map_entry::Owned>,
78) -> capnp::Result<HashMap<&'a str, &'a str>> {
79 reader
80 .iter()
81 .map(|entry| Ok((entry.get_key()?.to_str()?, entry.get_value()?.to_str()?)))
82 .collect()
83}
84
85struct Metadata<'a>(HashMap<&'a str, &'a str>);
87
88impl Extractor for Metadata<'_> {
89 fn get(&self, key: &str) -> Option<&str> {
90 self.0.get(key).copied()
91 }
92
93 fn keys(&self) -> Vec<&str> {
95 self.0.keys().copied().collect()
96 }
97}
98
99#[cfg(test)]
100mod tests;