ultrafast_mcp_monitoring/
tracing.rs1use tracing::{Level, debug, error, info, warn};
7use tracing_subscriber::{
8 EnvFilter,
9 fmt::{format::FmtSpan, time::UtcTime},
10};
11
12#[derive(Debug, Clone)]
14pub struct TracingConfig {
15 pub enabled: bool,
16 pub service_name: String,
17 pub service_version: String,
18 pub log_level: Level,
19 pub enable_console: bool,
20 pub enable_json: bool,
21 pub enable_otlp: bool,
22 pub otlp_endpoint: Option<String>,
23 pub enable_jaeger: bool,
24 pub jaeger_endpoint: Option<String>,
25 pub sample_rate: f64,
26 pub max_attributes: usize,
27}
28
29impl Default for TracingConfig {
30 fn default() -> Self {
31 Self {
32 enabled: true,
33 service_name: "ultrafast-mcp".to_string(),
34 service_version: "1.0.0".to_string(),
35 log_level: Level::INFO,
36 enable_console: true,
37 enable_json: false,
38 enable_otlp: false,
39 otlp_endpoint: None,
40 enable_jaeger: false,
41 jaeger_endpoint: None,
42 sample_rate: 1.0,
43 max_attributes: 10,
44 }
45 }
46}
47
48pub struct TracingSystem {
50 config: TracingConfig,
51 _guard: Option<()>,
52}
53
54impl TracingSystem {
55 pub fn new(config: TracingConfig) -> Self {
57 Self {
58 config,
59 _guard: None,
60 }
61 }
62
63 pub fn init(config: TracingConfig) -> anyhow::Result<Self> {
65 if !config.enabled {
66 return Ok(Self::new(config));
67 }
68
69 if config.enable_console {
71 tracing_subscriber::fmt()
72 .with_timer(UtcTime::rfc_3339())
73 .with_span_events(FmtSpan::CLOSE)
74 .with_target(false)
75 .with_thread_ids(false)
76 .with_thread_names(false)
77 .with_env_filter(EnvFilter::from_default_env())
78 .init();
79 } else if config.enable_json {
80 tracing_subscriber::fmt()
81 .json()
82 .with_timer(UtcTime::rfc_3339())
83 .with_span_events(FmtSpan::CLOSE)
84 .with_env_filter(EnvFilter::from_default_env())
85 .init();
86 } else {
87 tracing_subscriber::fmt()
88 .with_env_filter(EnvFilter::from_default_env())
89 .init();
90 }
91
92 let guard = ();
93
94 info!(
95 "Tracing system initialized for service: {} v{}",
96 config.service_name, config.service_version
97 );
98
99 Ok(Self {
100 config,
101 _guard: Some(guard),
102 })
103 }
104
105 pub fn config(&self) -> &TracingConfig {
107 &self.config
108 }
109
110 pub fn is_enabled(&self) -> bool {
112 self.config.enabled
113 }
114
115 pub fn span(&self, name: &str) -> tracing::Span {
117 tracing::info_span!("operation", name = name)
118 }
119
120 pub fn span_with_attrs(&self, name: &str, attrs: &[(&str, &str)]) -> tracing::Span {
122 let span = tracing::info_span!("operation", name = name);
123
124 for (_key, _value) in attrs.iter().take(self.config.max_attributes) {
125 }
128
129 span
130 }
131
132 pub fn event(&self, level: Level, message: &str) {
134 match level {
135 Level::ERROR => error!(message),
136 Level::WARN => warn!(message),
137 Level::INFO => info!(message),
138 Level::DEBUG => debug!(message),
139 Level::TRACE => tracing::trace!(message),
140 }
141 }
142
143 pub fn event_with_attrs(&self, level: Level, message: &str, attrs: &[(&str, &str)]) {
145 match level {
146 Level::ERROR => error!(?attrs, message),
147 Level::WARN => warn!(?attrs, message),
148 Level::INFO => info!(?attrs, message),
149 Level::DEBUG => debug!(?attrs, message),
150 Level::TRACE => tracing::trace!(?attrs, message),
151 }
152 }
153}
154
155impl Drop for TracingSystem {
156 fn drop(&mut self) {
157 if self.config.enabled {
158 info!("Shutting down tracing system");
159 }
161 }
162}
163
164pub struct TracingUtils;
166
167impl TracingUtils {
168 pub fn mcp_request_span(method: &str, request_id: &str) -> tracing::Span {
170 tracing::info_span!(
171 "mcp_request",
172 method = method,
173 request_id = request_id,
174 service = "ultrafast-mcp"
175 )
176 }
177
178 pub fn tool_execution_span(tool_name: &str, request_id: &str) -> tracing::Span {
180 tracing::info_span!(
181 "tool_execution",
182 tool_name = tool_name,
183 request_id = request_id,
184 service = "ultrafast-mcp"
185 )
186 }
187
188 pub fn resource_operation_span(operation: &str, uri: &str) -> tracing::Span {
190 tracing::info_span!(
191 "resource_operation",
192 operation = operation,
193 uri = uri,
194 service = "ultrafast-mcp"
195 )
196 }
197
198 pub fn transport_operation_span(operation: &str, transport_type: &str) -> tracing::Span {
200 tracing::info_span!(
201 "transport_operation",
202 operation = operation,
203 transport_type = transport_type,
204 service = "ultrafast-mcp"
205 )
206 }
207
208 pub fn record_request_start(method: &str, request_id: &str) {
210 info!(
211 "Request started method={} request_id={} service=ultrafast-mcp",
212 method, request_id
213 );
214 }
215
216 pub fn record_request_complete(
218 method: &str,
219 request_id: &str,
220 duration_ms: u64,
221 success: bool,
222 ) {
223 if success {
224 info!(
225 "Request completed method={} request_id={} duration_ms={} success={} service=ultrafast-mcp",
226 method, request_id, duration_ms, success
227 );
228 } else {
229 error!(
230 "Request failed method={} request_id={} duration_ms={} success={} service=ultrafast-mcp",
231 method, request_id, duration_ms, success
232 );
233 }
234 }
235
236 pub fn record_tool_execution(
238 tool_name: &str,
239 request_id: &str,
240 duration_ms: u64,
241 success: bool,
242 ) {
243 if success {
244 info!(
245 "Tool execution completed tool_name={} request_id={} duration_ms={} success={} service=ultrafast-mcp",
246 tool_name, request_id, duration_ms, success
247 );
248 } else {
249 error!(
250 "Tool execution failed tool_name={} request_id={} duration_ms={} success={} service=ultrafast-mcp",
251 tool_name, request_id, duration_ms, success
252 );
253 }
254 }
255
256 pub fn record_transport_event(
258 event_type: &str,
259 transport_type: &str,
260 bytes: Option<u64>,
261 error: Option<&str>,
262 ) {
263 let error_str = error.unwrap_or("none");
264 let bytes_str = bytes
265 .map(|b| b.to_string())
266 .unwrap_or_else(|| "none".to_string());
267
268 if error.is_some() {
269 error!(
270 "Transport event event_type={} transport_type={} bytes={} error={} service=ultrafast-mcp",
271 event_type, transport_type, bytes_str, error_str
272 );
273 } else {
274 debug!(
275 "Transport event event_type={} transport_type={} bytes={} error={} service=ultrafast-mcp",
276 event_type, transport_type, bytes_str, error_str
277 );
278 }
279 }
280}
281
282#[macro_export]
284macro_rules! trace_span {
285 ($name:expr) => {
286 let _span = tracing::info_span!($name);
287 let _enter = _span.enter();
288 };
289 ($name:expr, $($key:ident = $val:expr),*) => {
290 let _span = tracing::info_span!($name, $($key = $val),*);
291 let _enter = _span.enter();
292 };
293}
294
295#[macro_export]
297macro_rules! trace_function {
298 ($func_name:expr) => {
299 let _span = tracing::info_span!("function", name = $func_name);
300 let _enter = _span.enter();
301 };
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307
308 #[test]
309 fn test_tracing_config_default() {
310 let config = TracingConfig::default();
311 assert!(config.enabled);
312 assert_eq!(config.service_name, "ultrafast-mcp");
313 assert_eq!(config.service_version, "1.0.0");
314 assert_eq!(config.log_level, Level::INFO);
315 assert!(config.enable_console);
316 assert!(!config.enable_json);
317 assert!(!config.enable_otlp);
318 assert!(!config.enable_jaeger);
319 }
320
321 #[test]
322 fn test_tracing_config_custom() {
323 let config = TracingConfig {
324 service_name: "test-service".to_string(),
325 service_version: "2.0.0".to_string(),
326 log_level: Level::DEBUG,
327 enable_json: true,
328 ..Default::default()
329 };
330 assert_eq!(config.service_name, "test-service");
331 assert_eq!(config.service_version, "2.0.0");
332 assert_eq!(config.log_level, Level::DEBUG);
333 assert!(config.enable_json);
334 }
335
336 #[test]
337 fn test_tracing_system_creation() {
338 let config = TracingConfig::default();
339 let system = TracingSystem::new(config);
340
341 assert!(system.is_enabled());
342 assert_eq!(system.config().service_name, "ultrafast-mcp");
343 }
344
345 #[test]
346 fn test_tracing_utils_spans() {
347 let span = TracingUtils::mcp_request_span("test_method", "test_id");
348 assert_eq!(
349 span.metadata().map(|m| m.name()).unwrap_or("unknown"),
350 "mcp_request"
351 );
352
353 let span = TracingUtils::tool_execution_span("test_tool", "test_id");
354 assert_eq!(
355 span.metadata().map(|m| m.name()).unwrap_or("unknown"),
356 "tool_execution"
357 );
358
359 let span = TracingUtils::resource_operation_span("read", "test://uri");
360 assert_eq!(
361 span.metadata().map(|m| m.name()).unwrap_or("unknown"),
362 "resource_operation"
363 );
364
365 let span = TracingUtils::transport_operation_span("send", "http");
366 assert_eq!(
367 span.metadata().map(|m| m.name()).unwrap_or("unknown"),
368 "transport_operation"
369 );
370 }
371
372 #[test]
373 fn test_tracing_utils_events() {
374 TracingUtils::record_request_start("test_method", "test_id");
376 TracingUtils::record_request_complete("test_method", "test_id", 100, true);
377 TracingUtils::record_tool_execution("test_tool", "test_id", 50, true);
378 TracingUtils::record_transport_event("send", "http", Some(1024), None);
379 }
380}