relay_base_schema/
spans.rs

1//! Defines data types relating to performance spans.
2
3use std::fmt;
4use std::str::FromStr;
5
6use relay_protocol::{Annotated, Empty, Error, FromValue, IntoValue, SkipSerialization, Value};
7use serde::Serialize;
8
9/// Trace status.
10///
11/// Values from <https://github.com/open-telemetry/opentelemetry-specification/blob/8fb6c14e4709e75a9aaa64b0dbbdf02a6067682a/specification/api-tracing.md#status>
12/// Mapping to HTTP from <https://github.com/open-telemetry/opentelemetry-specification/blob/8fb6c14e4709e75a9aaa64b0dbbdf02a6067682a/specification/data-http.md#status>
13//
14// Note: This type is represented as a u8 in Snuba/Clickhouse, with Unknown being the default
15// value. We use repr(u8) to statically validate that the trace status has 255 variants at most.
16#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
17#[serde(rename_all = "snake_case")]
18#[repr(u8)] // size limit in clickhouse
19pub enum SpanStatus {
20    /// The operation completed successfully.
21    ///
22    /// HTTP status 100..299 + successful redirects from the 3xx range.
23    Ok = 0,
24
25    /// The operation was cancelled (typically by the user).
26    Cancelled = 1,
27
28    /// Unknown. Any non-standard HTTP status code.
29    ///
30    /// "We do not know whether the transaction failed or succeeded"
31    Unknown = 2,
32
33    /// Client specified an invalid argument. 4xx.
34    ///
35    /// Note that this differs from FailedPrecondition. InvalidArgument indicates arguments that
36    /// are problematic regardless of the state of the system.
37    InvalidArgument = 3,
38
39    /// Deadline expired before operation could complete.
40    ///
41    /// For operations that change the state of the system, this error may be returned even if the
42    /// operation has been completed successfully.
43    ///
44    /// HTTP redirect loops and 504 Gateway Timeout
45    DeadlineExceeded = 4,
46
47    /// 404 Not Found. Some requested entity (file or directory) was not found.
48    NotFound = 5,
49
50    /// Already exists (409)
51    ///
52    /// Some entity that we attempted to create already exists.
53    AlreadyExists = 6,
54
55    /// 403 Forbidden
56    ///
57    /// The caller does not have permission to execute the specified operation.
58    PermissionDenied = 7,
59
60    /// 429 Too Many Requests
61    ///
62    /// Some resource has been exhausted, perhaps a per-user quota or perhaps the entire file
63    /// system is out of space.
64    ResourceExhausted = 8,
65
66    /// Operation was rejected because the system is not in a state required for the operation's
67    /// execution
68    FailedPrecondition = 9,
69
70    /// The operation was aborted, typically due to a concurrency issue.
71    Aborted = 10,
72
73    /// Operation was attempted past the valid range.
74    OutOfRange = 11,
75
76    /// 501 Not Implemented
77    ///
78    /// Operation is not implemented or not enabled.
79    Unimplemented = 12,
80
81    /// Other/generic 5xx.
82    InternalError = 13,
83
84    /// 503 Service Unavailable
85    Unavailable = 14,
86
87    /// Unrecoverable data loss or corruption
88    DataLoss = 15,
89
90    /// 401 Unauthorized (actually does mean unauthenticated according to RFC 7235)
91    ///
92    /// Prefer PermissionDenied if a user is logged in.
93    Unauthenticated = 16,
94}
95
96impl SpanStatus {
97    /// Returns the string representation of the status.
98    pub fn as_str(&self) -> &'static str {
99        match *self {
100            SpanStatus::Ok => "ok",
101            SpanStatus::DeadlineExceeded => "deadline_exceeded",
102            SpanStatus::Unauthenticated => "unauthenticated",
103            SpanStatus::PermissionDenied => "permission_denied",
104            SpanStatus::NotFound => "not_found",
105            SpanStatus::ResourceExhausted => "resource_exhausted",
106            SpanStatus::InvalidArgument => "invalid_argument",
107            SpanStatus::Unimplemented => "unimplemented",
108            SpanStatus::Unavailable => "unavailable",
109            SpanStatus::InternalError => "internal_error",
110            SpanStatus::Unknown => "unknown",
111            SpanStatus::Cancelled => "cancelled",
112            SpanStatus::AlreadyExists => "already_exists",
113            SpanStatus::FailedPrecondition => "failed_precondition",
114            SpanStatus::Aborted => "aborted",
115            SpanStatus::OutOfRange => "out_of_range",
116            SpanStatus::DataLoss => "data_loss",
117        }
118    }
119}
120
121impl AsRef<str> for SpanStatus {
122    fn as_ref(&self) -> &str {
123        self.as_str()
124    }
125}
126
127/// Error parsing a `SpanStatus`.
128#[derive(Clone, Copy, Debug)]
129pub struct ParseSpanStatusError;
130
131impl fmt::Display for ParseSpanStatusError {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        write!(f, "invalid span status")
134    }
135}
136
137impl std::error::Error for ParseSpanStatusError {}
138
139impl FromStr for SpanStatus {
140    type Err = ParseSpanStatusError;
141
142    fn from_str(string: &str) -> Result<SpanStatus, Self::Err> {
143        Ok(match string {
144            "ok" => SpanStatus::Ok,
145            "success" => SpanStatus::Ok, // Backwards compat with initial schema
146            "deadline_exceeded" => SpanStatus::DeadlineExceeded,
147            "unauthenticated" => SpanStatus::Unauthenticated,
148            "permission_denied" => SpanStatus::PermissionDenied,
149            "not_found" => SpanStatus::NotFound,
150            "resource_exhausted" => SpanStatus::ResourceExhausted,
151            "invalid_argument" => SpanStatus::InvalidArgument,
152            "unimplemented" => SpanStatus::Unimplemented,
153            "unavailable" => SpanStatus::Unavailable,
154            "internal_error" => SpanStatus::InternalError,
155            "failure" => SpanStatus::InternalError, // Backwards compat with initial schema
156            "unknown" | "unknown_error" => SpanStatus::Unknown,
157            "cancelled" => SpanStatus::Cancelled,
158            "already_exists" => SpanStatus::AlreadyExists,
159            "failed_precondition" => SpanStatus::FailedPrecondition,
160            "aborted" => SpanStatus::Aborted,
161            "out_of_range" => SpanStatus::OutOfRange,
162            "data_loss" => SpanStatus::DataLoss,
163            _ => return Err(ParseSpanStatusError),
164        })
165    }
166}
167
168impl fmt::Display for SpanStatus {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        f.write_str(self.as_str())
171    }
172}
173
174impl Empty for SpanStatus {
175    #[inline]
176    fn is_empty(&self) -> bool {
177        false
178    }
179}
180
181impl FromValue for SpanStatus {
182    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
183        match value {
184            Annotated(Some(Value::String(value)), mut meta) => match value.parse() {
185                Ok(status) => Annotated(Some(status), meta),
186                Err(_) => {
187                    meta.add_error(Error::expected("a trace status"));
188                    meta.set_original_value(Some(value));
189                    Annotated(None, meta)
190                }
191            },
192            Annotated(Some(Value::I64(value)), mut meta) => Annotated(
193                Some(match value {
194                    0 => SpanStatus::Ok,
195                    1 => SpanStatus::Cancelled,
196                    2 => SpanStatus::Unknown,
197                    3 => SpanStatus::InvalidArgument,
198                    4 => SpanStatus::DeadlineExceeded,
199                    5 => SpanStatus::NotFound,
200                    6 => SpanStatus::AlreadyExists,
201                    7 => SpanStatus::PermissionDenied,
202                    8 => SpanStatus::ResourceExhausted,
203                    9 => SpanStatus::FailedPrecondition,
204                    10 => SpanStatus::Aborted,
205                    11 => SpanStatus::OutOfRange,
206                    12 => SpanStatus::Unimplemented,
207                    13 => SpanStatus::InternalError,
208                    14 => SpanStatus::Unavailable,
209                    15 => SpanStatus::DataLoss,
210                    16 => SpanStatus::Unauthenticated,
211                    _ => {
212                        meta.add_error(Error::expected("a trace status"));
213                        meta.set_original_value(Some(value));
214                        return Annotated(None, meta);
215                    }
216                }),
217                meta,
218            ),
219            Annotated(None, meta) => Annotated(None, meta),
220            Annotated(Some(value), mut meta) => {
221                meta.add_error(Error::expected("a string"));
222                meta.set_original_value(Some(value));
223                Annotated(None, meta)
224            }
225        }
226    }
227}
228
229impl IntoValue for SpanStatus {
230    fn into_value(self) -> Value {
231        Value::String(self.to_string())
232    }
233
234    fn serialize_payload<S>(&self, s: S, _behavior: SkipSerialization) -> Result<S::Ok, S::Error>
235    where
236        Self: Sized,
237        S: serde::Serializer,
238    {
239        Serialize::serialize(self.as_str(), s)
240    }
241}