code_analyze_mcp/
logging.rs1use rmcp::model::LoggingLevel;
7use serde_json::{Map, Value};
8use std::sync::{Arc, Mutex};
9use tokio::sync::mpsc;
10use tracing::subscriber::Interest;
11use tracing::{Level, Subscriber};
12use tracing_subscriber::Layer;
13use tracing_subscriber::filter::LevelFilter;
14use tracing_subscriber::layer::Context;
15
16pub fn level_to_mcp(level: &Level) -> LoggingLevel {
18 match *level {
19 Level::TRACE | Level::DEBUG => LoggingLevel::Debug,
20 Level::INFO => LoggingLevel::Info,
21 Level::WARN => LoggingLevel::Warning,
22 Level::ERROR => LoggingLevel::Error,
23 }
24}
25
26#[derive(Clone, Debug)]
28pub struct LogEvent {
29 pub level: LoggingLevel,
30 pub logger: String,
31 pub data: Value,
32}
33
34pub struct McpLoggingLayer {
37 event_tx: mpsc::UnboundedSender<LogEvent>,
38 log_level_filter: Arc<Mutex<LevelFilter>>,
39}
40
41impl McpLoggingLayer {
42 pub fn new(
43 event_tx: mpsc::UnboundedSender<LogEvent>,
44 log_level_filter: Arc<Mutex<LevelFilter>>,
45 ) -> Self {
46 Self {
47 event_tx,
48 log_level_filter,
49 }
50 }
51}
52
53impl<S> Layer<S> for McpLoggingLayer
54where
55 S: Subscriber,
56{
57 fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) {
58 let metadata = event.metadata();
59 let level = *metadata.level();
60 let target = metadata.target();
61
62 let filter_level = self.log_level_filter.lock().unwrap();
64 if level > *filter_level {
65 return;
66 }
67 drop(filter_level);
68
69 let mut fields = Map::new();
71 let mut visitor = MessageVisitor(&mut fields);
72 event.record(&mut visitor);
73
74 let mcp_level = level_to_mcp(&level);
75 let logger = target.to_string();
76 let data = Value::Object(fields);
77
78 let log_event = LogEvent {
80 level: mcp_level,
81 logger,
82 data,
83 };
84
85 let _ = self.event_tx.send(log_event);
87 }
88
89 fn register_callsite(&self, metadata: &'static tracing::Metadata<'static>) -> Interest {
90 let filter_level = self.log_level_filter.lock().unwrap();
91 if *metadata.level() <= *filter_level {
92 Interest::always()
93 } else {
94 Interest::never()
95 }
96 }
97
98 fn enabled(&self, metadata: &tracing::Metadata<'_>, _ctx: Context<'_, S>) -> bool {
99 let filter_level = self.log_level_filter.lock().unwrap();
100 *metadata.level() <= *filter_level
101 }
102}
103
104struct MessageVisitor<'a>(&'a mut Map<String, Value>);
106
107impl<'a> tracing::field::Visit for MessageVisitor<'a> {
108 fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
109 self.0.insert(
110 field.name().to_string(),
111 Value::String(format!("{:?}", value)),
112 );
113 }
114
115 fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
116 self.0
117 .insert(field.name().to_string(), Value::String(value.to_string()));
118 }
119
120 fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
121 self.0
122 .insert(field.name().to_string(), Value::Number(value.into()));
123 }
124
125 fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
126 self.0
127 .insert(field.name().to_string(), Value::Number(value.into()));
128 }
129
130 fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
131 self.0.insert(field.name().to_string(), Value::Bool(value));
132 }
133}