ant_quic/logging/
formatters.rs

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