use serde_json::json;
use std::sync::{Arc, Mutex};
use tracing::{Metadata, Subscriber};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
mod mcp_test_helpers;
use mcp_test_helpers::*;
struct LogCapture {
logs: Arc<Mutex<Vec<String>>>,
}
impl LogCapture {
fn new() -> (Self, Arc<Mutex<Vec<String>>>) {
let logs = Arc::new(Mutex::new(Vec::new()));
(Self { logs: logs.clone() }, logs)
}
}
impl<S> Layer<S> for LogCapture
where
S: Subscriber,
{
fn on_event(
&self,
event: &tracing::Event<'_>,
_ctx: tracing_subscriber::layer::Context<'_, S>,
) {
let mut visitor = LogVisitor::new();
event.record(&mut visitor);
if let Ok(mut logs) = self.logs.lock() {
logs.push(visitor.message);
}
}
}
struct LogVisitor {
message: String,
}
impl LogVisitor {
fn new() -> Self {
Self {
message: String::new(),
}
}
}
impl tracing::field::Visit for LogVisitor {
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
if field.name() == "message" {
self.message = format!("{:?}", value);
}
}
}
#[tokio::test]
async fn test_excessive_debug_logging_volume() {
let (log_capture, captured_logs) = LogCapture::new();
let subscriber = tracing_subscriber::registry()
.with(log_capture)
.with(tracing_subscriber::filter::LevelFilter::DEBUG);
let _guard = tracing::subscriber::set_default(subscriber);
let test_server = McpTestServer::start().await.expect("Failed to start test server");
let port = test_server.port;
let test_message = json!({
"jsonrpc": "2.0",
"id": "debug-volume-test",
"method": "tools/call",
"params": {
"name": "test_tool",
"arguments": {"param": "value"},
"_meta": {
"progressToken": "progress-123"
}
}
});
let client = reqwest::Client::new();
let url = format!("http://127.0.0.1:{}/mcp", port);
let _response = client
.post(&url)
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.header("User-Agent", "Cursor-Test-Client/1.0")
.json(&test_message)
.send()
.await
.expect("Failed to send request");
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let logs = captured_logs.lock().unwrap();
let total_logs = logs.len();
let total_log_chars: usize = logs.iter().map(|log| log.len()).sum();
println!("=== DEBUG LOGGING ANALYSIS ===");
println!("Total log entries: {}", total_logs);
println!("Total log characters: {}", total_log_chars);
println!("Average log entry size: {:.1} chars",
if total_logs > 0 { total_log_chars as f64 / total_logs as f64 } else { 0.0 });
let emoji_logs = logs.iter().filter(|log| log.contains("🚀") || log.contains("🔍") || log.contains("📊")).count();
let debug_analysis_logs = logs.iter().filter(|log| log.contains("=== MCP REQUEST ANALYSIS")).count();
let session_debug_logs = logs.iter().filter(|log| log.contains("SESSION DEBUG")).count();
let size_analysis_logs = logs.iter().filter(|log| log.contains("REQUEST SIZE ANALYSIS")).count();
println!("Emoji-decorated logs: {}", emoji_logs);
println!("Request analysis blocks: {}", debug_analysis_logs);
println!("Session debug blocks: {}", session_debug_logs);
println!("Size analysis blocks: {}", size_analysis_logs);
println!("\n=== SAMPLE OF VERBOSE LOGS ===");
for (i, log) in logs.iter().take(10).enumerate() {
println!("{}: {}", i + 1, log);
}
let max_reasonable_logs = 20; let max_reasonable_chars = 2000;
if total_logs > max_reasonable_logs {
println!("❌ EXCESSIVE LOGGING DETECTED!");
println!(" {} log entries for a single HTTP request", total_logs);
println!(" This excessive logging is the root cause of the debug pollution warning");
}
if total_log_chars > max_reasonable_chars {
println!("❌ EXCESSIVE LOG VOLUME DETECTED!");
println!(" {} characters of logs for a single HTTP request", total_log_chars);
println!(" This creates massive debug sections that trigger the 5000+ byte warning");
}
assert!(total_logs <= max_reasonable_logs,
"Excessive logging: {} log entries for a single request (max reasonable: {}). \
This is the root cause of debug pollution.", total_logs, max_reasonable_logs);
assert!(total_log_chars <= max_reasonable_chars,
"Excessive log volume: {} characters for a single request (max reasonable: {}). \
This creates the 'LARGE DEBUG SECTION DETECTED' warning.",
total_log_chars, max_reasonable_chars);
}
#[tokio::test]
async fn test_debug_section_threshold() {
let test_content = format!("debug {}", "x".repeat(5000));
let triggers_warning = test_content.contains("debug") && test_content.len() > 5000;
assert!(triggers_warning, "Should trigger the LARGE DEBUG SECTION DETECTED warning");
println!("✅ Confirmed: content with 'debug' over 5000 bytes triggers warning");
println!(" Test content size: {} bytes", test_content.len());
println!(" Contains 'debug': {}", test_content.contains("debug"));
}