saf_core/logging/subscriber/
layer.rs1use std::io::Write;
8use std::sync::Mutex;
9
10use tracing::Event;
11use tracing::field::{Field, Visit};
12use tracing_subscriber::Layer;
13use tracing_subscriber::layer::Context;
14
15use super::filter::SafLogFilter;
16use crate::logging::formatter::format_saf_log_line;
17
18pub struct SafLogLayer {
20 filter: SafLogFilter,
21 writer: Mutex<Box<dyn Write + Send>>,
22}
23
24impl SafLogLayer {
25 pub fn new(filter: SafLogFilter, writer: Box<dyn Write + Send>) -> Self {
27 Self {
28 filter,
29 writer: Mutex::new(writer),
30 }
31 }
32}
33
34impl<S: tracing::Subscriber> Layer<S> for SafLogLayer {
35 fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
36 if event.metadata().target() != "saf_debug" {
38 return;
39 }
40
41 if !self.filter.is_active() {
43 return;
44 }
45
46 let mut visitor = SafLogVisitor::default();
48 event.record(&mut visitor);
49
50 let module = visitor.module.as_deref().unwrap_or("");
51 let phase = visitor.phase.as_deref().unwrap_or("");
52 let tag = visitor.tag.as_deref().unwrap_or("");
53
54 if !self.filter.matches(module, phase, tag) {
56 return;
57 }
58
59 let narrative = visitor.narrative.as_deref().unwrap_or("");
60 let kv = visitor.kv.as_deref().unwrap_or("");
61
62 let line = format_saf_log_line(module, phase, tag, narrative, kv);
63
64 if let Ok(mut w) = self.writer.lock() {
65 let _ = writeln!(w, "{line}");
66 }
67 }
68}
69
70#[derive(Default)]
72struct SafLogVisitor {
73 module: Option<String>,
74 phase: Option<String>,
75 tag: Option<String>,
76 narrative: Option<String>,
77 kv: Option<String>,
78}
79
80impl Visit for SafLogVisitor {
81 fn record_str(&mut self, field: &Field, value: &str) {
82 match field.name() {
83 "saf_module" => self.module = Some(value.to_string()),
84 "saf_phase" => self.phase = Some(value.to_string()),
85 "saf_tag" => self.tag = Some(value.to_string()),
86 "saf_narrative" => self.narrative = Some(value.to_string()),
87 "saf_kv" => self.kv = Some(value.to_string()),
88 _ => {}
89 }
90 }
91
92 fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
93 let s = format!("{value:?}");
95 self.record_str(field, &s);
96 }
97}