1use tracing::{debug, error, info, instrument, trace, warn};
7use tracing_subscriber::{fmt, prelude::*, EnvFilter};
8
9pub fn init_tracing(verbose: bool, _json: bool) {
15 let filter = if verbose {
16 EnvFilter::new("debug")
17 } else {
18 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"))
19 };
20
21 tracing_subscriber::registry()
24 .with(filter)
25 .with(fmt::layer().compact())
26 .init();
27}
28
29pub fn init_with_filter(filter: &str) {
31 let filter = EnvFilter::new(filter);
32 tracing_subscriber::registry()
33 .with(filter)
34 .with(fmt::layer().compact())
35 .init();
36}
37
38#[derive(Debug)]
40pub struct AnalysisSpan {
41 pub repo: String,
42 pub operation: String,
43}
44
45impl AnalysisSpan {
46 pub fn new(repo: impl Into<String>, operation: impl Into<String>) -> Self {
47 Self {
48 repo: repo.into(),
49 operation: operation.into(),
50 }
51 }
52}
53
54pub struct LogOps;
56
57impl LogOps {
58 #[instrument(skip_all, fields(repo = %repo, commits = commits))]
60 pub fn analysis_start(repo: &str, commits: usize) {
61 info!("Starting repository analysis");
62 }
63
64 #[instrument(skip_all, fields(repo = %repo, patterns = patterns, duration_ms = duration_ms))]
66 pub fn analysis_complete(repo: &str, patterns: usize, duration_ms: u64) {
67 info!("Analysis complete");
68 }
69
70 #[instrument(skip_all, fields(count = count))]
72 pub fn features_extracted(count: usize) {
73 debug!("Features extracted");
74 }
75
76 #[instrument(skip_all, fields(size = size, backend = %backend))]
78 pub fn correlation_start(size: usize, backend: &str) {
79 debug!("Computing correlation");
80 }
81
82 #[instrument(skip_all, fields(result = %format!("{:.4}", result), duration_us = duration_us))]
84 pub fn correlation_complete(result: f32, duration_us: u64) {
85 trace!("Correlation computed");
86 }
87
88 #[instrument(skip_all, fields(model = %model, samples = samples))]
90 pub fn training_start(model: &str, samples: usize) {
91 info!("Training model");
92 }
93
94 #[instrument(skip_all, fields(model = %model, accuracy = %format!("{:.2}%", accuracy * 100.0)))]
96 pub fn training_complete(model: &str, accuracy: f32) {
97 info!("Model training complete");
98 }
99
100 #[instrument(skip_all, fields(model = %model))]
102 pub fn prediction(model: &str, category: u8) {
103 debug!(category = category, "Prediction made");
104 }
105
106 #[instrument(skip_all, fields(k = k, samples = samples))]
108 pub fn clustering_start(k: usize, samples: usize) {
109 debug!("Starting clustering");
110 }
111
112 #[instrument(skip_all, fields(k = k, inertia = %format!("{:.2}", inertia)))]
114 pub fn clustering_complete(k: usize, inertia: f32) {
115 debug!("Clustering complete");
116 }
117
118 #[instrument(skip_all, fields(operation = %operation, count = count))]
120 pub fn storage_op(operation: &str, count: usize) {
121 trace!("Storage operation");
122 }
123
124 #[instrument(skip_all, fields(operation = %operation, backend = %backend))]
126 pub fn gpu_op(operation: &str, backend: &str) {
127 debug!("GPU operation");
128 }
129
130 pub fn error_with_context(error: &impl std::fmt::Display, context: &str) {
132 error!(context = context, error = %error, "Operation failed");
133 }
134
135 pub fn warning(message: &str, context: &str) {
137 warn!(context = context, "{}", message);
138 }
139
140 #[instrument(skip_all, fields(operation = %operation, duration_ms = duration_ms, throughput = throughput))]
142 pub fn performance(operation: &str, duration_ms: u64, throughput: Option<f64>) {
143 if let Some(tp) = throughput {
144 info!(throughput_per_sec = %format!("{:.2}", tp), "Performance metric");
145 } else {
146 info!("Performance metric");
147 }
148 }
149}
150
151pub struct Timer {
153 start: std::time::Instant,
154 operation: String,
155}
156
157impl Timer {
158 pub fn new(operation: impl Into<String>) -> Self {
159 Self {
160 start: std::time::Instant::now(),
161 operation: operation.into(),
162 }
163 }
164
165 pub fn elapsed_ms(&self) -> u64 {
166 self.start.elapsed().as_millis() as u64
167 }
168
169 pub fn elapsed_us(&self) -> u64 {
170 self.start.elapsed().as_micros() as u64
171 }
172
173 pub fn log_completion(&self) {
174 let duration = self.elapsed_ms();
175 debug!(
176 operation = %self.operation,
177 duration_ms = duration,
178 "Operation completed"
179 );
180 }
181}
182
183impl Drop for Timer {
184 fn drop(&mut self) {
185 }
187}
188
189#[derive(Debug, Default)]
191pub struct Metrics {
192 pub analyses_count: u64,
193 pub features_extracted: u64,
194 pub correlations_computed: u64,
195 pub predictions_made: u64,
196 pub errors_count: u64,
197 pub total_duration_ms: u64,
198}
199
200impl Metrics {
201 pub fn new() -> Self {
202 Self::default()
203 }
204
205 pub fn record_analysis(&mut self, duration_ms: u64) {
206 self.analyses_count += 1;
207 self.total_duration_ms += duration_ms;
208 }
209
210 pub fn record_features(&mut self, count: u64) {
211 self.features_extracted += count;
212 }
213
214 pub fn record_correlation(&mut self) {
215 self.correlations_computed += 1;
216 }
217
218 pub fn record_prediction(&mut self) {
219 self.predictions_made += 1;
220 }
221
222 pub fn record_error(&mut self) {
223 self.errors_count += 1;
224 }
225
226 pub fn summary(&self) -> String {
227 format!(
228 "Metrics: analyses={}, features={}, correlations={}, predictions={}, errors={}, total_time={}ms",
229 self.analyses_count,
230 self.features_extracted,
231 self.correlations_computed,
232 self.predictions_made,
233 self.errors_count,
234 self.total_duration_ms
235 )
236 }
237
238 pub fn log_summary(&self) {
239 info!(
240 analyses = self.analyses_count,
241 features = self.features_extracted,
242 correlations = self.correlations_computed,
243 predictions = self.predictions_made,
244 errors = self.errors_count,
245 total_duration_ms = self.total_duration_ms,
246 "Session metrics"
247 );
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn test_timer_creation() {
257 let timer = Timer::new("test_operation");
258 assert_eq!(timer.operation, "test_operation");
259 }
260
261 #[test]
262 fn test_timer_elapsed() {
263 let timer = Timer::new("test");
264 std::thread::sleep(std::time::Duration::from_millis(10));
265 assert!(timer.elapsed_ms() >= 10);
266 }
267
268 #[test]
269 fn test_metrics_default() {
270 let metrics = Metrics::new();
271 assert_eq!(metrics.analyses_count, 0);
272 assert_eq!(metrics.errors_count, 0);
273 }
274
275 #[test]
276 fn test_metrics_recording() {
277 let mut metrics = Metrics::new();
278 metrics.record_analysis(100);
279 metrics.record_features(50);
280 metrics.record_correlation();
281 metrics.record_prediction();
282 metrics.record_error();
283
284 assert_eq!(metrics.analyses_count, 1);
285 assert_eq!(metrics.features_extracted, 50);
286 assert_eq!(metrics.correlations_computed, 1);
287 assert_eq!(metrics.predictions_made, 1);
288 assert_eq!(metrics.errors_count, 1);
289 assert_eq!(metrics.total_duration_ms, 100);
290 }
291
292 #[test]
293 fn test_metrics_summary() {
294 let mut metrics = Metrics::new();
295 metrics.record_analysis(50);
296 metrics.record_features(100);
297
298 let summary = metrics.summary();
299 assert!(summary.contains("analyses=1"));
300 assert!(summary.contains("features=100"));
301 }
302
303 #[test]
304 fn test_analysis_span() {
305 let span = AnalysisSpan::new("owner/repo", "analyze");
306 assert_eq!(span.repo, "owner/repo");
307 assert_eq!(span.operation, "analyze");
308 }
309
310 #[test]
311 fn test_timer_elapsed_us() {
312 let timer = Timer::new("test");
313 std::thread::sleep(std::time::Duration::from_micros(100));
314 assert!(timer.elapsed_us() >= 100);
315 }
316
317 #[test]
318 fn test_timer_log_completion() {
319 let timer = Timer::new("test_op");
320 timer.log_completion(); }
322
323 #[test]
324 fn test_timer_drop() {
325 let _timer = Timer::new("test");
326 }
328
329 #[test]
330 fn test_metrics_log_summary() {
331 let mut metrics = Metrics::new();
332 metrics.record_analysis(100);
333 metrics.log_summary(); }
335
336 #[test]
337 fn test_logops_analysis_lifecycle() {
338 LogOps::analysis_start("test/repo", 100);
339 LogOps::analysis_complete("test/repo", 42, 1000);
340 }
341
342 #[test]
343 fn test_logops_features() {
344 LogOps::features_extracted(50);
345 }
346
347 #[test]
348 fn test_logops_correlation() {
349 LogOps::correlation_start(100, "simd");
350 LogOps::correlation_complete(0.95, 5000);
351 }
352
353 #[test]
354 fn test_logops_ml_training() {
355 LogOps::training_start("k-means", 100);
356 LogOps::training_complete("k-means", 0.85);
357 }
358
359 #[test]
360 fn test_logops_prediction() {
361 LogOps::prediction("DefectPredictor", 3);
362 }
363
364 #[test]
365 fn test_logops_clustering() {
366 LogOps::clustering_start(5, 100);
367 LogOps::clustering_complete(5, 123.45);
368 }
369
370 #[test]
371 fn test_logops_storage() {
372 LogOps::storage_op("save", 100);
373 }
374
375 #[test]
376 fn test_logops_gpu() {
377 LogOps::gpu_op("correlate", "gpu");
378 }
379
380 #[test]
381 fn test_logops_error_with_context() {
382 let error = std::io::Error::other("test error");
383 LogOps::error_with_context(&error, "during analysis");
384 }
385
386 #[test]
387 fn test_logops_warning() {
388 LogOps::warning("low memory", "performance");
389 }
390
391 #[test]
392 fn test_logops_performance_with_throughput() {
393 LogOps::performance("analysis", 1000, Some(100.5));
394 }
395
396 #[test]
397 fn test_logops_performance_without_throughput() {
398 LogOps::performance("analysis", 1000, None);
399 }
400
401 #[test]
402 fn test_multiple_metrics_recordings() {
403 let mut metrics = Metrics::new();
404 for i in 0..10 {
405 metrics.record_analysis(i * 10);
406 metrics.record_features(i * 5);
407 }
408 assert_eq!(metrics.analyses_count, 10);
409 assert_eq!(metrics.features_extracted, 225); assert_eq!(metrics.total_duration_ms, 450); }
412
413 #[test]
414 fn test_metrics_default_trait() {
415 let metrics = Metrics::default();
416 assert_eq!(metrics.analyses_count, 0);
417 assert_eq!(metrics.features_extracted, 0);
418 assert_eq!(metrics.correlations_computed, 0);
419 assert_eq!(metrics.predictions_made, 0);
420 assert_eq!(metrics.errors_count, 0);
421 assert_eq!(metrics.total_duration_ms, 0);
422 }
423
424 #[test]
425 fn test_timer_with_string_operation() {
426 let timer = Timer::new(String::from("string_op"));
427 assert_eq!(timer.operation, "string_op");
428 }
429
430 #[test]
431 fn test_timer_elapsed_immediately() {
432 let timer = Timer::new("instant");
433 let elapsed = timer.elapsed_ms();
434 assert!(elapsed < 1000); }
437
438 #[test]
439 fn test_analysis_span_with_string() {
440 let span = AnalysisSpan::new(String::from("owner/repo"), String::from("op"));
441 assert_eq!(span.repo, "owner/repo");
442 assert_eq!(span.operation, "op");
443 }
444
445 #[test]
446 fn test_metrics_multiple_errors() {
447 let mut metrics = Metrics::new();
448 for _ in 0..5 {
449 metrics.record_error();
450 }
451 assert_eq!(metrics.errors_count, 5);
452 }
453
454 #[test]
455 fn test_metrics_multiple_predictions() {
456 let mut metrics = Metrics::new();
457 for _ in 0..10 {
458 metrics.record_prediction();
459 }
460 assert_eq!(metrics.predictions_made, 10);
461 }
462
463 #[test]
464 fn test_metrics_multiple_correlations() {
465 let mut metrics = Metrics::new();
466 for _ in 0..3 {
467 metrics.record_correlation();
468 }
469 assert_eq!(metrics.correlations_computed, 3);
470 }
471
472 #[test]
473 fn test_metrics_zero_duration() {
474 let mut metrics = Metrics::new();
475 metrics.record_analysis(0);
476 assert_eq!(metrics.analyses_count, 1);
477 assert_eq!(metrics.total_duration_ms, 0);
478 }
479
480 #[test]
481 fn test_metrics_large_duration() {
482 let mut metrics = Metrics::new();
483 metrics.record_analysis(1_000_000);
484 assert_eq!(metrics.total_duration_ms, 1_000_000);
485 }
486
487 #[test]
488 fn test_metrics_summary_contains_all_fields() {
489 let mut metrics = Metrics::new();
490 metrics.record_analysis(100);
491 metrics.record_features(50);
492 metrics.record_correlation();
493 metrics.record_prediction();
494 metrics.record_error();
495
496 let summary = metrics.summary();
497 assert!(summary.contains("analyses="));
498 assert!(summary.contains("features="));
499 assert!(summary.contains("correlations="));
500 assert!(summary.contains("predictions="));
501 assert!(summary.contains("errors="));
502 assert!(summary.contains("total_time="));
503 }
504
505 #[test]
506 fn test_timer_microsecond_precision() {
507 let timer = Timer::new("precision_test");
508 std::thread::sleep(std::time::Duration::from_micros(500));
509 let us = timer.elapsed_us();
510 let ms = timer.elapsed_ms();
511
512 assert!(us >= 500);
514 assert!(us >= ms * 1000);
516 }
517
518 #[test]
519 fn test_analysis_span_debug() {
520 let span = AnalysisSpan::new("test/repo", "operation");
521 let debug_str = format!("{:?}", span);
522 assert!(debug_str.contains("AnalysisSpan"));
523 }
524
525 #[test]
526 fn test_metrics_debug() {
527 let metrics = Metrics::new();
528 let debug_str = format!("{:?}", metrics);
529 assert!(debug_str.contains("Metrics"));
530 }
531
532 #[test]
533 fn test_logops_all_operations_no_panic() {
534 LogOps::analysis_start("repo", 100);
536 LogOps::analysis_complete("repo", 10, 1000);
537 LogOps::features_extracted(50);
538 LogOps::correlation_start(100, "cpu");
539 LogOps::correlation_complete(0.5, 1000);
540 LogOps::training_start("model", 100);
541 LogOps::training_complete("model", 0.9);
542 LogOps::prediction("model", 1);
543 LogOps::clustering_start(3, 100);
544 LogOps::clustering_complete(3, 50.0);
545 LogOps::storage_op("load", 50);
546 LogOps::gpu_op("compute", "gpu");
547
548 let err = std::io::Error::other("test");
549 LogOps::error_with_context(&err, "test context");
550 LogOps::warning("test warning", "test");
551 LogOps::performance("op", 100, Some(10.0));
552 LogOps::performance("op", 100, None);
553 }
554
555 #[test]
556 fn test_metrics_accumulation() {
557 let mut metrics = Metrics::new();
558
559 metrics.record_analysis(100);
561 metrics.record_analysis(200);
562 metrics.record_analysis(300);
563
564 assert_eq!(metrics.analyses_count, 3);
565 assert_eq!(metrics.total_duration_ms, 600);
566
567 metrics.record_features(10);
569 metrics.record_features(20);
570
571 assert_eq!(metrics.features_extracted, 30);
572 }
573}