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}