bitwarden_logging/
visitor.rs1use std::collections::HashMap;
4
5use tracing::field::{Field, Visit};
6
7#[derive(Debug, Default)]
9pub struct MessageVisitor {
10 pub message: String,
12 pub fields: HashMap<String, String>,
14}
15
16impl Visit for MessageVisitor {
17 fn record_str(&mut self, field: &Field, value: &str) {
18 if field.name() == "message" {
19 self.message = value.to_string();
20 } else {
21 self.fields
22 .insert(field.name().to_string(), value.to_string());
23 }
24 }
25
26 fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
27 if field.name() == "message" {
28 self.message = format!("{:?}", value);
29 } else {
30 self.fields
31 .insert(field.name().to_string(), format!("{:?}", value));
32 }
33 }
34}
35
36#[cfg(test)]
37mod tests {
38 use tracing::Level;
39
40 use super::*;
41
42 #[test]
43 fn test_visitor_extracts_message() {
44 let fields = tracing::field::FieldSet::new(
45 &["message"],
46 tracing::callsite::Identifier(&TEST_CALLSITE),
47 );
48
49 let mut visitor = MessageVisitor::default();
50 assert!(visitor.message.is_empty());
51 assert!(visitor.fields.is_empty());
52
53 let field = fields.field("message").expect("field should exist");
54 visitor.record_str(&field, "hello world");
55 assert_eq!(visitor.message, "hello world");
56 }
57
58 #[test]
59 fn test_visitor_extracts_extra_fields() {
60 let fields = tracing::field::FieldSet::new(
61 &["message", "user_id"],
62 tracing::callsite::Identifier(&TEST_CALLSITE),
63 );
64
65 let mut visitor = MessageVisitor::default();
66
67 let msg_field = fields.field("message").expect("field should exist");
68 visitor.record_str(&msg_field, "login attempt");
69
70 let id_field = fields.field("user_id").expect("field should exist");
71 visitor.record_str(&id_field, "abc-123");
72
73 assert_eq!(visitor.message, "login attempt");
74 assert_eq!(visitor.fields.get("user_id"), Some(&"abc-123".to_string()));
75 }
76
77 #[test]
78 fn test_visitor_record_debug_fallback() {
79 let fields = tracing::field::FieldSet::new(
80 &["message", "count"],
81 tracing::callsite::Identifier(&TEST_CALLSITE),
82 );
83
84 let mut visitor = MessageVisitor::default();
85
86 let count_field = fields.field("count").expect("field should exist");
87 visitor.record_debug(&count_field, &42);
88
89 assert_eq!(visitor.fields.get("count"), Some(&"42".to_string()));
90 }
91
92 static TEST_CALLSITE: TestCallsite = TestCallsite;
94
95 struct TestCallsite;
96
97 impl tracing::callsite::Callsite for TestCallsite {
98 fn set_interest(&self, _interest: tracing::subscriber::Interest) {}
99 fn metadata(&self) -> &tracing::Metadata<'_> {
100 static META: std::sync::LazyLock<tracing::Metadata<'static>> =
101 std::sync::LazyLock::new(|| {
102 tracing::metadata::Metadata::new(
103 "test",
104 "test_target",
105 Level::INFO,
106 None,
107 None,
108 None,
109 tracing::field::FieldSet::new(
110 &[],
111 tracing::callsite::Identifier(&TEST_CALLSITE),
112 ),
113 tracing::metadata::Kind::EVENT,
114 )
115 });
116 &META
117 }
118 }
119}