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}