use crate::services::telemetry_service::{telemetry, OperationMetrics, TelemetryInput};
use anyhow::Result;
use serde_json::json;
use std::collections::HashMap;
use std::time::Instant;
use tracing::{debug, info};
pub async fn handle_telemetry(
system: bool,
service: Option<String>,
reset: bool,
test_event: bool,
) -> Result<()> {
let start_time = Instant::now();
if reset {
return handle_reset_command().await;
}
if test_event {
return handle_test_event_command().await;
}
handle_display_command(system, service).await?;
let _ = record_telemetry_command_execution(start_time).await;
Ok(())
}
async fn handle_reset_command() -> Result<()> {
#[cfg(test)]
{
telemetry().reset();
println!("🔄 Telemetry data reset successfully");
Ok(())
}
#[cfg(not(test))]
{
println!("⚠️ Telemetry reset is only available in test builds");
Ok(())
}
}
async fn handle_test_event_command() -> Result<()> {
record_test_telemetry_event().await?;
println!("✅ Test telemetry event recorded successfully");
Ok(())
}
async fn handle_display_command(system: bool, service: Option<String>) -> Result<()> {
match (system, service) {
(_, Some(service_name)) => show_service_telemetry(&service_name).await,
(true, None) => show_system_telemetry().await,
(false, None) => show_system_overview().await,
}
}
async fn show_system_telemetry() -> Result<()> {
info!("📊 Generating system telemetry report");
let telemetry_service = telemetry();
let system_data = telemetry_service.get_system_telemetry().await?;
println!("🔍 PMAT System Telemetry Report");
println!("{}", "=".repeat(50));
println!();
println!("📊 System Overview:");
println!(" Uptime: {} seconds", system_data.uptime_seconds);
println!(
" Total Operations: {}",
system_data.system_metrics.total_operations
);
println!(
" Success Rate: {:.2}%",
system_data.system_metrics.success_rate * 100.0
);
println!(
" Average Duration: {} ms",
system_data.system_metrics.avg_duration_ms
);
println!(
" Total Items Processed: {}",
system_data.system_metrics.total_items_processed
);
println!();
if !system_data.services.is_empty() {
println!("🔧 Service Breakdown:");
for (service_name, service_data) in &system_data.services {
println!(" 📦 {service_name}");
println!(
" Operations: {} (Success: {}, Failed: {})",
service_data.total_operations,
service_data.successful_operations,
service_data.failed_operations
);
println!(
" Success Rate: {:.2}%",
service_data.success_rate * 100.0
);
println!(" Avg Duration: {} ms", service_data.avg_duration_ms);
println!(
" Items Processed: {}",
service_data.total_items_processed
);
if !service_data.operation_counts.is_empty() {
println!(" Top Operations:");
let mut ops: Vec<_> = service_data.operation_counts.iter().collect();
ops.sort_by(|a, b| b.1.cmp(a.1));
for (op, count) in ops.iter().take(3) {
println!(" - {op}: {count} times");
}
}
println!();
}
}
println!("📄 Raw Data (JSON):");
println!("{}", serde_json::to_string_pretty(&system_data)?);
Ok(())
}
async fn show_service_telemetry(service_name: &str) -> Result<()> {
info!(service = %service_name, "📊 Generating service telemetry report");
let telemetry_service = telemetry();
if let Some(service_data) = telemetry_service.get_service_telemetry(service_name).await {
println!("🔍 Service Telemetry: {service_name}");
println!("{}", "=".repeat(50));
println!();
println!("📊 Performance Metrics:");
println!(" Total Operations: {}", service_data.total_operations);
println!(
" Successful: {} ({:.2}%)",
service_data.successful_operations,
service_data.success_rate * 100.0
);
println!(" Failed: {}", service_data.failed_operations);
println!(" Average Duration: {} ms", service_data.avg_duration_ms);
println!(" Total Duration: {} ms", service_data.total_duration_ms);
println!(" Items Processed: {}", service_data.total_items_processed);
if service_data.peak_memory_bytes > 0 {
println!(" Peak Memory: {} bytes", service_data.peak_memory_bytes);
}
println!(
" Last Operation: {} (timestamp)",
service_data.last_operation_at
);
println!();
if !service_data.operation_counts.is_empty() {
println!("🔧 Operation Breakdown:");
let mut operations: Vec<_> = service_data.operation_counts.iter().collect();
operations.sort_by(|a, b| b.1.cmp(a.1));
for (operation, count) in operations {
let percentage = (*count as f64 / service_data.total_operations as f64) * 100.0;
println!(" - {operation}: {count} ({percentage:.1}%)");
}
}
println!();
println!("📄 Raw Data (JSON):");
println!("{}", serde_json::to_string_pretty(&service_data)?);
} else {
println!("❌ No telemetry data found for service: {service_name}");
println!("💡 Available services can be seen with: pmat telemetry --system");
}
Ok(())
}
async fn show_system_overview() -> Result<()> {
info!("📊 Generating system overview");
let telemetry_service = telemetry();
let system_data = telemetry_service.get_system_telemetry().await?;
println!("🔍 PMAT System Overview");
println!("{}", "=".repeat(30));
println!();
println!("⏱️ System Status:");
println!(" Uptime: {} seconds", system_data.uptime_seconds);
println!(
" Total Operations: {}",
system_data.system_metrics.total_operations
);
println!(
" Success Rate: {:.1}%",
system_data.system_metrics.success_rate * 100.0
);
println!();
if system_data.services.is_empty() {
println!("📊 No service telemetry data available yet");
println!("💡 Use --test-event to generate sample telemetry data");
} else {
println!("🔧 Active Services: {}", system_data.services.len());
for service_name in system_data.services.keys() {
println!(" - {service_name}");
}
println!();
println!("💡 Use --system for detailed metrics or --service <name> for service details");
}
Ok(())
}
async fn record_test_telemetry_event() -> Result<()> {
debug!("🧪 Recording test telemetry event");
let test_input = TelemetryInput {
event_type: "test_operation".to_string(),
service_name: "telemetry_test_service".to_string(),
operation: "test_command_execution".to_string(),
metrics: OperationMetrics {
duration_ms: 125,
items_processed: 3,
memory_bytes: Some(2048),
cpu_time_ms: Some(95),
success: true,
error_message: None,
},
tags: {
let mut tags = HashMap::new();
tags.insert("test_type".to_string(), "cli_demo".to_string());
tags.insert("user".to_string(), "system".to_string());
tags
},
properties: {
let mut props = HashMap::new();
props.insert("version".to_string(), json!("2.6.8"));
props.insert("environment".to_string(), json!("development"));
props
},
};
let output = telemetry().record_operation(test_input).await?;
info!(event_id = %output.event_id, "✅ Test telemetry event recorded");
Ok(())
}
async fn record_telemetry_command_execution(start_time: Instant) -> Result<()> {
let duration = start_time.elapsed();
let input = TelemetryInput {
event_type: "cli_command".to_string(),
service_name: "cli_telemetry_handler".to_string(),
operation: "telemetry_command".to_string(),
metrics: OperationMetrics {
duration_ms: duration.as_millis() as u64,
items_processed: 1,
memory_bytes: None,
cpu_time_ms: None,
success: true,
error_message: None,
},
tags: HashMap::new(),
properties: HashMap::new(),
};
let _ = telemetry().record_operation(input).await;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_telemetry_command_system() {
telemetry().reset();
record_test_telemetry_event().await.unwrap();
let result = show_system_overview().await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_telemetry_command_service() {
telemetry().reset();
record_test_telemetry_event().await.unwrap();
let result = show_service_telemetry("telemetry_test_service").await;
assert!(result.is_ok());
let result = show_service_telemetry("non_existent_service").await;
assert!(result.is_ok()); }
#[tokio::test]
async fn test_telemetry_reset() {
record_test_telemetry_event().await.unwrap();
let result = handle_telemetry(false, None, true, false).await;
assert!(result.is_ok());
let system_data = telemetry().get_system_telemetry().await.unwrap();
assert_eq!(system_data.system_metrics.total_operations, 0);
}
#[tokio::test]
async fn test_test_event_generation() {
telemetry().reset();
let result = handle_telemetry(false, None, false, true).await;
assert!(result.is_ok());
let system_data = telemetry().get_system_telemetry().await.unwrap();
assert!(system_data.system_metrics.total_operations > 0);
}
}
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}