Skip to main content

spin_world/wasi_otel/
trace_conversions.rs

1use crate::wasi;
2use opentelemetry_sdk::trace::{SpanEvents, SpanLinks};
3
4impl From<wasi::otel::tracing::SpanData> for opentelemetry_sdk::trace::SpanData {
5    fn from(value: wasi::otel::tracing::SpanData) -> Self {
6        let mut span_events = SpanEvents::default();
7        span_events.events = value.events.into_iter().map(Into::into).collect();
8        span_events.dropped_count = value.dropped_events;
9        let mut span_links = SpanLinks::default();
10        span_links.links = value.links.into_iter().map(Into::into).collect();
11        span_links.dropped_count = value.dropped_links;
12        Self {
13            span_context: value.span_context.into(),
14            parent_span_id: opentelemetry::trace::SpanId::from_hex(&value.parent_span_id)
15                .unwrap_or(opentelemetry::trace::SpanId::INVALID),
16            span_kind: value.span_kind.into(),
17            name: value.name.into(),
18            start_time: value.start_time.into(),
19            end_time: value.end_time.into(),
20            attributes: value.attributes.into_iter().map(Into::into).collect(),
21            dropped_attributes_count: value.dropped_attributes,
22            events: span_events,
23            links: span_links,
24            status: value.status.into(),
25            instrumentation_scope: value.instrumentation_scope.into(),
26        }
27    }
28}
29
30impl From<wasi::otel::tracing::SpanContext> for opentelemetry::trace::SpanContext {
31    fn from(sc: wasi::otel::tracing::SpanContext) -> Self {
32        let trace_id = opentelemetry::trace::TraceId::from_hex(&sc.trace_id)
33            .unwrap_or(opentelemetry::trace::TraceId::INVALID);
34        let span_id = opentelemetry::trace::SpanId::from_hex(&sc.span_id)
35            .unwrap_or(opentelemetry::trace::SpanId::INVALID);
36        let trace_state = opentelemetry::trace::TraceState::from_key_value(sc.trace_state)
37            .unwrap_or_else(|_| opentelemetry::trace::TraceState::default());
38        Self::new(
39            trace_id,
40            span_id,
41            sc.trace_flags.into(),
42            sc.is_remote,
43            trace_state,
44        )
45    }
46}
47
48impl From<opentelemetry::trace::SpanContext> for wasi::otel::tracing::SpanContext {
49    fn from(sc: opentelemetry::trace::SpanContext) -> Self {
50        Self {
51            trace_id: format!("{:x}", sc.trace_id()),
52            span_id: format!("{:x}", sc.span_id()),
53            trace_flags: sc.trace_flags().into(),
54            is_remote: sc.is_remote(),
55            trace_state: sc
56                .trace_state()
57                .header()
58                .split(',')
59                .filter_map(|s| {
60                    if let Some((key, value)) = s.split_once('=') {
61                        Some((key.to_string(), value.to_string()))
62                    } else {
63                        None
64                    }
65                })
66                .collect(),
67        }
68    }
69}
70
71impl From<wasi::otel::tracing::TraceFlags> for opentelemetry::trace::TraceFlags {
72    fn from(flags: wasi::otel::tracing::TraceFlags) -> Self {
73        Self::new(flags.as_array()[0] as u8)
74    }
75}
76
77impl From<opentelemetry::trace::TraceFlags> for wasi::otel::tracing::TraceFlags {
78    fn from(flags: opentelemetry::trace::TraceFlags) -> Self {
79        if flags.is_sampled() {
80            wasi::otel::tracing::TraceFlags::SAMPLED
81        } else {
82            wasi::otel::tracing::TraceFlags::empty()
83        }
84    }
85}
86
87impl From<wasi::otel::tracing::SpanKind> for opentelemetry::trace::SpanKind {
88    fn from(kind: wasi::otel::tracing::SpanKind) -> Self {
89        match kind {
90            wasi::otel::tracing::SpanKind::Client => opentelemetry::trace::SpanKind::Client,
91            wasi::otel::tracing::SpanKind::Server => opentelemetry::trace::SpanKind::Server,
92            wasi::otel::tracing::SpanKind::Producer => opentelemetry::trace::SpanKind::Producer,
93            wasi::otel::tracing::SpanKind::Consumer => opentelemetry::trace::SpanKind::Consumer,
94            wasi::otel::tracing::SpanKind::Internal => opentelemetry::trace::SpanKind::Internal,
95        }
96    }
97}
98
99impl From<wasi::otel::tracing::Event> for opentelemetry::trace::Event {
100    fn from(event: wasi::otel::tracing::Event) -> Self {
101        Self::new(
102            event.name,
103            event.time.into(),
104            event.attributes.into_iter().map(Into::into).collect(),
105            0,
106        )
107    }
108}
109
110impl From<wasi::otel::tracing::Link> for opentelemetry::trace::Link {
111    fn from(link: wasi::otel::tracing::Link) -> Self {
112        Self::new(
113            link.span_context.into(),
114            link.attributes.into_iter().map(Into::into).collect(),
115            0,
116        )
117    }
118}
119
120impl From<wasi::otel::tracing::Status> for opentelemetry::trace::Status {
121    fn from(status: wasi::otel::tracing::Status) -> Self {
122        match status {
123            wasi::otel::tracing::Status::Unset => Self::Unset,
124            wasi::otel::tracing::Status::Ok => Self::Ok,
125            wasi::otel::tracing::Status::Error(s) => Self::Error {
126                description: s.into(),
127            },
128        }
129    }
130}
131
132mod test {
133    #[test]
134    fn trace_flags() {
135        let flags = opentelemetry::trace::TraceFlags::SAMPLED;
136        let flags2 = crate::wasi::otel::tracing::TraceFlags::from(flags);
137        let flags3 = opentelemetry::trace::TraceFlags::from(flags2);
138        assert_eq!(flags, flags3);
139    }
140
141    #[test]
142    fn span_context() {
143        let sc = opentelemetry::trace::SpanContext::new(
144            opentelemetry::trace::TraceId::from_hex("4fb34cb4484029f7881399b149e41e98").unwrap(),
145            opentelemetry::trace::SpanId::from_hex("9ffd58d3cd4dd90b").unwrap(),
146            opentelemetry::trace::TraceFlags::SAMPLED,
147            false,
148            opentelemetry::trace::TraceState::from_key_value(vec![("foo", "bar"), ("baz", "qux")])
149                .unwrap(),
150        );
151        let sc2 = crate::wasi::otel::tracing::SpanContext::from(sc.clone());
152        let sc3 = opentelemetry::trace::SpanContext::from(sc2);
153        assert_eq!(sc, sc3);
154    }
155}