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