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
119 //
120 // IMPORTANT: After adding a new entry to DataCategory, go to the `relay-cabi` subfolder and run
121 // `make header` to regenerate the C-binding. This allows using the data category from Python.
122 // Rerun this step every time the **code name** of the variant is updated.
123 //
124 /// Any other data category not known by this Relay.
125 #[serde(other)]
126 Unknown = -1,
127}
128
129impl DataCategory {
130 /// Returns the data category corresponding to the given name.
131 pub fn from_name(string: &str) -> Self {
132 // TODO: This should probably use serde.
133 match string {
134 "default" => Self::Default,
135 "error" => Self::Error,
136 "transaction" => Self::Transaction,
137 "security" => Self::Security,
138 "attachment" => Self::Attachment,
139 "session" => Self::Session,
140 "profile" => Self::Profile,
141 "profile_indexed" => Self::ProfileIndexed,
142 "replay" => Self::Replay,
143 "transaction_processed" => Self::TransactionProcessed,
144 "transaction_indexed" => Self::TransactionIndexed,
145 "monitor" => Self::Monitor,
146 "span" => Self::Span,
147 "log_item" => Self::LogItem,
148 "log_byte" => Self::LogByte,
149 "monitor_seat" => Self::MonitorSeat,
150 "feedback" => Self::UserReportV2,
151 "user_report_v2" => Self::UserReportV2,
152 "metric_bucket" => Self::MetricBucket,
153 "span_indexed" => Self::SpanIndexed,
154 "profile_duration" => Self::ProfileDuration,
155 "profile_duration_ui" => Self::ProfileDurationUi,
156 "profile_chunk" => Self::ProfileChunk,
157 "profile_chunk_ui" => Self::ProfileChunkUi,
158 "metric_second" => Self::MetricSecond,
159 "replay_video" => Self::DoNotUseReplayVideo,
160 "uptime" => Self::Uptime,
161 "attachment_item" => Self::AttachmentItem,
162 _ => Self::Unknown,
163 }
164 }
165
166 /// Returns the canonical name of this data category.
167 pub fn name(self) -> &'static str {
168 // TODO: This should probably use serde.
169 match self {
170 Self::Default => "default",
171 Self::Error => "error",
172 Self::Transaction => "transaction",
173 Self::Security => "security",
174 Self::Attachment => "attachment",
175 Self::Session => "session",
176 Self::Profile => "profile",
177 Self::ProfileIndexed => "profile_indexed",
178 Self::Replay => "replay",
179 Self::DoNotUseReplayVideo => "replay_video",
180 Self::TransactionProcessed => "transaction_processed",
181 Self::TransactionIndexed => "transaction_indexed",
182 Self::Monitor => "monitor",
183 Self::Span => "span",
184 Self::LogItem => "log_item",
185 Self::LogByte => "log_byte",
186 Self::MonitorSeat => "monitor_seat",
187 Self::UserReportV2 => "feedback",
188 Self::MetricBucket => "metric_bucket",
189 Self::SpanIndexed => "span_indexed",
190 Self::ProfileDuration => "profile_duration",
191 Self::ProfileDurationUi => "profile_duration_ui",
192 Self::ProfileChunk => "profile_chunk",
193 Self::ProfileChunkUi => "profile_chunk_ui",
194 Self::MetricSecond => "metric_second",
195 Self::Uptime => "uptime",
196 Self::AttachmentItem => "attachment_item",
197 Self::Unknown => "unknown",
198 }
199 }
200
201 /// Returns true if the DataCategory refers to an error (i.e an error event).
202 pub fn is_error(self) -> bool {
203 matches!(self, Self::Error | Self::Default | Self::Security)
204 }
205
206 /// Returns the numeric value for this outcome.
207 pub fn value(self) -> Option<u8> {
208 // negative values (Internal and Unknown) cannot be sent as
209 // outcomes (internally so!)
210 (self as i8).try_into().ok()
211 }
212
213 /// Returns a dedicated category for indexing if this data can be converted to metrics.
214 ///
215 /// This returns `None` for most data categories.
216 pub fn index_category(self) -> Option<Self> {
217 match self {
218 Self::Transaction => Some(Self::TransactionIndexed),
219 Self::Span => Some(Self::SpanIndexed),
220 Self::Profile => Some(Self::ProfileIndexed),
221 _ => None,
222 }
223 }
224
225 /// Returns `true` if this data category is an indexed data category.
226 pub fn is_indexed(self) -> bool {
227 matches!(
228 self,
229 Self::TransactionIndexed | Self::SpanIndexed | Self::ProfileIndexed
230 )
231 }
232}
233
234impl fmt::Display for DataCategory {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 write!(f, "{}", self.name())
237 }
238}
239
240impl FromStr for DataCategory {
241 type Err = ();
242
243 fn from_str(string: &str) -> Result<Self, Self::Err> {
244 Ok(Self::from_name(string))
245 }
246}
247
248impl From<EventType> for DataCategory {
249 fn from(ty: EventType) -> Self {
250 match ty {
251 EventType::Default | EventType::Error | EventType::Nel => Self::Error,
252 EventType::Transaction => Self::Transaction,
253 EventType::Csp | EventType::Hpkp | EventType::ExpectCt | EventType::ExpectStaple => {
254 Self::Security
255 }
256 EventType::UserReportV2 => Self::UserReportV2,
257 }
258 }
259}