witchcraft_server/debug/
mod.rs1use std::collections::hash_map::Entry;
16use std::collections::HashMap;
17use std::sync::Arc;
18
19use bytes::Bytes;
20use conjure_error::Error;
21use http::HeaderValue;
22use once_cell::sync::Lazy;
23use parking_lot::Mutex;
24use regex::Regex;
25
26pub(crate) mod diagnostic_types;
27pub(crate) mod endpoint;
28#[cfg(feature = "jemalloc")]
29pub(crate) mod heap_profile;
30#[cfg(feature = "jemalloc")]
31pub(crate) mod heap_stats;
32pub(crate) mod metric_names;
33#[cfg(target_os = "linux")]
34pub(crate) mod thread_dump;
35
36static TYPE_PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r"([a-z0-9]+\.)+v[0-9]+").unwrap());
37
38pub trait Diagnostic {
40 fn type_(&self) -> &str;
44
45 fn content_type(&self) -> HeaderValue;
49
50 fn safe_loggable(&self) -> bool;
52
53 fn result(&self) -> Result<Bytes, Error>;
55}
56
57pub struct DiagnosticRegistry {
59 diagnostics: Mutex<HashMap<String, Arc<dyn Diagnostic + Sync + Send>>>,
60}
61
62impl Default for DiagnosticRegistry {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68impl DiagnosticRegistry {
69 pub fn new() -> Self {
71 DiagnosticRegistry {
72 diagnostics: Mutex::new(HashMap::new()),
73 }
74 }
75
76 pub fn register<T>(&self, diagnostic: T)
85 where
86 T: Diagnostic + 'static + Sync + Send,
87 {
88 self.register_inner(Arc::new(diagnostic));
89 }
90
91 fn register_inner(&self, diagnostic: Arc<dyn Diagnostic + Sync + Send>) {
92 let type_ = diagnostic.type_();
93
94 assert!(
95 TYPE_PATTERN.is_match(type_),
96 "{type_} must be `lower.case.dot.delimited.v1`",
97 );
98
99 match self.diagnostics.lock().entry(type_.to_string()) {
100 Entry::Occupied(_) => {
101 panic!("a diagnostic has already been registered for type {type_}")
102 }
103 Entry::Vacant(e) => {
104 e.insert(diagnostic);
105 }
106 }
107 }
108
109 pub fn get(&self, type_: &str) -> Option<Arc<dyn Diagnostic + Sync + Send>> {
111 self.diagnostics.lock().get(type_).cloned()
112 }
113}