relay_base_schema/data_category.rs
1//! Defines the [`DataCategory`] type that classifies data Relay can handle.
2
3use std::fmt;
4use std::str::FromStr;
5
6use serde::{Deserialize, Serialize};
7
8use crate::events::EventType;
9
10/// Classifies the type of data that is being ingested.
11#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
12#[serde(rename_all = "snake_case")]
13#[repr(i8)]
14pub enum DataCategory {
15 /// Reserved and unused.
16 Default = 0,
17 /// Error events and Events with an `event_type` not explicitly listed below.
18 Error = 1,
19 /// Transaction events.
20 Transaction = 2,
21 /// Events with an event type of `csp`, `hpkp`, `expectct` and `expectstaple`.
22 Security = 3,
23 /// An attachment. Quantity is the size of the attachment in bytes.
24 Attachment = 4,
25 /// Session updates. Quantity is the number of updates in the batch.
26 Session = 5,
27 /// Profile
28 ///
29 /// This is the category for processed profiles (all profiles, whether or not we store them).
30 Profile = 6,
31 /// Session Replays
32 Replay = 7,
33 /// DEPRECATED: A transaction for which metrics were extracted.
34 ///
35 /// This category is now obsolete because the `Transaction` variant will represent
36 /// processed transactions from now on.
37 TransactionProcessed = 8,
38 /// Indexed transaction events.
39 ///
40 /// This is the category for transaction payloads that were accepted and stored in full. In
41 /// contrast, `transaction` only guarantees that metrics have been accepted for the transaction.
42 TransactionIndexed = 9,
43 /// Monitor check-ins.
44 Monitor = 10,
45 /// Indexed Profile
46 ///
47 /// This is the category for indexed profiles that will be stored later.
48 ProfileIndexed = 11,
49 /// Span
50 ///
51 /// This is the category for spans from which we extracted metrics from.
52 Span = 12,
53 /// Monitor Seat
54 ///
55 /// Represents a monitor job that has scheduled monitor checkins. The seats are not ingested
56 /// but we define it here to prevent clashing values since this data category enumeration
57 /// is also used outside of Relay via the Python package.
58 MonitorSeat = 13,
59 /// User Feedback
60 ///
61 /// Represents a User Feedback processed.
62 /// Currently standardized on name UserReportV2 to avoid clashing with the old UserReport.
63 /// TODO(jferg): Rename this to UserFeedback once old UserReport is deprecated.
64 UserReportV2 = 14,
65 /// Metric buckets.
66 MetricBucket = 15,
67 /// SpanIndexed
68 ///
69 /// This is the category for spans we store in full.
70 SpanIndexed = 16,
71 /// ProfileDuration
72 ///
73 /// This data category is used to count the number of milliseconds per indexed profile chunk,
74 /// excluding UI profile chunks.
75 ProfileDuration = 17,
76 /// ProfileChunk
77 ///
78 /// This is a count of profile chunks received. It will not be used for billing but will be
79 /// useful for customers to track what's being dropped.
80 ProfileChunk = 18,
81 /// MetricSecond
82 ///
83 /// Reserved by billing to summarize the bucketed product of metric volume
84 /// and metric cardinality. Defined here so as not to clash with future
85 /// categories.
86 MetricSecond = 19,
87 /// Replay Video
88 ///
89 /// This is the data category for Session Replays produced via a video recording.
90 DoNotUseReplayVideo = 20,
91 /// This is the data category for Uptime monitors.
92 Uptime = 21,
93 /// Counts the number of individual attachments, as opposed to the number of bytes in an attachment.
94 AttachmentItem = 22,
95 /// LogItem
96 ///
97 /// This is the category for logs for which we store the count log events for users for measuring
98 /// missing breadcrumbs, and count of logs for rate limiting purposes.
99 LogItem = 23,
100 /// LogByte
101 ///
102 /// This is the category for logs for which we store log event total bytes for users.
103 LogByte = 24,
104 /// Profile duration of a UI profile.
105 ///
106 /// This data category is used to count the number of milliseconds per indexed UI profile
107 /// chunk.
108 ///
109 /// See also: [`Self::ProfileDuration`]
110 ProfileDurationUi = 25,
111 /// UI Profile Chunk.
112 ///
113 /// This data category is used to count the number of milliseconds per indexed UI profile
114 /// chunk.
115 ///
116 /// See also: [`Self::ProfileChunk`]
117 ProfileChunkUi = 26,
118 /// This is the data category to count Seer Autofix run events.
119 SeerAutofix = 27,
120 /// This is the data category to count Seer Scanner run events.
121 SeerScanner = 28,
122 //
123 // IMPORTANT: After adding a new entry to DataCategory, go to the `relay-cabi` subfolder and run
124 // `make header` to regenerate the C-binding. This allows using the data category from Python.
125 // Rerun this step every time the **code name** of the variant is updated.
126 //
127 /// Any other data category not known by this Relay.
128 #[serde(other)]
129 Unknown = -1,
130}
131
132impl DataCategory {
133 /// Returns the data category corresponding to the given name.
134 pub fn from_name(string: &str) -> Self {
135 // TODO: This should probably use serde.
136 match string {
137 "default" => Self::Default,
138 "error" => Self::Error,
139 "transaction" => Self::Transaction,
140 "security" => Self::Security,
141 "attachment" => Self::Attachment,
142 "session" => Self::Session,
143 "profile" => Self::Profile,
144 "profile_indexed" => Self::ProfileIndexed,
145 "replay" => Self::Replay,
146 "transaction_processed" => Self::TransactionProcessed,
147 "transaction_indexed" => Self::TransactionIndexed,
148 "monitor" => Self::Monitor,
149 "span" => Self::Span,
150 "log_item" => Self::LogItem,
151 "log_byte" => Self::LogByte,
152 "monitor_seat" => Self::MonitorSeat,
153 "feedback" => Self::UserReportV2,
154 "user_report_v2" => Self::UserReportV2,
155 "metric_bucket" => Self::MetricBucket,
156 "span_indexed" => Self::SpanIndexed,
157 "profile_duration" => Self::ProfileDuration,
158 "profile_duration_ui" => Self::ProfileDurationUi,
159 "profile_chunk" => Self::ProfileChunk,
160 "profile_chunk_ui" => Self::ProfileChunkUi,
161 "metric_second" => Self::MetricSecond,
162 "replay_video" => Self::DoNotUseReplayVideo,
163 "uptime" => Self::Uptime,
164 "attachment_item" => Self::AttachmentItem,
165 "seer_autofix" => Self::SeerAutofix,
166 "seer_scanner" => Self::SeerScanner,
167 _ => Self::Unknown,
168 }
169 }
170
171 /// Returns the canonical name of this data category.
172 pub fn name(self) -> &'static str {
173 // TODO: This should probably use serde.
174 match self {
175 Self::Default => "default",
176 Self::Error => "error",
177 Self::Transaction => "transaction",
178 Self::Security => "security",
179 Self::Attachment => "attachment",
180 Self::Session => "session",
181 Self::Profile => "profile",
182 Self::ProfileIndexed => "profile_indexed",
183 Self::Replay => "replay",
184 Self::DoNotUseReplayVideo => "replay_video",
185 Self::TransactionProcessed => "transaction_processed",
186 Self::TransactionIndexed => "transaction_indexed",
187 Self::Monitor => "monitor",
188 Self::Span => "span",
189 Self::LogItem => "log_item",
190 Self::LogByte => "log_byte",
191 Self::MonitorSeat => "monitor_seat",
192 Self::UserReportV2 => "feedback",
193 Self::MetricBucket => "metric_bucket",
194 Self::SpanIndexed => "span_indexed",
195 Self::ProfileDuration => "profile_duration",
196 Self::ProfileDurationUi => "profile_duration_ui",
197 Self::ProfileChunk => "profile_chunk",
198 Self::ProfileChunkUi => "profile_chunk_ui",
199 Self::MetricSecond => "metric_second",
200 Self::Uptime => "uptime",
201 Self::AttachmentItem => "attachment_item",
202 Self::SeerAutofix => "seer_autofix",
203 Self::SeerScanner => "seer_scanner",
204 Self::Unknown => "unknown",
205 }
206 }
207
208 /// Returns true if the DataCategory refers to an error (i.e an error event).
209 pub fn is_error(self) -> bool {
210 matches!(self, Self::Error | Self::Default | Self::Security)
211 }
212
213 /// Returns the numeric value for this outcome.
214 pub fn value(self) -> Option<u8> {
215 // negative values (Internal and Unknown) cannot be sent as
216 // outcomes (internally so!)
217 (self as i8).try_into().ok()
218 }
219
220 /// Returns a dedicated category for indexing if this data can be converted to metrics.
221 ///
222 /// This returns `None` for most data categories.
223 pub fn index_category(self) -> Option<Self> {
224 match self {
225 Self::Transaction => Some(Self::TransactionIndexed),
226 Self::Span => Some(Self::SpanIndexed),
227 Self::Profile => Some(Self::ProfileIndexed),
228 _ => None,
229 }
230 }
231
232 /// Returns `true` if this data category is an indexed data category.
233 pub fn is_indexed(self) -> bool {
234 matches!(
235 self,
236 Self::TransactionIndexed | Self::SpanIndexed | Self::ProfileIndexed
237 )
238 }
239}
240
241impl fmt::Display for DataCategory {
242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 write!(f, "{}", self.name())
244 }
245}
246
247impl FromStr for DataCategory {
248 type Err = ();
249
250 fn from_str(string: &str) -> Result<Self, Self::Err> {
251 Ok(Self::from_name(string))
252 }
253}
254
255impl From<EventType> for DataCategory {
256 fn from(ty: EventType) -> Self {
257 match ty {
258 EventType::Default | EventType::Error | EventType::Nel => Self::Error,
259 EventType::Transaction => Self::Transaction,
260 EventType::Csp | EventType::Hpkp | EventType::ExpectCt | EventType::ExpectStaple => {
261 Self::Security
262 }
263 EventType::UserReportV2 => Self::UserReportV2,
264 }
265 }
266}