forked_tarpc/
trace.rs

1// Copyright 2018 Google LLC
2//
3// Use of this source code is governed by an MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT.
6
7#![deny(missing_docs, missing_debug_implementations)]
8
9//! Provides building blocks for tracing distributed programs.
10//!
11//! A trace is logically a tree of causally-related events called spans. Traces are tracked via a
12//! [context](Context) that identifies the current trace, span, and parent of the current span.  In
13//! distributed systems, a context can be sent from client to server to connect events occurring on
14//! either side.
15//!
16//! This crate's design is based on [opencensus
17//! tracing](https://opencensus.io/core-concepts/tracing/).
18
19#[cfg(feature = "opentelemetry")]
20use opentelemetry::trace::TraceContextExt;
21use rand::Rng;
22use std::{
23    convert::TryFrom,
24    fmt::{self, Formatter},
25    num::{NonZeroU128, NonZeroU64},
26};
27#[cfg(feature = "opentelemetry")]
28use tracing_opentelemetry::OpenTelemetrySpanExt;
29
30/// A context for tracing the execution of processes, distributed or otherwise.
31///
32/// Consists of a span identifying an event, an optional parent span identifying a causal event
33/// that triggered the current span, and a trace with which all related spans are associated.
34#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
35#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
36pub struct Context {
37    /// An identifier of the trace associated with the current context. A trace ID is typically
38    /// created at a root span and passed along through all causal events.
39    pub trace_id: TraceId,
40    /// An identifier of the current span. In typical RPC usage, a span is created by a client
41    /// before making an RPC, and the span ID is sent to the server. The server is free to create
42    /// its own spans, for which it sets the client's span as the parent span.
43    pub span_id: SpanId,
44    /// Indicates whether a sampler has already decided whether or not to sample the trace
45    /// associated with the Context. If `sampling_decision` is None, then a decision has not yet
46    /// been made. Downstream samplers do not need to abide by "no sample" decisions--for example,
47    /// an upstream client may choose to never sample, which may not make sense for the client's
48    /// dependencies. On the other hand, if an upstream process has chosen to sample this trace,
49    /// then the downstream samplers are expected to respect that decision and also sample the
50    /// trace. Otherwise, the full trace would not be able to be reconstructed.
51    pub sampling_decision: SamplingDecision,
52}
53
54/// A 128-bit UUID identifying a trace. All spans caused by the same originating span share the
55/// same trace ID.
56#[derive(Default, PartialEq, Eq, Hash, Clone, Copy)]
57#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
58pub struct TraceId(#[cfg_attr(feature = "serde1", serde(with = "u128_serde"))] u128);
59
60/// A 64-bit identifier of a span within a trace. The identifier is unique within the span's trace.
61#[derive(Default, PartialEq, Eq, Hash, Clone, Copy)]
62#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
63pub struct SpanId(u64);
64
65/// Indicates whether a sampler has decided whether or not to sample the trace associated with the
66/// Context. Downstream samplers do not need to abide by "no sample" decisions--for example, an
67/// upstream client may choose to never sample, which may not make sense for the client's
68/// dependencies. On the other hand, if an upstream process has chosen to sample this trace, then
69/// the downstream samplers are expected to respect that decision and also sample the trace.
70/// Otherwise, the full trace would not be able to be reconstructed reliably.
71#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
72#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
73#[repr(u8)]
74pub enum SamplingDecision {
75    /// The associated span was sampled by its creating process. Child spans must also be sampled.
76    Sampled,
77    /// The associated span was not sampled by its creating process.
78    Unsampled,
79}
80
81impl Context {
82    /// Constructs a new context with the trace ID and sampling decision inherited from the parent.
83    pub(crate) fn new_child(&self) -> Self {
84        Self {
85            trace_id: self.trace_id,
86            span_id: SpanId::random(&mut rand::thread_rng()),
87            sampling_decision: self.sampling_decision,
88        }
89    }
90}
91
92impl TraceId {
93    /// Returns a random trace ID that can be assumed to be globally unique if `rng` generates
94    /// actually-random numbers.
95    pub fn random<R: Rng>(rng: &mut R) -> Self {
96        TraceId(rng.gen::<NonZeroU128>().get())
97    }
98
99    /// Returns true iff the trace ID is 0.
100    pub fn is_none(&self) -> bool {
101        self.0 == 0
102    }
103}
104
105impl SpanId {
106    /// Returns a random span ID that can be assumed to be unique within a single trace.
107    pub fn random<R: Rng>(rng: &mut R) -> Self {
108        SpanId(rng.gen::<NonZeroU64>().get())
109    }
110
111    /// Returns true iff the span ID is 0.
112    pub fn is_none(&self) -> bool {
113        self.0 == 0
114    }
115}
116
117impl From<TraceId> for u128 {
118    fn from(trace_id: TraceId) -> Self {
119        trace_id.0
120    }
121}
122
123impl From<u128> for TraceId {
124    fn from(trace_id: u128) -> Self {
125        Self(trace_id)
126    }
127}
128
129impl From<SpanId> for u64 {
130    fn from(span_id: SpanId) -> Self {
131        span_id.0
132    }
133}
134
135impl From<u64> for SpanId {
136    fn from(span_id: u64) -> Self {
137        Self(span_id)
138    }
139}
140
141#[cfg(feature = "opentelemetry")]
142impl From<opentelemetry::trace::TraceId> for TraceId {
143    fn from(trace_id: opentelemetry::trace::TraceId) -> Self {
144        Self::from(u128::from_be_bytes(trace_id.to_bytes()))
145    }
146}
147#[cfg(feature = "opentelemetry")]
148impl From<TraceId> for opentelemetry::trace::TraceId {
149    fn from(trace_id: TraceId) -> Self {
150        Self::from_bytes(u128::from(trace_id).to_be_bytes())
151    }
152}
153#[cfg(feature = "opentelemetry")]
154impl From<opentelemetry::trace::SpanId> for SpanId {
155    fn from(span_id: opentelemetry::trace::SpanId) -> Self {
156        Self::from(u64::from_be_bytes(span_id.to_bytes()))
157    }
158}
159#[cfg(feature = "opentelemetry")]
160impl From<SpanId> for opentelemetry::trace::SpanId {
161    fn from(span_id: SpanId) -> Self {
162        Self::from_bytes(u64::from(span_id).to_be_bytes())
163    }
164}
165
166impl TryFrom<&tracing::Span> for Context {
167    type Error = NoActiveSpan;
168    #[cfg(feature = "opentelemetry")]
169    fn try_from(span: &tracing::Span) -> Result<Self, NoActiveSpan> {
170        let context = span.context();
171        if context.has_active_span() {
172            Ok(Self::from(context.span()))
173        } else {
174            Err(NoActiveSpan)
175        }
176    }
177    #[cfg(not(feature = "opentelemetry"))]
178    fn try_from(_span: &tracing::Span) -> Result<Self, NoActiveSpan> {
179        Err(NoActiveSpan)
180    }
181}
182
183#[cfg(feature = "opentelemetry")]
184impl From<opentelemetry::trace::SpanRef<'_>> for Context {
185    fn from(span: opentelemetry::trace::SpanRef<'_>) -> Self {
186        let otel_ctx = span.span_context();
187        Self {
188            trace_id: TraceId::from(otel_ctx.trace_id()),
189            span_id: SpanId::from(otel_ctx.span_id()),
190            sampling_decision: SamplingDecision::from(otel_ctx),
191        }
192    }
193}
194#[cfg(feature = "opentelemetry")]
195impl From<SamplingDecision> for opentelemetry::trace::TraceFlags {
196    fn from(decision: SamplingDecision) -> Self {
197        match decision {
198            SamplingDecision::Sampled => opentelemetry::trace::TraceFlags::SAMPLED,
199            SamplingDecision::Unsampled => opentelemetry::trace::TraceFlags::default(),
200        }
201    }
202}
203#[cfg(feature = "opentelemetry")]
204impl From<&opentelemetry::trace::SpanContext> for SamplingDecision {
205    fn from(context: &opentelemetry::trace::SpanContext) -> Self {
206        if context.is_sampled() {
207            SamplingDecision::Sampled
208        } else {
209            SamplingDecision::Unsampled
210        }
211    }
212}
213
214impl Default for SamplingDecision {
215    fn default() -> Self {
216        Self::Unsampled
217    }
218}
219
220/// Returned when a [`Context`] cannot be constructed from a [`Span`](tracing::Span).
221#[derive(Debug)]
222pub struct NoActiveSpan;
223
224impl fmt::Display for TraceId {
225    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
226        write!(f, "{:02x}", self.0)?;
227        Ok(())
228    }
229}
230
231impl fmt::Debug for TraceId {
232    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
233        write!(f, "{:02x}", self.0)?;
234        Ok(())
235    }
236}
237
238impl fmt::Display for SpanId {
239    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
240        write!(f, "{:02x}", self.0)?;
241        Ok(())
242    }
243}
244
245impl fmt::Debug for SpanId {
246    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
247        write!(f, "{:02x}", self.0)?;
248        Ok(())
249    }
250}
251
252#[cfg(feature = "serde1")]
253mod u128_serde {
254    pub fn serialize<S>(u: &u128, serializer: S) -> Result<S::Ok, S::Error>
255    where
256        S: serde::Serializer,
257    {
258        serde::Serialize::serialize(&u.to_le_bytes(), serializer)
259    }
260
261    pub fn deserialize<'de, D>(deserializer: D) -> Result<u128, D::Error>
262    where
263        D: serde::Deserializer<'de>,
264    {
265        Ok(u128::from_le_bytes(serde::Deserialize::deserialize(
266            deserializer,
267        )?))
268    }
269}