1use super::{MetricsCollector, PerformanceReport};
2use std::fmt::Write;
3
4pub struct MetricsReporter {
7 format: ReportFormat,
9 alert_thresholds: Vec<AlertThreshold>,
11 include_details: bool,
13}
14
15#[derive(Debug, Clone, PartialEq)]
17pub enum ReportFormat {
18 PlainText,
20 Json,
22 Markdown,
24 Csv,
26}
27
28#[derive(Debug, Clone)]
30pub struct AlertThreshold {
31 pub metric_name: String,
33 pub threshold: f64,
35 pub condition: AlertCondition,
37 pub severity: AlertSeverity,
39 pub description: String,
41}
42
43#[derive(Debug, Clone, PartialEq)]
45pub enum AlertCondition {
46 GreaterThan,
48 LessThan,
50 Equals,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
56pub enum AlertSeverity {
57 Info,
59 Warning,
61 Error,
63 Critical,
65}
66
67impl MetricsReporter {
68 pub fn new() -> Self {
70 Self {
71 format: ReportFormat::PlainText,
72 alert_thresholds: Self::default_memory_thresholds(),
73 include_details: true,
74 }
75 }
76
77 pub fn with_format(format: ReportFormat) -> Self {
79 Self {
80 format,
81 alert_thresholds: Self::default_memory_thresholds(),
82 include_details: true,
83 }
84 }
85
86 pub fn with_details(mut self, include_details: bool) -> Self {
88 self.include_details = include_details;
89 self
90 }
91
92 pub fn add_alert_threshold(mut self, threshold: AlertThreshold) -> Self {
94 self.alert_thresholds.push(threshold);
95 self
96 }
97
98 pub fn generate_report(&self, report: &PerformanceReport) -> String {
100 match self.format {
101 ReportFormat::PlainText => self.generate_text_report(report),
102 ReportFormat::Json => self.generate_json_report(report),
103 ReportFormat::Markdown => self.generate_markdown_report(report),
104 ReportFormat::Csv => self.generate_csv_report(report),
105 }
106 }
107
108 pub fn generate_metrics_summary(&self, collector: &MetricsCollector) -> String {
110 let summary = collector.get_summary();
111
112 match self.format {
113 ReportFormat::PlainText => {
114 format!(
115 "Metrics Summary:\n\
116 - Total Metrics: {}\n\
117 - Active Metrics: {}\n\
118 - Update Rate: {:.2}/sec\n\
119 - Uptime: {:.2}h\n\
120 - Sample Rate: {:.1}%\n",
121 summary.total_metrics,
122 summary.active_metrics,
123 summary.update_rate,
124 summary.uptime.as_secs_f64() / 3600.0,
125 summary.sample_rate * 100.0
126 )
127 }
128 ReportFormat::Json => {
129 format!(
130 r#"{{"total_metrics": {}, "active_metrics": {}, "update_rate": {:.2}, "uptime_hours": {:.2}, "sample_rate": {:.3}}}"#,
131 summary.total_metrics,
132 summary.active_metrics,
133 summary.update_rate,
134 summary.uptime.as_secs_f64() / 3600.0,
135 summary.sample_rate
136 )
137 }
138 _ => self.generate_text_report(&PerformanceReport {
139 efficiency_score: 0.0,
140 tracking_performance: Default::default(),
141 symbol_performance: Default::default(),
142 pointer_performance: Default::default(),
143 memory_efficiency: Default::default(),
144 recommendations: vec![],
145 }),
146 }
147 }
148
149 pub fn check_alerts(&self, collector: &MetricsCollector) -> Vec<TriggeredAlert> {
151 let mut alerts = Vec::new();
152
153 for threshold in &self.alert_thresholds {
154 if let Some(metric) = collector.get_metric(&threshold.metric_name) {
155 let current_value = self.extract_metric_value(metric);
156
157 let triggered = match threshold.condition {
158 AlertCondition::GreaterThan => current_value > threshold.threshold,
159 AlertCondition::LessThan => current_value < threshold.threshold,
160 AlertCondition::Equals => (current_value - threshold.threshold).abs() < 0.001,
161 };
162
163 if triggered {
164 alerts.push(TriggeredAlert {
165 metric_name: threshold.metric_name.clone(),
166 current_value,
167 threshold_value: threshold.threshold,
168 condition: threshold.condition.clone(),
169 severity: threshold.severity.clone(),
170 description: threshold.description.clone(),
171 });
172 }
173 }
174 }
175
176 alerts.sort_by(|a, b| b.severity.cmp(&a.severity));
178 alerts
179 }
180
181 fn default_memory_thresholds() -> Vec<AlertThreshold> {
182 vec![
183 AlertThreshold {
184 metric_name: "tracking_completeness".to_string(),
185 threshold: 0.95,
186 condition: AlertCondition::LessThan,
187 severity: AlertSeverity::Warning,
188 description: "Memory tracking completeness below 95%".to_string(),
189 },
190 AlertThreshold {
191 metric_name: "allocation_tracking_time".to_string(),
192 threshold: 100.0, condition: AlertCondition::GreaterThan,
194 severity: AlertSeverity::Error,
195 description: "Allocation tracking latency exceeds 100µs".to_string(),
196 },
197 AlertThreshold {
198 metric_name: "symbol_cache_hit_ratio".to_string(),
199 threshold: 0.8,
200 condition: AlertCondition::LessThan,
201 severity: AlertSeverity::Warning,
202 description: "Symbol cache hit ratio below 80%".to_string(),
203 },
204 AlertThreshold {
205 metric_name: "total_analysis_memory".to_string(),
206 threshold: 512.0, condition: AlertCondition::GreaterThan,
208 severity: AlertSeverity::Critical,
209 description: "Analysis memory usage exceeds 512MB".to_string(),
210 },
211 AlertThreshold {
212 metric_name: "memory_fragmentation".to_string(),
213 threshold: 0.3,
214 condition: AlertCondition::GreaterThan,
215 severity: AlertSeverity::Warning,
216 description: "Memory fragmentation exceeds 30%".to_string(),
217 },
218 ]
219 }
220
221 fn generate_text_report(&self, report: &PerformanceReport) -> String {
222 let mut output = String::new();
223
224 writeln!(output, "=== Memory Analysis Performance Report ===").expect("Write failed");
225 writeln!(
226 output,
227 "Overall Efficiency Score: {:.1}%",
228 report.efficiency_score * 100.0
229 )
230 .expect("Write failed");
231 writeln!(output).expect("Write failed");
232
233 writeln!(output, "Memory Tracking Performance:").expect("Write failed");
235 writeln!(
236 output,
237 " - Average Allocation Time: {:.2}µs",
238 report.tracking_performance.avg_allocation_time.as_micros()
239 )
240 .expect("Write failed");
241 writeln!(
242 output,
243 " - Tracking Completeness: {:.1}%",
244 report.tracking_performance.completeness * 100.0
245 )
246 .expect("Write failed");
247 writeln!(
248 output,
249 " - Memory Overhead: {:.2}MB",
250 report.tracking_performance.overhead_bytes as f64 / (1024.0 * 1024.0)
251 )
252 .expect("Write failed");
253 writeln!(
254 output,
255 " - Throughput: {:.0} allocs/sec",
256 report.tracking_performance.throughput
257 )
258 .expect("Write failed");
259 writeln!(output).expect("Write failed");
260
261 writeln!(output, "Symbol Resolution Performance:").expect("Write failed");
263 writeln!(
264 output,
265 " - Average Resolution Time: {:.2}ms",
266 report.symbol_performance.avg_resolution_time.as_millis()
267 )
268 .expect("Write failed");
269 writeln!(
270 output,
271 " - Cache Hit Ratio: {:.1}%",
272 report.symbol_performance.cache_hit_ratio * 100.0
273 )
274 .expect("Write failed");
275 writeln!(
276 output,
277 " - Resolution Rate: {:.0} symbols/sec",
278 report.symbol_performance.resolution_rate
279 )
280 .expect("Write failed");
281 writeln!(
282 output,
283 " - Cache Memory: {:.2}MB",
284 report.symbol_performance.cache_memory_usage as f64 / (1024.0 * 1024.0)
285 )
286 .expect("Write failed");
287 writeln!(output).expect("Write failed");
288
289 writeln!(output, "Smart Pointer Analysis:").expect("Write failed");
291 writeln!(
292 output,
293 " - Analysis Time: {:.2}ms",
294 report.pointer_performance.analysis_time.as_millis()
295 )
296 .expect("Write failed");
297 writeln!(
298 output,
299 " - Leak Detection Accuracy: {:.1}%",
300 report.pointer_performance.leak_detection_accuracy * 100.0
301 )
302 .expect("Write failed");
303 writeln!(
304 output,
305 " - Analysis Rate: {:.0} pointers/sec",
306 report.pointer_performance.analysis_rate
307 )
308 .expect("Write failed");
309 writeln!(output).expect("Write failed");
310
311 writeln!(output, "Memory Usage Efficiency:").expect("Write failed");
313 writeln!(
314 output,
315 " - Total Memory: {:.1}MB",
316 report.memory_efficiency.total_memory_mb
317 )
318 .expect("Write failed");
319 writeln!(
320 output,
321 " - Memory per Allocation: {:.1} bytes",
322 report.memory_efficiency.memory_per_allocation
323 )
324 .expect("Write failed");
325 writeln!(
326 output,
327 " - Growth Rate: {:.2}MB/hour",
328 report.memory_efficiency.growth_rate
329 )
330 .expect("Write failed");
331 writeln!(
332 output,
333 " - Fragmentation: {:.1}%",
334 report.memory_efficiency.fragmentation_ratio * 100.0
335 )
336 .expect("Write failed");
337 writeln!(output).expect("Write failed");
338
339 if !report.recommendations.is_empty() {
341 writeln!(output, "Performance Recommendations:").expect("Write failed");
342 for (i, rec) in report.recommendations.iter().enumerate() {
343 writeln!(output, " {}. {}", i + 1, rec).expect("Write failed");
344 }
345 }
346
347 output
348 }
349
350 fn generate_json_report(&self, report: &PerformanceReport) -> String {
351 format!(
352 r#"{{
353 "efficiency_score": {:.3},
354 "tracking_performance": {{
355 "avg_allocation_time_us": {:.2},
356 "completeness": {:.3},
357 "overhead_bytes": {},
358 "throughput": {:.2}
359 }},
360 "symbol_performance": {{
361 "avg_resolution_time_ms": {:.2},
362 "cache_hit_ratio": {:.3},
363 "resolution_rate": {:.2},
364 "cache_memory_usage": {}
365 }},
366 "pointer_performance": {{
367 "analysis_time_ms": {:.2},
368 "leak_detection_accuracy": {:.3},
369 "analysis_rate": {:.2}
370 }},
371 "memory_efficiency": {{
372 "total_memory_mb": {:.2},
373 "memory_per_allocation": {:.2},
374 "growth_rate": {:.2},
375 "fragmentation_ratio": {:.3}
376 }},
377 "recommendations": [{}]
378}}"#,
379 report.efficiency_score,
380 report.tracking_performance.avg_allocation_time.as_micros(),
381 report.tracking_performance.completeness,
382 report.tracking_performance.overhead_bytes,
383 report.tracking_performance.throughput,
384 report.symbol_performance.avg_resolution_time.as_millis(),
385 report.symbol_performance.cache_hit_ratio,
386 report.symbol_performance.resolution_rate,
387 report.symbol_performance.cache_memory_usage,
388 report.pointer_performance.analysis_time.as_millis(),
389 report.pointer_performance.leak_detection_accuracy,
390 report.pointer_performance.analysis_rate,
391 report.memory_efficiency.total_memory_mb,
392 report.memory_efficiency.memory_per_allocation,
393 report.memory_efficiency.growth_rate,
394 report.memory_efficiency.fragmentation_ratio,
395 report
396 .recommendations
397 .iter()
398 .map(|r| format!("\"{}\"", r.replace('"', "\\\"")))
399 .collect::<Vec<_>>()
400 .join(", ")
401 )
402 }
403
404 fn generate_markdown_report(&self, report: &PerformanceReport) -> String {
405 let mut output = String::new();
406
407 writeln!(output, "# Memory Analysis Performance Report").expect("Write failed");
408 writeln!(output).expect("Write failed");
409 writeln!(
410 output,
411 "**Overall Efficiency Score:** {:.1}%",
412 report.efficiency_score * 100.0
413 )
414 .expect("Write failed");
415 writeln!(output).expect("Write failed");
416
417 writeln!(output, "## Performance Metrics").expect("Write failed");
418 writeln!(output).expect("Write failed");
419
420 writeln!(output, "### Memory Tracking").expect("Write failed");
421 writeln!(output, "| Metric | Value |").expect("Write failed");
422 writeln!(output, "|--------|-------|").expect("Write failed");
423 writeln!(
424 output,
425 "| Allocation Time | {:.2}µs |",
426 report.tracking_performance.avg_allocation_time.as_micros()
427 )
428 .expect("Write failed");
429 writeln!(
430 output,
431 "| Completeness | {:.1}% |",
432 report.tracking_performance.completeness * 100.0
433 )
434 .expect("Write failed");
435 writeln!(
436 output,
437 "| Memory Overhead | {:.2}MB |",
438 report.tracking_performance.overhead_bytes as f64 / (1024.0 * 1024.0)
439 )
440 .expect("Write failed");
441 writeln!(
442 output,
443 "| Throughput | {:.0} allocs/sec |",
444 report.tracking_performance.throughput
445 )
446 .expect("Write failed");
447 writeln!(output).expect("Write failed");
448
449 writeln!(output, "### Symbol Resolution").expect("Write failed");
450 writeln!(output, "| Metric | Value |").expect("Write failed");
451 writeln!(output, "|--------|-------|").expect("Write failed");
452 writeln!(
453 output,
454 "| Resolution Time | {:.2}ms |",
455 report.symbol_performance.avg_resolution_time.as_millis()
456 )
457 .expect("Write failed");
458 writeln!(
459 output,
460 "| Cache Hit Ratio | {:.1}% |",
461 report.symbol_performance.cache_hit_ratio * 100.0
462 )
463 .expect("Write failed");
464 writeln!(
465 output,
466 "| Resolution Rate | {:.0} symbols/sec |",
467 report.symbol_performance.resolution_rate
468 )
469 .expect("Write failed");
470 writeln!(output).expect("Write failed");
471
472 if !report.recommendations.is_empty() {
473 writeln!(output, "## Recommendations").expect("Write failed");
474 writeln!(output).expect("Write failed");
475 for rec in &report.recommendations {
476 writeln!(output, "- {}", rec).expect("Write failed");
477 }
478 }
479
480 output
481 }
482
483 fn generate_csv_report(&self, report: &PerformanceReport) -> String {
484 let mut output = String::new();
485
486 writeln!(output, "metric_category,metric_name,value,unit").expect("Write failed");
487 writeln!(
488 output,
489 "overall,efficiency_score,{:.3},percentage",
490 report.efficiency_score
491 )
492 .expect("Write failed");
493 writeln!(
494 output,
495 "tracking,avg_allocation_time,{:.2},microseconds",
496 report.tracking_performance.avg_allocation_time.as_micros()
497 )
498 .expect("Write failed");
499 writeln!(
500 output,
501 "tracking,completeness,{:.3},ratio",
502 report.tracking_performance.completeness
503 )
504 .expect("Write failed");
505 writeln!(
506 output,
507 "tracking,overhead_bytes,{},bytes",
508 report.tracking_performance.overhead_bytes
509 )
510 .expect("Write failed");
511 writeln!(
512 output,
513 "tracking,throughput,{:.2},allocations_per_second",
514 report.tracking_performance.throughput
515 )
516 .expect("Write failed");
517 writeln!(
518 output,
519 "symbol,avg_resolution_time,{:.2},milliseconds",
520 report.symbol_performance.avg_resolution_time.as_millis()
521 )
522 .expect("Write failed");
523 writeln!(
524 output,
525 "symbol,cache_hit_ratio,{:.3},ratio",
526 report.symbol_performance.cache_hit_ratio
527 )
528 .expect("Write failed");
529 writeln!(
530 output,
531 "symbol,resolution_rate,{:.2},symbols_per_second",
532 report.symbol_performance.resolution_rate
533 )
534 .expect("Write failed");
535 writeln!(
536 output,
537 "memory,total_memory_mb,{:.2},megabytes",
538 report.memory_efficiency.total_memory_mb
539 )
540 .expect("Write failed");
541 writeln!(
542 output,
543 "memory,fragmentation_ratio,{:.3},ratio",
544 report.memory_efficiency.fragmentation_ratio
545 )
546 .expect("Write failed");
547
548 output
549 }
550
551 fn extract_metric_value(&self, metric: &super::Metric) -> f64 {
552 match &metric.value {
553 super::MetricValue::Counter(counter) => {
554 counter.load(std::sync::atomic::Ordering::Relaxed) as f64
555 }
556 super::MetricValue::Gauge(value) => *value,
557 super::MetricValue::Histogram(hist) => hist.average(),
558 super::MetricValue::Timer(timer) => timer.average_duration().as_millis() as f64,
559 super::MetricValue::Rate(rate) => rate.current_rate,
560 }
561 }
562}
563
564#[derive(Debug, Clone)]
566pub struct TriggeredAlert {
567 pub metric_name: String,
569 pub current_value: f64,
571 pub threshold_value: f64,
573 pub condition: AlertCondition,
575 pub severity: AlertSeverity,
577 pub description: String,
579}
580
581impl Default for MetricsReporter {
582 fn default() -> Self {
583 Self::new()
584 }
585}
586
587#[cfg(test)]
588mod tests {
589 use super::*;
590
591 #[test]
592 fn test_reporter_creation() {
593 let reporter = MetricsReporter::new();
594 assert_eq!(reporter.format, ReportFormat::PlainText);
595 assert!(reporter.include_details);
596 assert!(!reporter.alert_thresholds.is_empty());
597 }
598
599 #[test]
600 fn test_report_formats() {
601 let report = PerformanceReport {
602 efficiency_score: 0.85,
603 tracking_performance: Default::default(),
604 symbol_performance: Default::default(),
605 pointer_performance: Default::default(),
606 memory_efficiency: Default::default(),
607 recommendations: vec!["Test recommendation".to_string()],
608 };
609
610 let text_reporter = MetricsReporter::with_format(ReportFormat::PlainText);
611 let text_report = text_reporter.generate_report(&report);
612 assert!(text_report.contains("Memory Analysis Performance Report"));
613 assert!(text_report.contains("85.0%"));
614
615 let json_reporter = MetricsReporter::with_format(ReportFormat::Json);
616 let json_report = json_reporter.generate_report(&report);
617 assert!(json_report.contains("efficiency_score"));
618 assert!(json_report.contains("0.850"));
619
620 let md_reporter = MetricsReporter::with_format(ReportFormat::Markdown);
621 let md_report = md_reporter.generate_report(&report);
622 assert!(md_report.contains("# Memory Analysis Performance Report"));
623 assert!(md_report.contains("## Recommendations"));
624 }
625
626 #[test]
627 fn test_alert_threshold() {
628 let threshold = AlertThreshold {
629 metric_name: "test_metric".to_string(),
630 threshold: 100.0,
631 condition: AlertCondition::GreaterThan,
632 severity: AlertSeverity::Warning,
633 description: "Test alert".to_string(),
634 };
635
636 assert_eq!(threshold.metric_name, "test_metric");
637 assert_eq!(threshold.condition, AlertCondition::GreaterThan);
638 assert_eq!(threshold.severity, AlertSeverity::Warning);
639 }
640
641 #[test]
642 fn test_alert_severity_ordering() {
643 assert!(AlertSeverity::Critical > AlertSeverity::Error);
644 assert!(AlertSeverity::Error > AlertSeverity::Warning);
645 assert!(AlertSeverity::Warning > AlertSeverity::Info);
646 }
647}