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}