ts_opentelemetry_api/trace/
span.rs

1use crate::{trace::SpanContext, KeyValue};
2use std::borrow::Cow;
3use std::error::Error;
4use std::time::SystemTime;
5
6/// The interface for a single operation within a trace.
7///
8/// Spans can be nested to form a trace tree. Each trace contains a root span,
9/// which typically describes the entire operation and, optionally, one or more
10/// sub-spans for its sub-operations.
11///
12/// The span `name` concisely identifies the work represented by the span, for
13/// example, an RPC method name, a function name, or the name of a subtask or
14/// stage within a larger computation. The span name should be the most general
15/// string that identifies a (statistically) interesting class of spans, rather
16/// than individual span instances while still being human-readable. That is,
17/// `"get_user"` is a reasonable name, while `"get_user/314159"`, where `"314159"` is
18/// a user ID, is not a good name due to its high cardinality. _Generality_
19/// should be prioritized over _human-readability_.
20///
21/// For example, here are potential span names for an endpoint that gets a
22/// hypothetical account information:
23///
24/// | Span Name         | Guidance     |
25/// | ----------------- | ------------ |
26/// | `get`             | Too general  |
27/// | `get_account/42`  | Too specific |
28/// | `get_account`     | Good, and account_id=42 would make a nice Span attribute |
29/// | `get_account/{accountId}` | Also good (using the "HTTP route") |
30///
31/// The span's start and end timestamps reflect the elapsed real time of the
32/// operation.
33///
34/// For example, if a span represents a request-response cycle (e.g. HTTP or an
35/// RPC), the span should have a start time that corresponds to the start time
36/// of the first sub-operation, and an end time of when the final sub-operation
37/// is complete. This includes:
38///
39/// * receiving the data from the request
40/// * parsing of the data (e.g. from a binary or json format)
41/// * any middleware or additional processing logic
42/// * business logic
43/// * construction of the response
44/// * sending of the response
45///
46/// Child spans (or in some cases events) may be created to represent
47/// sub-operations which require more detailed observability. Child spans should
48/// measure the timing of the respective sub-operation, and may add additional
49/// attributes.
50pub trait Span {
51    /// Record an event in the context this span.
52    ///
53    /// Note that the OpenTelemetry project documents certain "[standard
54    /// attributes]" that have prescribed semantic meanings and are available via
55    /// the [opentelemetry_semantic_conventions] crate.
56    ///
57    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
58    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
59    fn add_event<T>(&mut self, name: T, attributes: Vec<KeyValue>)
60    where
61        T: Into<Cow<'static, str>>,
62    {
63        self.add_event_with_timestamp(name, crate::time::now(), attributes)
64    }
65
66    /// Record an error as an event for this span.
67    ///
68    /// An additional call to [Span::set_status] is required if the status of the
69    /// span should be set to error, as this method does not change the span status.
70    ///
71    /// If this span is not being recorded then this method does nothing.
72    fn record_error(&mut self, err: &dyn Error) {
73        if self.is_recording() {
74            let attributes = vec![KeyValue::new("exception.message", err.to_string())];
75            self.add_event("exception", attributes);
76        }
77    }
78
79    /// Record an event with a timestamp in the context this span.
80    ///
81    /// Note that the OpenTelemetry project documents certain "[standard
82    /// attributes]" that have prescribed semantic meanings and are available via
83    /// the [opentelemetry_semantic_conventions] crate.
84    ///
85    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
86    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
87    fn add_event_with_timestamp<T>(
88        &mut self,
89        name: T,
90        timestamp: SystemTime,
91        attributes: Vec<KeyValue>,
92    ) where
93        T: Into<Cow<'static, str>>;
94
95    /// A reference to the [`SpanContext`] for this span.
96    fn span_context(&self) -> &SpanContext;
97
98    /// Returns `true` if this span is recording information.
99    ///
100    /// Spans will not be recording information after they have ended.
101    ///
102    /// This flag may be `true` despite the entire trace being sampled out. This
103    /// allows recording and processing of information about the individual
104    /// spans without sending it to the backend. An example of this scenario may
105    /// be recording and processing of all incoming requests for the processing
106    /// and building of SLA/SLO latency charts while sending only a subset -
107    /// sampled spans - to the backend.
108    fn is_recording(&self) -> bool;
109
110    /// Set an attribute of this span.
111    ///
112    /// Setting an attribute with the same key as an existing attribute
113    /// generally overwrites the existing attribute's value.
114    ///
115    /// Note that the OpenTelemetry project documents certain "[standard
116    /// attributes]" that have prescribed semantic meanings and are available via
117    /// the [opentelemetry_semantic_conventions] crate.
118    ///
119    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
120    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
121    fn set_attribute(&mut self, attribute: KeyValue);
122
123    /// Set multiple attributes of this span.
124    ///
125    /// Setting an attribute with the same key as an existing attribute
126    /// generally overwrites the existing attribute's value.
127    ///
128    /// Note that the OpenTelemetry project documents certain "[standard
129    /// attributes]" that have prescribed semantic meanings and are available via
130    /// the [opentelemetry_semantic_conventions] crate.
131    ///
132    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
133    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
134    fn set_attributes(&mut self, attributes: impl IntoIterator<Item = KeyValue>) {
135        if self.is_recording() {
136            for attr in attributes.into_iter() {
137                self.set_attribute(attr);
138            }
139        }
140    }
141
142    /// Sets the status of this `Span`.
143    ///
144    /// If used, this will override the default span status, which is [`Status::Unset`].
145    fn set_status(&mut self, status: Status);
146
147    /// Updates the span's name.
148    ///
149    /// After this update, any sampling behavior based on the name will depend on
150    /// the implementation.
151    fn update_name<T>(&mut self, new_name: T)
152    where
153        T: Into<Cow<'static, str>>;
154
155    /// Signals that the operation described by this span has now ended.
156    fn end(&mut self) {
157        self.end_with_timestamp(crate::time::now());
158    }
159
160    /// Signals that the operation described by this span ended at the given time.
161    fn end_with_timestamp(&mut self, timestamp: SystemTime);
162}
163
164/// `SpanKind` describes the relationship between the [`Span`], its parents, and
165/// its children in a trace.
166///
167/// `SpanKind` describes two independent properties that benefit tracing systems
168/// during analysis:
169///
170/// The first property described by `SpanKind` reflects whether the span is a
171/// "logical" remote child or parent. By "logical", we mean that the span is
172/// logically a remote child or parent, from the point of view of the library
173/// that is being instrumented. Spans with a remote parent are interesting
174/// because they are sources of external load. Spans with a remote child are
175/// interesting because they reflect a non-local system dependency.
176///
177/// The second property described by `SpanKind` reflects whether a child span
178/// represents a synchronous call.  When a child span is synchronous, the parent
179/// is expected to wait for it to complete under ordinary circumstances. It can
180/// be useful for tracing systems to know this property, since synchronous spans
181/// may contribute to the overall trace latency. Asynchronous scenarios can be
182/// remote or local.
183///
184/// In order for `SpanKind` to be meaningful, callers should arrange that a
185/// single span does not serve more than one purpose. For example, a server-side
186/// span should not be used directly as the parent of another remote span. As a
187/// simple guideline, instrumentation should create a new span prior to
188/// extracting and serializing the SpanContext for a remote call.
189///
190/// Note: there are complex scenarios where a `SpanKind::Client` span may have a
191/// child that is also logically a `SpanKind::Client` span, or a
192/// `SpanKind::Producer` span might have a local child that is a
193/// `SpanKind::Client` span, depending on how the various libraries that are
194/// providing the functionality are built and instrumented. These scenarios,
195/// when they occur, should be detailed in the semantic conventions appropriate
196/// to the relevant libraries.
197///
198/// To summarize the interpretation of these kinds:
199///
200/// | `SpanKind` | Synchronous | Asynchronous | Remote Incoming | Remote Outgoing |
201/// |---|---|---|---|---|
202/// | `Client` | yes | | | yes |
203/// | `Server` | yes | | yes | |
204/// | `Producer` | | yes | | maybe |
205/// | `Consumer` | | yes | maybe | |
206/// | `Internal` | | | | |
207#[derive(Clone, Debug, PartialEq, Eq)]
208pub enum SpanKind {
209    /// Indicates that the span describes a request to some remote service. This
210    /// span is usually the parent of a remote `SpanKind::Server` span and does
211    /// not end until the response is received.
212    Client,
213
214    /// Indicates that the span covers server-side handling of a synchronous RPC
215    /// or other remote request. This span is often the child of a remote
216    /// `SpanKind::Client` span that was expected to wait for a response.
217    Server,
218
219    /// Indicates that the span describes the initiators of an asynchronous
220    /// request. This parent span will often end before the corresponding child
221    /// `SpanKind::Consumer` span, possibly even before the child span starts.
222    ///
223    /// In messaging scenarios with batching, tracing individual messages
224    /// requires a new `SpanKind::Producer` span per message to be created.
225    Producer,
226
227    /// Indicates that the span describes a child of an asynchronous
228    /// `SpanKind::Producer` request.
229    Consumer,
230
231    /// Default value.
232    ///
233    /// Indicates that the span represents an internal operation within an
234    /// application, as opposed to an operations with remote parents or
235    /// children.
236    Internal,
237}
238
239/// The status of a [`Span`].
240///
241/// These values form a total order: Ok > Error > Unset. This means that setting
242/// `Status::Ok` will override any prior or future attempts to set a status with
243/// `Status::Error` or `Status::Unset`.
244///
245/// The status should remain unset, except for the following circumstances:
246///
247/// Generally, instrumentation libraries should not set the code to
248/// `Status::Ok`, unless explicitly configured to do so. Instrumentation
249/// libraries should leave the status code as unset unless there is an error.
250///
251/// Application developers and operators may set the status code to
252/// `Status::Ok`.
253///
254/// When span status is set to `Status::Ok` it should be considered final and
255/// any further attempts to change it should be ignored.
256///
257/// Analysis tools should respond to a `Status::Ok` status by suppressing any
258/// errors they would otherwise generate. For example, to suppress noisy errors
259/// such as 404s.
260///
261/// Only the value of the last call will be recorded, and implementations are
262/// free to ignore previous calls.
263#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
264pub enum Status {
265    /// The default status.
266    Unset,
267
268    /// The operation contains an error.
269    Error {
270        /// The description of the error
271        description: Cow<'static, str>,
272    },
273
274    /// The operation has been validated by an application developer or operator to
275    /// have completed successfully.
276    Ok,
277}
278
279impl Status {
280    /// Create a new error status with a given description.
281    ///
282    /// # Examples
283    ///
284    /// ```
285    /// use ts_opentelemetry_api::trace::Status;
286    ///
287    /// // record error with `str` description
288    /// let error_status = Status::error("something went wrong");
289    ///
290    /// // or with `String` description
291    /// let error_status = Status::error(format!("too many foos: {}", 42));
292    /// # drop(error_status);
293    /// ```
294    pub fn error(description: impl Into<Cow<'static, str>>) -> Self {
295        Status::Error {
296            description: description.into(),
297        }
298    }
299}
300
301impl Default for Status {
302    fn default() -> Self {
303        Status::Unset
304    }
305}
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310
311    #[test]
312    fn status_order() {
313        assert!(Status::Ok > Status::error(""));
314        assert!(Status::error("") > Status::Unset);
315    }
316}