1use reifydb_core::profiler::ProfilerCategoryId;
5use serde::{Deserialize, Serialize};
6use tracing::{Level, level_filters::LevelFilter};
7
8#[repr(u8)]
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum ProfilerCategory {
11 Query = 0,
12 Txn = 1,
13 Storage = 2,
14 Plan = 3,
15 Cdc = 4,
16 Flow = 5,
17 Subscription = 6,
18 Server = 7,
19 Wire = 8,
20 Auth = 9,
21 Catalog = 10,
22 Engine = 11,
23 Mutate = 12,
24 Transport = 13,
25 Task = 14,
26 Policy = 15,
27 ExternC = 16,
28 Cache = 17,
29 RowShape = 18,
30 Api = 19,
31 Actor = 20,
32 Lifecycle = 21,
33}
34
35pub const CATEGORY_COUNT: usize = 22;
36
37pub const ALL_CATEGORIES: [ProfilerCategory; CATEGORY_COUNT] = [
38 ProfilerCategory::Query,
39 ProfilerCategory::Txn,
40 ProfilerCategory::Storage,
41 ProfilerCategory::Plan,
42 ProfilerCategory::Cdc,
43 ProfilerCategory::Flow,
44 ProfilerCategory::Subscription,
45 ProfilerCategory::Server,
46 ProfilerCategory::Wire,
47 ProfilerCategory::Auth,
48 ProfilerCategory::Catalog,
49 ProfilerCategory::Engine,
50 ProfilerCategory::Mutate,
51 ProfilerCategory::Transport,
52 ProfilerCategory::Task,
53 ProfilerCategory::Policy,
54 ProfilerCategory::ExternC,
55 ProfilerCategory::Cache,
56 ProfilerCategory::RowShape,
57 ProfilerCategory::Api,
58 ProfilerCategory::Actor,
59 ProfilerCategory::Lifecycle,
60];
61
62impl ProfilerCategory {
63 pub const fn as_id(self) -> ProfilerCategoryId {
64 ProfilerCategoryId(self as u8)
65 }
66
67 pub const fn from_id(id: ProfilerCategoryId) -> Option<Self> {
68 match id.0 {
69 0 => Some(ProfilerCategory::Query),
70 1 => Some(ProfilerCategory::Txn),
71 2 => Some(ProfilerCategory::Storage),
72 3 => Some(ProfilerCategory::Plan),
73 4 => Some(ProfilerCategory::Cdc),
74 5 => Some(ProfilerCategory::Flow),
75 6 => Some(ProfilerCategory::Subscription),
76 7 => Some(ProfilerCategory::Server),
77 8 => Some(ProfilerCategory::Wire),
78 9 => Some(ProfilerCategory::Auth),
79 10 => Some(ProfilerCategory::Catalog),
80 11 => Some(ProfilerCategory::Engine),
81 12 => Some(ProfilerCategory::Mutate),
82 13 => Some(ProfilerCategory::Transport),
83 14 => Some(ProfilerCategory::Task),
84 15 => Some(ProfilerCategory::Policy),
85 16 => Some(ProfilerCategory::ExternC),
86 17 => Some(ProfilerCategory::Cache),
87 18 => Some(ProfilerCategory::RowShape),
88 19 => Some(ProfilerCategory::Api),
89 20 => Some(ProfilerCategory::Actor),
90 21 => Some(ProfilerCategory::Lifecycle),
91 _ => None,
92 }
93 }
94
95 pub fn from_span_name(name: &str) -> Option<Self> {
96 if name.starts_with("flow::extern_c::") {
97 Some(ProfilerCategory::ExternC)
98 } else if name.starts_with("flow::") {
99 Some(ProfilerCategory::Flow)
100 } else if name.starts_with("transaction::") {
101 Some(ProfilerCategory::Txn)
102 } else if name.starts_with("store::single::")
103 || name.starts_with("store::multi::")
104 || name.starts_with("store::cdc::")
105 || name.starts_with("store::operator::")
106 || name.starts_with("drop::")
107 {
108 Some(ProfilerCategory::Storage)
109 } else if name.starts_with("volcano::") || name.starts_with("vm::") {
110 Some(ProfilerCategory::Query)
111 } else if name.starts_with("rql::") {
112 Some(ProfilerCategory::Plan)
113 } else if name.starts_with("catalog::") {
114 Some(ProfilerCategory::Catalog)
115 } else if name.starts_with("cache::") {
116 Some(ProfilerCategory::Cache)
117 } else if name.starts_with("shape_store::") || name.starts_with("row_shape_registry::") {
118 Some(ProfilerCategory::RowShape)
119 } else if name.starts_with("api::") {
120 Some(ProfilerCategory::Api)
121 } else if name.starts_with("actor::") {
122 Some(ProfilerCategory::Actor)
123 } else if name.starts_with("cdc::") {
124 Some(ProfilerCategory::Cdc)
125 } else if name.starts_with("subscription::") {
126 Some(ProfilerCategory::Subscription)
127 } else if name.starts_with("server::") {
128 Some(ProfilerCategory::Server)
129 } else if name.starts_with("wire::") {
130 Some(ProfilerCategory::Wire)
131 } else if name.starts_with("auth::") {
132 Some(ProfilerCategory::Auth)
133 } else if name.starts_with("engine::")
134 || name.starts_with("executor::")
135 || name.starts_with("session::")
136 {
137 Some(ProfilerCategory::Engine)
138 } else if name.starts_with("mutate::") {
139 Some(ProfilerCategory::Mutate)
140 } else if name.starts_with("http::") || name.starts_with("dispatch::") {
141 Some(ProfilerCategory::Transport)
142 } else if name.starts_with("lifecycle::") {
143 Some(ProfilerCategory::Lifecycle)
144 } else if name.starts_with("task::") {
145 Some(ProfilerCategory::Task)
146 } else if name.starts_with("policy::") {
147 Some(ProfilerCategory::Policy)
148 } else if name.starts_with("extern_c::")
149 || name.starts_with("procedure::")
150 || name.starts_with("transform::")
151 {
152 Some(ProfilerCategory::ExternC)
153 } else {
154 None
155 }
156 }
157
158 pub const fn name(self) -> &'static str {
159 match self {
160 ProfilerCategory::Query => "query",
161 ProfilerCategory::Txn => "txn",
162 ProfilerCategory::Storage => "storage",
163 ProfilerCategory::Plan => "plan",
164 ProfilerCategory::Cdc => "cdc",
165 ProfilerCategory::Flow => "flow",
166 ProfilerCategory::Subscription => "subscription",
167 ProfilerCategory::Server => "server",
168 ProfilerCategory::Wire => "wire",
169 ProfilerCategory::Auth => "auth",
170 ProfilerCategory::Catalog => "catalog",
171 ProfilerCategory::Engine => "engine",
172 ProfilerCategory::Mutate => "mutate",
173 ProfilerCategory::Transport => "transport",
174 ProfilerCategory::Task => "task",
175 ProfilerCategory::Policy => "policy",
176 ProfilerCategory::ExternC => "extern_c",
177 ProfilerCategory::Cache => "cache",
178 ProfilerCategory::RowShape => "row_shape",
179 ProfilerCategory::Api => "api",
180 ProfilerCategory::Actor => "actor",
181 ProfilerCategory::Lifecycle => "lifecycle",
182 }
183 }
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
187pub enum ProfilerLevel {
188 Trace,
189 Debug,
190 Info,
191 Warn,
192 Error,
193}
194
195impl ProfilerLevel {
196 pub fn as_level_filter(self) -> LevelFilter {
197 match self {
198 Self::Trace => LevelFilter::TRACE,
199 Self::Debug => LevelFilter::DEBUG,
200 Self::Info => LevelFilter::INFO,
201 Self::Warn => LevelFilter::WARN,
202 Self::Error => LevelFilter::ERROR,
203 }
204 }
205
206 pub fn admits(self, level: &Level) -> bool {
207 *level <= self.as_level_filter()
208 }
209}
210
211#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
212pub struct CategorySet {
213 levels: [Option<ProfilerLevel>; CATEGORY_COUNT],
214}
215
216impl CategorySet {
217 pub const fn empty() -> Self {
218 Self {
219 levels: [None; CATEGORY_COUNT],
220 }
221 }
222
223 pub const fn all() -> Self {
224 Self::all_at(ProfilerLevel::Trace)
225 }
226
227 pub const fn all_at(level: ProfilerLevel) -> Self {
228 Self {
229 levels: [Some(level); CATEGORY_COUNT],
230 }
231 }
232
233 pub fn contains(&self, c: ProfilerCategory) -> bool {
234 self.levels[c as usize].is_some()
235 }
236
237 pub fn level_for(&self, c: ProfilerCategory) -> Option<ProfilerLevel> {
238 self.levels[c as usize]
239 }
240
241 pub fn insert(&mut self, c: ProfilerCategory) {
242 self.insert_at(c, ProfilerLevel::Trace);
243 }
244
245 pub fn insert_at(&mut self, c: ProfilerCategory, level: ProfilerLevel) {
246 self.levels[c as usize] = Some(level);
247 }
248
249 pub fn remove(&mut self, c: ProfilerCategory) {
250 self.levels[c as usize] = None;
251 }
252
253 pub fn with(mut self, c: ProfilerCategory) -> Self {
254 self.insert(c);
255 self
256 }
257
258 pub fn with_level(mut self, c: ProfilerCategory, level: ProfilerLevel) -> Self {
259 self.insert_at(c, level);
260 self
261 }
262
263 pub fn without(mut self, c: ProfilerCategory) -> Self {
264 self.remove(c);
265 self
266 }
267
268 pub fn is_empty(&self) -> bool {
269 self.levels.iter().all(|l| l.is_none())
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276
277 #[test]
278 fn from_span_name_known_prefixes() {
279 assert_eq!(ProfilerCategory::from_span_name("flow::engine::apply"), Some(ProfilerCategory::Flow));
280 assert_eq!(
281 ProfilerCategory::from_span_name("flow::engine::process_batch"),
282 Some(ProfilerCategory::Flow)
283 );
284 assert_eq!(ProfilerCategory::from_span_name("transaction::commit"), Some(ProfilerCategory::Txn));
285 assert_eq!(ProfilerCategory::from_span_name("store::multi::write"), Some(ProfilerCategory::Storage));
286 assert_eq!(ProfilerCategory::from_span_name("store::single::scan"), Some(ProfilerCategory::Storage));
287 assert_eq!(
288 ProfilerCategory::from_span_name("store::cdc::sqlite::write"),
289 Some(ProfilerCategory::Storage)
290 );
291 assert_eq!(
292 ProfilerCategory::from_span_name("store::operator::range_batch"),
293 Some(ProfilerCategory::Storage)
294 );
295 assert_eq!(ProfilerCategory::from_span_name("drop::range"), Some(ProfilerCategory::Storage));
296 assert_eq!(ProfilerCategory::from_span_name("volcano::project"), Some(ProfilerCategory::Query));
297 assert_eq!(ProfilerCategory::from_span_name("vm::executor"), Some(ProfilerCategory::Query));
298 assert_eq!(ProfilerCategory::from_span_name("rql::parse"), Some(ProfilerCategory::Plan));
299 assert_eq!(ProfilerCategory::from_span_name("cdc::write"), Some(ProfilerCategory::Cdc));
300 assert_eq!(
301 ProfilerCategory::from_span_name("subscription::consume"),
302 Some(ProfilerCategory::Subscription)
303 );
304 assert_eq!(ProfilerCategory::from_span_name("server::deliver"), Some(ProfilerCategory::Server));
305 assert_eq!(ProfilerCategory::from_span_name("wire::encode_frames"), Some(ProfilerCategory::Wire));
306 assert_eq!(ProfilerCategory::from_span_name("auth::authenticate"), Some(ProfilerCategory::Auth));
307 assert_eq!(ProfilerCategory::from_span_name("engine::query_as"), Some(ProfilerCategory::Engine));
308 assert_eq!(ProfilerCategory::from_span_name("executor::compile"), Some(ProfilerCategory::Engine));
309 assert_eq!(ProfilerCategory::from_span_name("session::query"), Some(ProfilerCategory::Engine));
310 assert_eq!(ProfilerCategory::from_span_name("mutate::table::insert"), Some(ProfilerCategory::Mutate));
311 assert_eq!(ProfilerCategory::from_span_name("http::query"), Some(ProfilerCategory::Transport));
312 assert_eq!(
313 ProfilerCategory::from_span_name("dispatch::send_server_message"),
314 Some(ProfilerCategory::Transport)
315 );
316 assert_eq!(ProfilerCategory::from_span_name("task::spawn"), Some(ProfilerCategory::Task));
317 assert_eq!(ProfilerCategory::from_span_name("policy::enforce"), Some(ProfilerCategory::Policy));
318 assert_eq!(ProfilerCategory::from_span_name("extern_c::callback"), Some(ProfilerCategory::ExternC));
319 assert_eq!(
320 ProfilerCategory::from_span_name("procedure::extern_c::execute"),
321 Some(ProfilerCategory::ExternC)
322 );
323 assert_eq!(
324 ProfilerCategory::from_span_name("transform::extern_c::apply"),
325 Some(ProfilerCategory::ExternC)
326 );
327 assert_eq!(
329 ProfilerCategory::from_span_name("flow::extern_c::vtable_call"),
330 Some(ProfilerCategory::ExternC)
331 );
332 assert_eq!(ProfilerCategory::from_span_name("flow::engine::apply"), Some(ProfilerCategory::Flow));
333 }
334
335 #[test]
336 fn catalog_split_out_of_plan() {
337 assert_eq!(
339 ProfilerCategory::from_span_name("catalog::column::find_by_name"),
340 Some(ProfilerCategory::Catalog)
341 );
342 assert_eq!(ProfilerCategory::from_span_name("rql::plan"), Some(ProfilerCategory::Plan));
343 }
344
345 #[test]
346 fn from_span_name_new_subsystem_prefixes() {
347 assert_eq!(ProfilerCategory::from_span_name("cache::row_shape::load"), Some(ProfilerCategory::Cache));
349 assert_eq!(ProfilerCategory::from_span_name("shape_store::create"), Some(ProfilerCategory::RowShape));
350 assert_eq!(
352 ProfilerCategory::from_span_name("row_shape_registry::load"),
353 Some(ProfilerCategory::RowShape)
354 );
355 assert_eq!(ProfilerCategory::from_span_name("api::stop_fast"), Some(ProfilerCategory::Api));
356 assert_eq!(ProfilerCategory::from_span_name("actor::task_pool"), Some(ProfilerCategory::Actor));
357 }
358
359 #[test]
360 fn from_span_name_flow_covers_non_engine_prefixes() {
361 assert_eq!(
364 ProfilerCategory::from_span_name("flow::coordinator::consume"),
365 Some(ProfilerCategory::Flow)
366 );
367 assert_eq!(ProfilerCategory::from_span_name("flow::pool::submit"), Some(ProfilerCategory::Flow));
368 assert_eq!(ProfilerCategory::from_span_name("flow::actor::tick"), Some(ProfilerCategory::Flow));
369 assert_eq!(ProfilerCategory::from_span_name("flow::engine::apply"), Some(ProfilerCategory::Flow));
370 }
371
372 #[test]
373 fn from_span_name_unknown_returns_none() {
374 assert_eq!(ProfilerCategory::from_span_name("random::other::name"), None);
375 assert_eq!(ProfilerCategory::from_span_name(""), None);
376 assert_eq!(ProfilerCategory::from_span_name("tracing::subsystem::start"), None);
377 }
378
379 #[test]
380 fn id_roundtrip() {
381 for c in ALL_CATEGORIES {
382 assert_eq!(ProfilerCategory::from_id(c.as_id()), Some(c));
383 }
384 assert_eq!(ProfilerCategory::from_id(ProfilerCategoryId(CATEGORY_COUNT as u8)), None);
385 assert_eq!(ProfilerCategory::from_id(ProfilerCategoryId(255)), None);
386 }
387
388 #[test]
389 fn category_set_membership() {
390 let mut s = CategorySet::empty();
391 assert!(s.is_empty());
392 s.insert(ProfilerCategory::Flow);
393 assert!(s.contains(ProfilerCategory::Flow));
394 assert!(!s.contains(ProfilerCategory::Query));
395 s.insert(ProfilerCategory::Query);
396 assert!(s.contains(ProfilerCategory::Query));
397 s.remove(ProfilerCategory::Flow);
398 assert!(!s.contains(ProfilerCategory::Flow));
399
400 let all = CategorySet::all();
401 for c in ALL_CATEGORIES {
402 assert!(all.contains(c));
403 }
404 }
405
406 #[test]
407 fn category_set_default_insert_is_trace() {
408 let mut s = CategorySet::empty();
409 s.insert(ProfilerCategory::Flow);
410 assert_eq!(s.level_for(ProfilerCategory::Flow), Some(ProfilerLevel::Trace));
411 }
412
413 #[test]
414 fn category_set_per_category_level() {
415 let mut s = CategorySet::empty();
416 s.insert_at(ProfilerCategory::Flow, ProfilerLevel::Trace);
417 s.insert_at(ProfilerCategory::Query, ProfilerLevel::Debug);
418 s.insert_at(ProfilerCategory::Storage, ProfilerLevel::Info);
419
420 assert_eq!(s.level_for(ProfilerCategory::Flow), Some(ProfilerLevel::Trace));
421 assert_eq!(s.level_for(ProfilerCategory::Query), Some(ProfilerLevel::Debug));
422 assert_eq!(s.level_for(ProfilerCategory::Storage), Some(ProfilerLevel::Info));
423 assert_eq!(s.level_for(ProfilerCategory::Plan), None);
424
425 assert!(s.contains(ProfilerCategory::Flow));
426 assert!(s.contains(ProfilerCategory::Query));
427 assert!(s.contains(ProfilerCategory::Storage));
428 assert!(!s.contains(ProfilerCategory::Plan));
429 }
430
431 #[test]
432 fn category_set_with_level_round_trips() {
433 let s = CategorySet::empty()
434 .with_level(ProfilerCategory::Flow, ProfilerLevel::Trace)
435 .with_level(ProfilerCategory::Plan, ProfilerLevel::Debug);
436 assert_eq!(s.level_for(ProfilerCategory::Flow), Some(ProfilerLevel::Trace));
437 assert_eq!(s.level_for(ProfilerCategory::Plan), Some(ProfilerLevel::Debug));
438 assert_eq!(s.level_for(ProfilerCategory::Cdc), None);
439 }
440
441 #[test]
442 fn category_set_all_at_sets_uniform_level() {
443 let s = CategorySet::all_at(ProfilerLevel::Debug);
444 for c in ALL_CATEGORIES {
445 assert_eq!(s.level_for(c), Some(ProfilerLevel::Debug));
446 }
447 }
448
449 #[test]
450 fn profile_level_admits_at_or_less_verbose() {
451 assert!(ProfilerLevel::Debug.admits(&Level::DEBUG));
452 assert!(ProfilerLevel::Debug.admits(&Level::INFO));
453 assert!(ProfilerLevel::Debug.admits(&Level::WARN));
454 assert!(ProfilerLevel::Debug.admits(&Level::ERROR));
455 assert!(!ProfilerLevel::Debug.admits(&Level::TRACE));
456
457 assert!(ProfilerLevel::Trace.admits(&Level::TRACE));
458 assert!(!ProfilerLevel::Error.admits(&Level::WARN));
459 }
460}