mithril_common/
logging.rs1use slog::Logger;
4
5pub trait LoggerExtensions {
7 fn new_with_component_name<T>(&self) -> Self;
9
10 fn new_with_name(&self, name: &str) -> Self;
12}
13
14impl LoggerExtensions for Logger {
15 fn new_with_component_name<T>(&self) -> Self {
16 self.new_with_name(component_name::<T>())
17 }
18
19 fn new_with_name(&self, name: &str) -> Self {
20 self.new(slog::o!("src" => name.to_owned()))
21 }
22}
23
24fn component_name<T>() -> &'static str {
25 let complete_name = std::any::type_name::<T>();
26 let without_generic = {
27 if complete_name.contains('<') {
28 complete_name.split('<').next().unwrap_or("")
29 } else {
30 complete_name
31 }
32 };
33 without_generic.split("::").last().unwrap_or(complete_name)
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39 use crate::test_utils::TestLogger;
40 use slog::info;
41
42 struct TestStruct;
43 #[allow(dead_code)]
45 struct TestStructWithLifetime<'a>(&'a str);
46 enum TestEnum {}
47
48 struct TestStructWithGeneric<T> {
49 _phantom: std::marker::PhantomData<T>,
50 }
51
52 mod test_mod {
53 pub struct ScopedTestStruct;
54 pub enum ScopedTestEnum {}
55 }
56
57 impl TestStruct {
58 fn self_component_name() -> &'static str {
59 component_name::<Self>()
60 }
61 }
62
63 #[test]
64 fn extract_component_name_remove_namespaces() {
65 assert_eq!(component_name::<TestStruct>(), "TestStruct");
66 assert_eq!(component_name::<TestEnum>(), "TestEnum");
67 assert_eq!(
68 component_name::<test_mod::ScopedTestStruct>(),
69 "ScopedTestStruct"
70 );
71 assert_eq!(
72 component_name::<test_mod::ScopedTestEnum>(),
73 "ScopedTestEnum"
74 );
75 assert_eq!(TestStruct::self_component_name(), "TestStruct");
76 assert_eq!(
77 component_name::<TestStructWithLifetime>(),
78 "TestStructWithLifetime"
79 );
80 assert_eq!(
81 component_name::<TestStructWithGeneric<test_mod::ScopedTestStruct>>(),
82 "TestStructWithGeneric"
83 );
84 assert_eq!(
85 component_name::<TestStructWithGeneric<&str>>(),
86 "TestStructWithGeneric"
87 );
88 }
89
90 #[test]
91 fn logger_extension_new_with_component_name() {
92 let (root_logger, log_inspector) = TestLogger::memory();
93 let child_logger = root_logger.new_with_component_name::<TestStruct>();
94 info!(child_logger, "Child log");
95
96 assert!(
97 log_inspector.contains_log("src") && log_inspector.contains_log("TestStruct"),
98 "log should contain `src` key for `TestStruct` as component name was provided, logs:\n{log_inspector}"
99 );
100 }
101
102 #[test]
103 fn logger_extension_new_with_name() {
104 let expected_name = "my name";
105 let (root_logger, log_inspector) = TestLogger::memory();
106 let child_logger = root_logger.new_with_name(expected_name);
107 info!(child_logger, "Child log");
108
109 assert!(
110 log_inspector.contains_log("src") && log_inspector.contains_log(expected_name),
111 "log should contain `src` key for `{expected_name}` as a name was provided, logs:\n{log_inspector}"
112 );
113 }
114}