things3_cli/mcp/middleware/
logging.rs1use super::{McpMiddleware, MiddlewareContext, MiddlewareResult};
4use crate::mcp::{CallToolRequest, CallToolResult, McpError, McpResult};
5
6pub struct LoggingMiddleware {
7 level: LogLevel,
8}
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum LogLevel {
12 Debug,
13 Info,
14 Warn,
15 Error,
16}
17
18impl LoggingMiddleware {
19 #[must_use]
21 pub fn new(level: LogLevel) -> Self {
22 Self { level }
23 }
24
25 #[must_use]
27 pub fn debug() -> Self {
28 Self::new(LogLevel::Debug)
29 }
30
31 #[must_use]
33 pub fn info() -> Self {
34 Self::new(LogLevel::Info)
35 }
36
37 #[must_use]
39 pub fn warn() -> Self {
40 Self::new(LogLevel::Warn)
41 }
42
43 #[must_use]
45 pub fn error() -> Self {
46 Self::new(LogLevel::Error)
47 }
48
49 pub(super) fn should_log(&self, level: LogLevel) -> bool {
50 matches!(
51 (self.level, level),
52 (LogLevel::Debug, _)
53 | (
54 LogLevel::Info,
55 LogLevel::Info | LogLevel::Warn | LogLevel::Error
56 )
57 | (LogLevel::Warn, LogLevel::Warn | LogLevel::Error)
58 | (LogLevel::Error, LogLevel::Error)
59 )
60 }
61
62 fn log(&self, level: LogLevel, message: &str) {
63 if self.should_log(level) {
64 match level {
65 LogLevel::Debug => println!("[DEBUG] {message}"),
66 LogLevel::Info => println!("[INFO] {message}"),
67 LogLevel::Warn => println!("[WARN] {message}"),
68 LogLevel::Error => println!("[ERROR] {message}"),
69 }
70 }
71 }
72}
73
74#[async_trait::async_trait]
75impl McpMiddleware for LoggingMiddleware {
76 fn name(&self) -> &'static str {
77 "logging"
78 }
79
80 fn priority(&self) -> i32 {
81 100 }
83
84 async fn before_request(
85 &self,
86 request: &CallToolRequest,
87 context: &mut MiddlewareContext,
88 ) -> McpResult<MiddlewareResult> {
89 self.log(
90 LogLevel::Info,
91 &format!(
92 "Request started: {} (ID: {})",
93 request.name, context.request_id
94 ),
95 );
96 Ok(MiddlewareResult::Continue)
97 }
98
99 async fn after_request(
100 &self,
101 request: &CallToolRequest,
102 response: &mut CallToolResult,
103 context: &mut MiddlewareContext,
104 ) -> McpResult<MiddlewareResult> {
105 let elapsed = context.elapsed();
106 let status = if response.is_error {
107 "ERROR"
108 } else {
109 "SUCCESS"
110 };
111
112 self.log(
113 LogLevel::Info,
114 &format!(
115 "Request completed: {} (ID: {}) - {} in {:?}",
116 request.name, context.request_id, status, elapsed
117 ),
118 );
119 Ok(MiddlewareResult::Continue)
120 }
121
122 async fn on_error(
123 &self,
124 request: &CallToolRequest,
125 error: &McpError,
126 context: &mut MiddlewareContext,
127 ) -> McpResult<MiddlewareResult> {
128 self.log(
129 LogLevel::Error,
130 &format!(
131 "Request failed: {} (ID: {}) - {}",
132 request.name, context.request_id, error
133 ),
134 );
135 Ok(MiddlewareResult::Continue)
136 }
137}