ant_quic/logging/
formatters.rs

1/// Log formatting utilities
2///
3/// Provides various utility functions for formatting log data
4use crate::{ConnectionId, Duration};
5
6/// Format bytes in a human-readable way
7pub fn format_bytes(bytes: u64) -> String {
8    const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
9    let mut size = bytes as f64;
10    let mut unit_idx = 0;
11
12    while size >= 1024.0 && unit_idx < UNITS.len() - 1 {
13        size /= 1024.0;
14        unit_idx += 1;
15    }
16
17    if unit_idx == 0 {
18        format!("{} {}", bytes, UNITS[unit_idx])
19    } else {
20        format!("{:.2} {}", size, UNITS[unit_idx])
21    }
22}
23
24/// Format duration in a human-readable way
25pub fn format_duration(duration: Duration) -> String {
26    let micros = duration.as_micros();
27    if micros < 1000 {
28        format!("{micros}μs")
29    } else if micros < 1_000_000 {
30        format!("{:.2}ms", micros as f64 / 1000.0)
31    } else if micros < 60_000_000 {
32        format!("{:.2}s", micros as f64 / 1_000_000.0)
33    } else {
34        let seconds = micros / 1_000_000;
35        let minutes = seconds / 60;
36        let seconds = seconds % 60;
37        format!("{minutes}m{seconds}s")
38    }
39}
40
41/// Format a connection ID for display
42pub fn format_conn_id(conn_id: &ConnectionId) -> String {
43    let bytes = conn_id.as_ref();
44    if bytes.len() <= 8 {
45        hex::encode(bytes)
46    } else {
47        format!(
48            "{}..{}",
49            hex::encode(&bytes[..4]),
50            hex::encode(&bytes[bytes.len() - 4..])
51        )
52    }
53}
54
55/// Format a structured log event as JSON  
56pub(super) fn format_as_json(event: &super::LogEvent) -> String {
57    use serde_json::json;
58
59    let json = json!({
60        "timestamp": event.timestamp.elapsed().as_secs(),
61        "level": match event.level {
62            tracing::Level::ERROR => "ERROR",
63            tracing::Level::WARN => "WARN",
64            tracing::Level::INFO => "INFO",
65            tracing::Level::DEBUG => "DEBUG",
66            tracing::Level::TRACE => "TRACE",
67        },
68        "target": event.target,
69        "message": event.message,
70        "fields": event.fields,
71        "span_id": event.span_id,
72    });
73
74    json.to_string()
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_format_bytes() {
83        assert_eq!(format_bytes(0), "0 B");
84        assert_eq!(format_bytes(1023), "1023 B");
85        assert_eq!(format_bytes(1024), "1.00 KB");
86        assert_eq!(format_bytes(1536), "1.50 KB");
87        assert_eq!(format_bytes(1048576), "1.00 MB");
88        assert_eq!(format_bytes(1073741824), "1.00 GB");
89    }
90
91    #[test]
92    fn test_format_duration() {
93        use crate::Duration;
94
95        assert_eq!(format_duration(Duration::from_micros(500)), "500μs");
96        assert_eq!(format_duration(Duration::from_micros(1500)), "1.50ms");
97        assert_eq!(format_duration(Duration::from_millis(50)), "50.00ms");
98        assert_eq!(format_duration(Duration::from_secs(5)), "5.00s");
99        assert_eq!(format_duration(Duration::from_secs(65)), "1m5s");
100    }
101}