1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! # OpenTelemetry Tracing API.
//!
//! The tracing API consist of a few main traits:
//!
//! * The `Tracer` trait which describes all tracing operations.
//! * The `Span` trait with is a mutable object storing information about the
//! current operation execution.
//! * The `SpanContext` struct is the portion of a `Span` which must be
//! serialized and propagated along side of a distributed context
//!
//! ## Tracer
//!
//! The OpenTelemetry library achieves in-process context propagation of `Span`s by
//! way of the `Tracer`.
//!
//! The `Tracer` is responsible for tracking the currently active `Span`, and
//! exposes methods for creating and activating new `Span`s. The `Tracer` is
//! configured with `Propagator`s which support transferring span context across
//! process boundaries.
//!
//! `Tracer`s are generally expected to be used as singletons. Implementations
//! SHOULD provide a single global default `Tracer`.
//!
//! Some applications may require multiple `Tracer` instances, e.g. to create
//! `Span`s on behalf of other applications. Implementations MAY provide a global
//! registry of `Tracer`s for such applications.
//!
//! ## Span
//!
//! A `Span` represents a single operation within a trace. Spans can be nested to
//! form a trace tree. Each trace contains a root span, which typically describes
//! the end-to-end latency and, optionally, one or more sub-spans for its
//! sub-operations.
//!
//! `Span`s encapsulate:
//!
//! - The span name
//! - An immutable `SpanContext` that uniquely identifies the `Span`
//! - A parent span in the form of a `SpanContext`, or None
//! - A start timestamp
//! - An end timestamp
//! - An ordered mapping of `Attribute`s
//! - A list of `Link`s to other `Span`s
//! - A list of timestamped `Event`s
//! - A `Status`.
//!
//! The _span name_ is a human-readable string which concisely identifies the work
//! represented by the `Span`, for example, an RPC method name, a function name,
//! or the name of a subtask or stage within a larger computation. The span name
//! should be the most general string that identifies a (statistically) interesting
//! _class of Spans_, rather than individual Span instances. That is, "get_user" is
//! a reasonable name, while "get_user/314159", where "314159" is a user ID, is not
//! a good name due to its high cardinality.
//!
//! For example, here are potential span names for an endpoint that gets a
//! hypothetical account information:
//!
//! | Span Name                 | Guidance     |
//! | ------------------------- | ------------ |
//! | `get`                     | Too general  |
//! | `get_account/42`          | Too specific |
//! | `get_account`             | Good, and account_id=42 would make a nice Span attribute |
//! | `get_account/{accountId}` | Also good (using the "HTTP route") |
//!
//! The `Span`'s start and end timestamps reflect the elapsed real time of the
//! operation. A `Span`'s start time SHOULD be set to the current time on span
//! creation. After the `Span` is created, it SHOULD be possible to
//! change the its name, set its `Attribute`s, and add `Link`s and `Event`s. These
//! MUST NOT be changed after the `Span`'s end time has been set.
//!
//! `Span`s are not meant to be used to propagate information within a process. To
//! prevent misuse, implementations SHOULD NOT provide access to a `Span`'s
//! attributes besides its `SpanContext`.
//!
//! Vendors may implement the `Span` interface to effect vendor-specific logic.
//! However, alternative implementations MUST NOT allow callers to create `Span`s
//! directly. All `Span`s MUST be created via a `Tracer`.
//!
//! ## SpanContext
//!
//! A `SpanContext` represents the portion of a `Span` which must be serialized and
//! propagated along side of a distributed context. `SpanContext`s are immutable.
//! `SpanContext`.
//!
//! The OpenTelemetry `SpanContext` representation conforms to the [w3c TraceContext
//! specification](https://www.w3.org/TR/trace-context/). It contains two
//! identifiers - a `TraceId` and a `SpanId` - along with a set of common
//! `TraceFlags` and system-specific `TraceState` values.
//!
//! `TraceId` A valid trace identifier is a non-zero `u128`
//!
//! `SpanId` A valid span identifier is a non-zero `u64` byte.
//!
//! `TraceFlags` contain details about the trace. Unlike Tracestate values,
//! TraceFlags are present in all traces. Currently, the only `TraceFlags` is a
//! boolean `sampled`
//! [flag](https://www.w3.org/TR/trace-context/#trace-flags).
//!
//! `Tracestate` carries system-specific configuration data, represented as a list
//! of key-value pairs. TraceState allows multiple tracing systems to participate in
//! the same trace.
//!
//! `IsValid` is a boolean flag which returns true if the SpanContext has a non-zero
//! TraceID and a non-zero SpanID.
//!
//! `IsRemote` is a boolean flag which returns true if the SpanContext was propagated
//! from a remote parent. When creating children from remote spans, their IsRemote
//! flag MUST be set to false.
//!
//! Please review the W3C specification for details on the [Tracestate
//! field](https://www.w3.org/TR/trace-context/#tracestate-field).
//!
use ::futures::channel::{mpsc::TrySendError, oneshot::Canceled};
use thiserror::Error;

mod context;
mod event;
mod futures;
mod id_generator;
mod link;
mod noop;
mod provider;
mod span;
mod span_context;
mod tracer;

pub use self::{
    context::{get_active_span, mark_span_as_active, TraceContextExt},
    event::Event,
    futures::FutureExt,
    id_generator::IdGenerator,
    link::Link,
    noop::{NoopSpan, NoopSpanExporter, NoopTracer, NoopTracerProvider},
    provider::TracerProvider,
    span::{Span, SpanKind, StatusCode},
    span_context::{
        SpanContext, SpanId, TraceId, TraceState, TRACE_FLAG_DEBUG, TRACE_FLAG_DEFERRED,
        TRACE_FLAG_NOT_SAMPLED, TRACE_FLAG_SAMPLED,
    },
    tracer::{SpanBuilder, Tracer},
};
use crate::sdk::export::ExportError;
use std::time;

/// Describe the result of operations in tracing API.
pub type TraceResult<T> = Result<T, TraceError>;

/// Errors returned by the trace API.
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum TraceError {
    /// Export failed with the error returned by the exporter
    #[error("Exporter {} failed with {0}", .0.exporter_name())]
    ExportFailed(Box<dyn ExportError>),

    /// Export failed to finish after certain period and processor stopped the export.
    #[error("Exporting timed out after {} seconds", .0.as_secs())]
    ExportTimedOut(time::Duration),

    /// Other errors propagated from trace SDK that weren't covered above
    #[error(transparent)]
    Other(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
}

impl<T> From<T> for TraceError
where
    T: ExportError,
{
    fn from(err: T) -> Self {
        TraceError::ExportFailed(Box::new(err))
    }
}

impl<T> From<TrySendError<T>> for TraceError {
    fn from(err: TrySendError<T>) -> Self {
        TraceError::Other(Box::new(err.into_send_error()))
    }
}

impl From<Canceled> for TraceError {
    fn from(err: Canceled) -> Self {
        TraceError::Other(Box::new(err))
    }
}

impl From<String> for TraceError {
    fn from(err_msg: String) -> Self {
        TraceError::Other(Box::new(Custom(err_msg)))
    }
}

impl From<&'static str> for TraceError {
    fn from(err_msg: &'static str) -> Self {
        TraceError::Other(Box::new(Custom(err_msg.into())))
    }
}

/// Wrap type for string
#[derive(Error, Debug)]
#[error("{0}")]
struct Custom(String);