mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-06-30 05:49:36 +00:00
694 lines
20 KiB
Rust
694 lines
20 KiB
Rust
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::{HashMap, VecDeque};
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
use tokio::sync::RwLock;
|
|
|
|
/// Debug feature types
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum DebugFeature {
|
|
APITesting,
|
|
PerformanceMonitoring,
|
|
MemoryProfiling,
|
|
NetworkInspector,
|
|
EventLogger,
|
|
StateInspector,
|
|
LogViewer,
|
|
CrashReporter,
|
|
BenchmarkRunner,
|
|
DiagnosticReport,
|
|
}
|
|
|
|
/// Debug log level
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub enum LogLevel {
|
|
Trace,
|
|
Debug,
|
|
Info,
|
|
Warn,
|
|
Error,
|
|
}
|
|
|
|
/// Debug log entry
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct LogEntry {
|
|
pub timestamp: DateTime<Utc>,
|
|
pub level: LogLevel,
|
|
pub component: String,
|
|
pub message: String,
|
|
pub metadata: HashMap<String, serde_json::Value>,
|
|
}
|
|
|
|
/// Performance metric
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PerformanceMetric {
|
|
pub name: String,
|
|
pub value: f64,
|
|
pub unit: String,
|
|
pub timestamp: DateTime<Utc>,
|
|
pub tags: HashMap<String, String>,
|
|
}
|
|
|
|
/// Memory snapshot
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct MemorySnapshot {
|
|
pub timestamp: DateTime<Utc>,
|
|
pub heap_used_mb: f64,
|
|
pub heap_total_mb: f64,
|
|
pub external_mb: f64,
|
|
pub process_rss_mb: f64,
|
|
pub details: HashMap<String, f64>,
|
|
}
|
|
|
|
/// Network request log
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct NetworkRequest {
|
|
pub id: String,
|
|
pub timestamp: DateTime<Utc>,
|
|
pub method: String,
|
|
pub url: String,
|
|
pub status: Option<u16>,
|
|
pub duration_ms: Option<u64>,
|
|
pub request_headers: HashMap<String, String>,
|
|
pub response_headers: HashMap<String, String>,
|
|
pub request_body: Option<String>,
|
|
pub response_body: Option<String>,
|
|
pub error: Option<String>,
|
|
}
|
|
|
|
/// API test case
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct APITestCase {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub endpoint: String,
|
|
pub method: String,
|
|
pub headers: HashMap<String, String>,
|
|
pub body: Option<serde_json::Value>,
|
|
pub expected_status: u16,
|
|
pub expected_body: Option<serde_json::Value>,
|
|
pub timeout_ms: u64,
|
|
}
|
|
|
|
/// API test result
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct APITestResult {
|
|
pub test_id: String,
|
|
pub success: bool,
|
|
pub actual_status: Option<u16>,
|
|
pub actual_body: Option<serde_json::Value>,
|
|
pub duration_ms: u64,
|
|
pub error: Option<String>,
|
|
pub timestamp: DateTime<Utc>,
|
|
}
|
|
|
|
/// Benchmark configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct BenchmarkConfig {
|
|
pub name: String,
|
|
pub iterations: u32,
|
|
pub warmup_iterations: u32,
|
|
pub timeout_ms: u64,
|
|
pub collect_memory: bool,
|
|
pub collect_cpu: bool,
|
|
}
|
|
|
|
/// Benchmark result
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct BenchmarkResult {
|
|
pub name: String,
|
|
pub iterations: u32,
|
|
pub mean_ms: f64,
|
|
pub median_ms: f64,
|
|
pub min_ms: f64,
|
|
pub max_ms: f64,
|
|
pub std_dev_ms: f64,
|
|
pub memory_usage_mb: Option<f64>,
|
|
pub cpu_usage_percent: Option<f64>,
|
|
pub timestamp: DateTime<Utc>,
|
|
}
|
|
|
|
/// Diagnostic report
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct DiagnosticReport {
|
|
pub timestamp: DateTime<Utc>,
|
|
pub system_info: SystemInfo,
|
|
pub app_info: AppInfo,
|
|
pub performance_summary: PerformanceSummary,
|
|
pub error_summary: ErrorSummary,
|
|
pub recommendations: Vec<String>,
|
|
}
|
|
|
|
/// System information
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SystemInfo {
|
|
pub os: String,
|
|
pub arch: String,
|
|
pub cpu_count: usize,
|
|
pub total_memory_mb: u64,
|
|
pub available_memory_mb: u64,
|
|
pub disk_space_mb: u64,
|
|
pub node_version: Option<String>,
|
|
pub rust_version: String,
|
|
}
|
|
|
|
/// Application information
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct AppInfo {
|
|
pub version: String,
|
|
pub build_date: String,
|
|
pub uptime_seconds: u64,
|
|
pub active_sessions: usize,
|
|
pub total_requests: u64,
|
|
pub error_count: u64,
|
|
}
|
|
|
|
/// Performance summary
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PerformanceSummary {
|
|
pub avg_response_time_ms: f64,
|
|
pub p95_response_time_ms: f64,
|
|
pub p99_response_time_ms: f64,
|
|
pub requests_per_second: f64,
|
|
pub cpu_usage_percent: f64,
|
|
pub memory_usage_mb: f64,
|
|
}
|
|
|
|
/// Error summary
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ErrorSummary {
|
|
pub total_errors: u64,
|
|
pub errors_by_type: HashMap<String, u64>,
|
|
pub recent_errors: Vec<LogEntry>,
|
|
}
|
|
|
|
/// Debug settings
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct DebugSettings {
|
|
pub enabled: bool,
|
|
pub log_level: LogLevel,
|
|
pub max_log_entries: usize,
|
|
pub enable_performance_monitoring: bool,
|
|
pub enable_memory_profiling: bool,
|
|
pub enable_network_inspector: bool,
|
|
pub enable_crash_reporting: bool,
|
|
pub log_to_file: bool,
|
|
pub log_file_path: Option<PathBuf>,
|
|
pub performance_sample_interval_ms: u64,
|
|
pub memory_sample_interval_ms: u64,
|
|
}
|
|
|
|
impl Default for DebugSettings {
|
|
fn default() -> Self {
|
|
Self {
|
|
enabled: cfg!(debug_assertions),
|
|
log_level: LogLevel::Info,
|
|
max_log_entries: 10000,
|
|
enable_performance_monitoring: false,
|
|
enable_memory_profiling: false,
|
|
enable_network_inspector: false,
|
|
enable_crash_reporting: true,
|
|
log_to_file: false,
|
|
log_file_path: None,
|
|
performance_sample_interval_ms: 1000,
|
|
memory_sample_interval_ms: 5000,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Debug features manager
|
|
pub struct DebugFeaturesManager {
|
|
settings: Arc<RwLock<DebugSettings>>,
|
|
logs: Arc<RwLock<VecDeque<LogEntry>>>,
|
|
performance_metrics: Arc<RwLock<VecDeque<PerformanceMetric>>>,
|
|
memory_snapshots: Arc<RwLock<VecDeque<MemorySnapshot>>>,
|
|
network_requests: Arc<RwLock<HashMap<String, NetworkRequest>>>,
|
|
api_test_results: Arc<RwLock<HashMap<String, Vec<APITestResult>>>>,
|
|
benchmark_results: Arc<RwLock<Vec<BenchmarkResult>>>,
|
|
notification_manager: Option<Arc<crate::notification_manager::NotificationManager>>,
|
|
}
|
|
|
|
impl Default for DebugFeaturesManager {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl DebugFeaturesManager {
|
|
/// Create a new debug features manager
|
|
pub fn new() -> Self {
|
|
Self {
|
|
settings: Arc::new(RwLock::new(DebugSettings::default())),
|
|
logs: Arc::new(RwLock::new(VecDeque::new())),
|
|
performance_metrics: Arc::new(RwLock::new(VecDeque::new())),
|
|
memory_snapshots: Arc::new(RwLock::new(VecDeque::new())),
|
|
network_requests: Arc::new(RwLock::new(HashMap::new())),
|
|
api_test_results: Arc::new(RwLock::new(HashMap::new())),
|
|
benchmark_results: Arc::new(RwLock::new(Vec::new())),
|
|
notification_manager: None,
|
|
}
|
|
}
|
|
|
|
/// Set the notification manager
|
|
pub fn set_notification_manager(
|
|
&mut self,
|
|
notification_manager: Arc<crate::notification_manager::NotificationManager>,
|
|
) {
|
|
self.notification_manager = Some(notification_manager);
|
|
}
|
|
|
|
/// Get debug settings
|
|
pub async fn get_settings(&self) -> DebugSettings {
|
|
self.settings.read().await.clone()
|
|
}
|
|
|
|
/// Update debug settings
|
|
pub async fn update_settings(&self, settings: DebugSettings) {
|
|
*self.settings.write().await = settings;
|
|
}
|
|
|
|
/// Log a message
|
|
pub async fn log(
|
|
&self,
|
|
level: LogLevel,
|
|
component: &str,
|
|
message: &str,
|
|
metadata: HashMap<String, serde_json::Value>,
|
|
) {
|
|
let settings = self.settings.read().await;
|
|
|
|
// Check if logging is enabled and level is appropriate
|
|
if !settings.enabled || level < settings.log_level {
|
|
return;
|
|
}
|
|
|
|
let entry = LogEntry {
|
|
timestamp: Utc::now(),
|
|
level,
|
|
component: component.to_string(),
|
|
message: message.to_string(),
|
|
metadata,
|
|
};
|
|
|
|
// Add to in-memory log
|
|
let mut logs = self.logs.write().await;
|
|
logs.push_back(entry.clone());
|
|
|
|
// Limit log size
|
|
while logs.len() > settings.max_log_entries {
|
|
logs.pop_front();
|
|
}
|
|
|
|
// Log to file if enabled
|
|
if settings.log_to_file {
|
|
if let Some(path) = &settings.log_file_path {
|
|
let _ = self.write_log_to_file(&entry, path).await;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Record a performance metric
|
|
pub async fn record_metric(
|
|
&self,
|
|
name: &str,
|
|
value: f64,
|
|
unit: &str,
|
|
tags: HashMap<String, String>,
|
|
) {
|
|
let settings = self.settings.read().await;
|
|
|
|
if !settings.enabled || !settings.enable_performance_monitoring {
|
|
return;
|
|
}
|
|
|
|
let metric = PerformanceMetric {
|
|
name: name.to_string(),
|
|
value,
|
|
unit: unit.to_string(),
|
|
timestamp: Utc::now(),
|
|
tags,
|
|
};
|
|
|
|
let mut metrics = self.performance_metrics.write().await;
|
|
metrics.push_back(metric);
|
|
|
|
// Keep only last 1000 metrics
|
|
while metrics.len() > 1000 {
|
|
metrics.pop_front();
|
|
}
|
|
}
|
|
|
|
/// Take a memory snapshot
|
|
pub async fn take_memory_snapshot(&self) -> Result<MemorySnapshot, String> {
|
|
let settings = self.settings.read().await;
|
|
|
|
if !settings.enabled || !settings.enable_memory_profiling {
|
|
return Err("Memory profiling is disabled".to_string());
|
|
}
|
|
|
|
// TODO: Implement actual memory profiling
|
|
let snapshot = MemorySnapshot {
|
|
timestamp: Utc::now(),
|
|
heap_used_mb: 0.0,
|
|
heap_total_mb: 0.0,
|
|
external_mb: 0.0,
|
|
process_rss_mb: 0.0,
|
|
details: HashMap::new(),
|
|
};
|
|
|
|
let mut snapshots = self.memory_snapshots.write().await;
|
|
snapshots.push_back(snapshot.clone());
|
|
|
|
// Keep only last 100 snapshots
|
|
while snapshots.len() > 100 {
|
|
snapshots.pop_front();
|
|
}
|
|
|
|
Ok(snapshot)
|
|
}
|
|
|
|
/// Log a network request
|
|
#[allow(dead_code)]
|
|
pub async fn log_network_request(&self, request: NetworkRequest) {
|
|
let settings = self.settings.read().await;
|
|
|
|
if !settings.enabled || !settings.enable_network_inspector {
|
|
return;
|
|
}
|
|
|
|
let mut requests = self.network_requests.write().await;
|
|
requests.insert(request.id.clone(), request);
|
|
|
|
// Keep only last 500 requests
|
|
if requests.len() > 500 {
|
|
// Remove oldest entries
|
|
let mut ids: Vec<_> = requests.keys().cloned().collect();
|
|
ids.sort();
|
|
for id in ids.iter().take(requests.len() - 500) {
|
|
requests.remove(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Run API tests
|
|
pub async fn run_api_tests(&self, tests: Vec<APITestCase>) -> Vec<APITestResult> {
|
|
let mut results = Vec::new();
|
|
|
|
for test in tests {
|
|
let result = self.run_single_api_test(&test).await;
|
|
results.push(result.clone());
|
|
|
|
// Store result
|
|
let mut test_results = self.api_test_results.write().await;
|
|
test_results
|
|
.entry(test.id.clone())
|
|
.or_insert_with(Vec::new)
|
|
.push(result);
|
|
}
|
|
|
|
results
|
|
}
|
|
|
|
/// Run a single API test
|
|
async fn run_single_api_test(&self, test: &APITestCase) -> APITestResult {
|
|
let start = std::time::Instant::now();
|
|
|
|
// TODO: Implement actual API testing
|
|
let duration_ms = start.elapsed().as_millis() as u64;
|
|
|
|
APITestResult {
|
|
test_id: test.id.clone(),
|
|
success: false,
|
|
actual_status: None,
|
|
actual_body: None,
|
|
duration_ms,
|
|
error: Some("API testing not yet implemented".to_string()),
|
|
timestamp: Utc::now(),
|
|
}
|
|
}
|
|
|
|
/// Run benchmarks
|
|
pub async fn run_benchmarks(&self, configs: Vec<BenchmarkConfig>) -> Vec<BenchmarkResult> {
|
|
let mut results = Vec::new();
|
|
|
|
for config in configs {
|
|
let result = self.run_single_benchmark(&config).await;
|
|
results.push(result.clone());
|
|
|
|
// Store result
|
|
self.benchmark_results.write().await.push(result);
|
|
}
|
|
|
|
results
|
|
}
|
|
|
|
/// Run a single benchmark
|
|
async fn run_single_benchmark(&self, config: &BenchmarkConfig) -> BenchmarkResult {
|
|
// TODO: Implement actual benchmarking
|
|
BenchmarkResult {
|
|
name: config.name.clone(),
|
|
iterations: config.iterations,
|
|
mean_ms: 0.0,
|
|
median_ms: 0.0,
|
|
min_ms: 0.0,
|
|
max_ms: 0.0,
|
|
std_dev_ms: 0.0,
|
|
memory_usage_mb: None,
|
|
cpu_usage_percent: None,
|
|
timestamp: Utc::now(),
|
|
}
|
|
}
|
|
|
|
/// Generate diagnostic report
|
|
pub async fn generate_diagnostic_report(&self) -> DiagnosticReport {
|
|
let system_info = self.get_system_info().await;
|
|
let app_info = self.get_app_info().await;
|
|
let performance_summary = self.get_performance_summary().await;
|
|
let error_summary = self.get_error_summary().await;
|
|
let recommendations = self.generate_recommendations(
|
|
&system_info,
|
|
&app_info,
|
|
&performance_summary,
|
|
&error_summary,
|
|
);
|
|
|
|
DiagnosticReport {
|
|
timestamp: Utc::now(),
|
|
system_info,
|
|
app_info,
|
|
performance_summary,
|
|
error_summary,
|
|
recommendations,
|
|
}
|
|
}
|
|
|
|
/// Get recent logs
|
|
pub async fn get_logs(&self, limit: Option<usize>, level: Option<LogLevel>) -> Vec<LogEntry> {
|
|
let logs = self.logs.read().await;
|
|
let iter = logs.iter().rev();
|
|
|
|
let filtered: Vec<_> = if let Some(min_level) = level {
|
|
iter.filter(|log| log.level >= min_level).cloned().collect()
|
|
} else {
|
|
iter.cloned().collect()
|
|
};
|
|
|
|
match limit {
|
|
Some(n) => filtered.into_iter().take(n).collect(),
|
|
None => filtered,
|
|
}
|
|
}
|
|
|
|
/// Get performance metrics
|
|
pub async fn get_performance_metrics(&self, limit: Option<usize>) -> Vec<PerformanceMetric> {
|
|
let metrics = self.performance_metrics.read().await;
|
|
match limit {
|
|
Some(n) => metrics.iter().rev().take(n).cloned().collect(),
|
|
None => metrics.iter().cloned().collect(),
|
|
}
|
|
}
|
|
|
|
/// Get memory snapshots
|
|
pub async fn get_memory_snapshots(&self, limit: Option<usize>) -> Vec<MemorySnapshot> {
|
|
let snapshots = self.memory_snapshots.read().await;
|
|
match limit {
|
|
Some(n) => snapshots.iter().rev().take(n).cloned().collect(),
|
|
None => snapshots.iter().cloned().collect(),
|
|
}
|
|
}
|
|
|
|
/// Get network requests
|
|
pub async fn get_network_requests(&self, limit: Option<usize>) -> Vec<NetworkRequest> {
|
|
let requests = self.network_requests.read().await;
|
|
let mut sorted: Vec<_> = requests.values().cloned().collect();
|
|
sorted.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
|
|
|
|
match limit {
|
|
Some(n) => sorted.into_iter().take(n).collect(),
|
|
None => sorted,
|
|
}
|
|
}
|
|
|
|
/// Clear all debug data
|
|
pub async fn clear_all_data(&self) {
|
|
self.logs.write().await.clear();
|
|
self.performance_metrics.write().await.clear();
|
|
self.memory_snapshots.write().await.clear();
|
|
self.network_requests.write().await.clear();
|
|
self.api_test_results.write().await.clear();
|
|
self.benchmark_results.write().await.clear();
|
|
}
|
|
|
|
/// Enable/disable debug mode
|
|
pub async fn set_debug_mode(&self, enabled: bool) {
|
|
self.settings.write().await.enabled = enabled;
|
|
|
|
if let Some(notification_manager) = &self.notification_manager {
|
|
let message = if enabled {
|
|
"Debug mode enabled"
|
|
} else {
|
|
"Debug mode disabled"
|
|
};
|
|
let _ = notification_manager
|
|
.notify_success("Debug Mode", message)
|
|
.await;
|
|
}
|
|
}
|
|
|
|
// Helper methods
|
|
async fn write_log_to_file(&self, entry: &LogEntry, path: &PathBuf) -> Result<(), String> {
|
|
use tokio::io::AsyncWriteExt;
|
|
|
|
let log_line = format!(
|
|
"[{}] [{}] [{}] {}\n",
|
|
entry.timestamp.format("%Y-%m-%d %H:%M:%S%.3f"),
|
|
format!("{:?}", entry.level),
|
|
entry.component,
|
|
entry.message
|
|
);
|
|
|
|
let mut file = tokio::fs::OpenOptions::new()
|
|
.create(true)
|
|
.append(true)
|
|
.open(path)
|
|
.await
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
file.write_all(log_line.as_bytes())
|
|
.await
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn get_system_info(&self) -> SystemInfo {
|
|
SystemInfo {
|
|
os: std::env::consts::OS.to_string(),
|
|
arch: std::env::consts::ARCH.to_string(),
|
|
cpu_count: num_cpus::get(),
|
|
total_memory_mb: 0, // TODO: Get actual memory
|
|
available_memory_mb: 0,
|
|
disk_space_mb: 0,
|
|
node_version: None,
|
|
rust_version: "1.70.0".to_string(), // TODO: Get actual rust version
|
|
}
|
|
}
|
|
|
|
async fn get_app_info(&self) -> AppInfo {
|
|
AppInfo {
|
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
|
build_date: chrono::Utc::now().to_rfc3339(), // TODO: Get actual build date
|
|
uptime_seconds: 0, // TODO: Track uptime
|
|
active_sessions: 0,
|
|
total_requests: 0,
|
|
error_count: 0,
|
|
}
|
|
}
|
|
|
|
async fn get_performance_summary(&self) -> PerformanceSummary {
|
|
PerformanceSummary {
|
|
avg_response_time_ms: 0.0,
|
|
p95_response_time_ms: 0.0,
|
|
p99_response_time_ms: 0.0,
|
|
requests_per_second: 0.0,
|
|
cpu_usage_percent: 0.0,
|
|
memory_usage_mb: 0.0,
|
|
}
|
|
}
|
|
|
|
async fn get_error_summary(&self) -> ErrorSummary {
|
|
let logs = self.logs.read().await;
|
|
let errors: Vec<_> = logs
|
|
.iter()
|
|
.filter(|log| log.level == LogLevel::Error)
|
|
.cloned()
|
|
.collect();
|
|
|
|
let mut errors_by_type = HashMap::new();
|
|
for error in &errors {
|
|
let error_type = error
|
|
.metadata
|
|
.get("type")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("unknown")
|
|
.to_string();
|
|
*errors_by_type.entry(error_type).or_insert(0) += 1;
|
|
}
|
|
|
|
ErrorSummary {
|
|
total_errors: errors.len() as u64,
|
|
errors_by_type,
|
|
recent_errors: errors.into_iter().rev().take(10).collect(),
|
|
}
|
|
}
|
|
|
|
fn generate_recommendations(
|
|
&self,
|
|
system: &SystemInfo,
|
|
_app: &AppInfo,
|
|
perf: &PerformanceSummary,
|
|
errors: &ErrorSummary,
|
|
) -> Vec<String> {
|
|
let mut recommendations = Vec::new();
|
|
|
|
if perf.cpu_usage_percent > 80.0 {
|
|
recommendations.push(
|
|
"High CPU usage detected. Consider optimizing performance-critical code."
|
|
.to_string(),
|
|
);
|
|
}
|
|
|
|
if perf.memory_usage_mb > (system.total_memory_mb as f64 * 0.8) {
|
|
recommendations.push("High memory usage detected. Check for memory leaks.".to_string());
|
|
}
|
|
|
|
if errors.total_errors > 100 {
|
|
recommendations
|
|
.push("High error rate detected. Review error logs for patterns.".to_string());
|
|
}
|
|
|
|
if perf.avg_response_time_ms > 1000.0 {
|
|
recommendations.push(
|
|
"Slow response times detected. Consider caching or query optimization.".to_string(),
|
|
);
|
|
}
|
|
|
|
recommendations
|
|
}
|
|
}
|
|
|
|
/// Debug statistics
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct DebugStats {
|
|
pub total_logs: usize,
|
|
pub logs_by_level: HashMap<String, usize>,
|
|
pub total_metrics: usize,
|
|
pub total_snapshots: usize,
|
|
pub total_requests: usize,
|
|
pub total_test_results: usize,
|
|
pub total_benchmarks: usize,
|
|
}
|
|
|
|
// Re-export num_cpus if needed
|
|
extern crate num_cpus;
|