onesignal_tracing_tail_sample/opentelemetry/
span_ext.rs

1// Copied from https://github.com/tokio-rs/tracing/blob/master/tracing-opentelemetry/src/span_ext.rs
2//
3// Copyright (c) 2019 Tokio Contributors
4//
5// Permission is hereby granted, free of charge, to any
6// person obtaining a copy of this software and associated
7// documentation files (the "Software"), to deal in the
8// Software without restriction, including without
9// limitation the rights to use, copy, modify, merge,
10// publish, distribute, sublicense, and/or sell copies of
11// the Software, and to permit persons to whom the Software
12// is furnished to do so, subject to the following
13// conditions:
14//
15// The above copyright notice and this permission notice
16// shall be included in all copies or substantial portions
17// of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
20// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
21// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
22// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
23// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
26// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27// DEALINGS IN THE SOFTWARE.
28use crate::opentelemetry::layer::WithContext;
29use opentelemetry::{trace::SpanContext, Context, KeyValue};
30
31/// Utility functions to allow tracing [`Span`]s to accept and return
32/// [OpenTelemetry] [`Context`]s.
33///
34/// [`Span`]: https://docs.rs/tracing/latest/tracing/struct.Span.html
35/// [OpenTelemetry]: https://opentelemetry.io
36/// [`Context`]: opentelemetry::Context
37pub trait OpenTelemetrySpanExt {
38    /// Associates `self` with a given OpenTelemetry trace, using the provided
39    /// parent [`Context`].
40    ///
41    /// [`Context`]: opentelemetry::Context
42    ///
43    /// # Examples
44    ///
45    /// ```rust
46    /// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt};
47    /// use opentelemetry::sdk::propagation::TraceContextPropagator;
48    /// use onesignal_tracing_tail_sample::opentelemetry::OpenTelemetrySpanExt;
49    /// use std::collections::HashMap;
50    /// use tracing::Span;
51    ///
52    /// // Example carrier, could be a framework header map that impls otel's `Extract`.
53    /// let mut carrier = HashMap::new();
54    ///
55    /// // Propagator can be swapped with b3 propagator, jaeger propagator, etc.
56    /// let propagator = TraceContextPropagator::new();
57    ///
58    /// // Extract otel parent context via the chosen propagator
59    /// let parent_context = propagator.extract(&carrier);
60    ///
61    /// // Generate a tracing span as usual
62    /// let app_root = tracing::span!(tracing::Level::INFO, "app_start");
63    ///
64    /// // Assign parent trace from external context
65    /// app_root.set_parent(parent_context.clone());
66    ///
67    /// // Or if the current span has been created elsewhere:
68    /// Span::current().set_parent(parent_context);
69    /// ```
70    fn set_parent(&self, cx: Context);
71
72    /// Associates `self` with a given OpenTelemetry trace, using the provided
73    /// followed span [`SpanContext`].
74    ///
75    /// [`SpanContext`]: opentelemetry::trace::SpanContext
76    ///
77    /// # Examples
78    ///
79    /// ```rust
80    /// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt};
81    /// use opentelemetry::sdk::propagation::TraceContextPropagator;
82    /// use onesignal_tracing_tail_sample::opentelemetry::OpenTelemetrySpanExt;
83    /// use std::collections::HashMap;
84    /// use tracing::Span;
85    ///
86    /// // Example carrier, could be a framework header map that impls otel's `Extract`.
87    /// let mut carrier = HashMap::new();
88    ///
89    /// // Propagator can be swapped with b3 propagator, jaeger propagator, etc.
90    /// let propagator = TraceContextPropagator::new();
91    ///
92    /// // Extract otel context of linked span via the chosen propagator
93    /// let linked_span_otel_context = propagator.extract(&carrier);
94    ///
95    /// // Extract the linked span context from the otel context
96    /// let linked_span_context = linked_span_otel_context.span().span_context().clone();
97    ///
98    /// // Generate a tracing span as usual
99    /// let app_root = tracing::span!(tracing::Level::INFO, "app_start");
100    ///
101    /// // Assign linked trace from external context
102    /// app_root.add_link(linked_span_context);
103    ///
104    /// // Or if the current span has been created elsewhere:
105    /// let linked_span_context = linked_span_otel_context.span().span_context().clone();
106    /// Span::current().add_link(linked_span_context);
107    /// ```
108    fn add_link(&self, cx: SpanContext);
109
110    /// Associates `self` with a given OpenTelemetry trace, using the provided
111    /// followed span [`SpanContext`] and attributes.
112    ///
113    /// [`SpanContext`]: opentelemetry::trace::SpanContext
114    fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec<KeyValue>);
115
116    /// Extracts an OpenTelemetry [`Context`] from `self`.
117    ///
118    /// [`Context`]: opentelemetry::Context
119    ///
120    /// # Examples
121    ///
122    /// ```rust
123    /// use opentelemetry::Context;
124    /// use onesignal_tracing_tail_sample::opentelemetry::OpenTelemetrySpanExt;
125    /// use tracing::Span;
126    ///
127    /// fn make_request(cx: Context) {
128    ///     // perform external request after injecting context
129    ///     // e.g. if the request's headers impl `opentelemetry::propagation::Injector`
130    ///     // then `propagator.inject_context(cx, request.headers_mut())`
131    /// }
132    ///
133    /// // Generate a tracing span as usual
134    /// let app_root = tracing::span!(tracing::Level::INFO, "app_start");
135    ///
136    /// // To include tracing context in client requests from _this_ app,
137    /// // extract the current OpenTelemetry context.
138    /// make_request(app_root.context());
139    ///
140    /// // Or if the current span has been created elsewhere:
141    /// make_request(Span::current().context())
142    /// ```
143    fn context(&self) -> Context;
144}
145
146impl OpenTelemetrySpanExt for tracing::Span {
147    fn set_parent(&self, cx: Context) {
148        let mut cx = Some(cx);
149        self.with_subscriber(move |(id, subscriber)| {
150            if let Some(get_context) = subscriber.downcast_ref::<WithContext>() {
151                get_context.with_context(subscriber, id, move |data, _tracer| {
152                    if let Some(cx) = cx.take() {
153                        data.parent_cx = cx;
154                    }
155                });
156            }
157        });
158    }
159
160    fn add_link(&self, cx: SpanContext) {
161        self.add_link_with_attributes(cx, Vec::new())
162    }
163
164    fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec<KeyValue>) {
165        if cx.is_valid() {
166            let mut cx = Some(cx);
167            let mut att = Some(attributes);
168            self.with_subscriber(move |(id, subscriber)| {
169                if let Some(get_context) = subscriber.downcast_ref::<WithContext>() {
170                    get_context.with_context(subscriber, id, move |data, _tracer| {
171                        if let Some(cx) = cx.take() {
172                            let attr = att.take().unwrap_or_default();
173                            let follows_link = opentelemetry::trace::Link::new(cx, attr);
174                            data.builder
175                                .links
176                                .get_or_insert_with(|| Vec::with_capacity(1))
177                                .push(follows_link);
178                        }
179                    });
180                }
181            });
182        }
183    }
184
185    fn context(&self) -> Context {
186        let mut cx = None;
187        self.with_subscriber(|(id, subscriber)| {
188            if let Some(get_context) = subscriber.downcast_ref::<WithContext>() {
189                get_context.with_context(subscriber, id, |builder, tracer| {
190                    cx = Some(tracer.sampled_context(builder));
191                })
192            }
193        });
194
195        cx.unwrap_or_default()
196    }
197}