1use std::collections::HashMap;
14use std::net::SocketAddr;
15use std::sync::atomic::{AtomicU64, Ordering};
16use std::sync::{Arc, Mutex};
17
18use tracing::{Level, Span, debug, error, info, trace, warn};
19use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
20
21use crate::{
22 ConnectionId, Duration, Instant, Side, connection::nat_traversal::NatTraversalRole,
23 frame::FrameType, transport_parameters::TransportParameterId,
24};
25
26#[cfg(test)]
27mod tests;
28
29mod components;
30mod filters;
31mod formatters;
32mod lifecycle;
33pub mod metrics;
35mod structured;
36
37pub use components::*;
38pub use filters::*;
39pub use formatters::*;
40pub use lifecycle::*;
41pub use metrics::*;
42pub use structured::*;
43
44static LOGGER: once_cell::sync::OnceCell<Arc<Logger>> = once_cell::sync::OnceCell::new();
46
47#[allow(clippy::expect_used)]
49pub fn init_logging(config: LoggingConfig) -> Result<(), LoggingError> {
50 let logger = Arc::new(Logger::new(config)?);
51
52 LOGGER
53 .set(logger.clone())
54 .map_err(|_| LoggingError::AlreadyInitialized)?;
55
56 let env_filter = EnvFilter::from_default_env().add_directive(
58 "ant_quic=debug"
59 .parse()
60 .expect("Static directive should always parse"),
61 );
62
63 if logger.use_json() {
64 let fmt_layer = tracing_subscriber::fmt::layer()
65 .json()
66 .with_target(true)
67 .with_thread_ids(true)
68 .with_level(true);
69
70 tracing_subscriber::registry()
71 .with(env_filter)
72 .with(fmt_layer)
73 .init();
74 } else {
75 let fmt_layer = tracing_subscriber::fmt::layer()
76 .with_target(true)
77 .with_thread_ids(true)
78 .with_level(true);
79
80 tracing_subscriber::registry()
81 .with(env_filter)
82 .with(fmt_layer)
83 .init();
84 }
85
86 info!("ant-quic logging system initialized");
87 Ok(())
88}
89
90#[allow(clippy::expect_used)]
92pub fn logger() -> Arc<Logger> {
93 LOGGER.get().cloned().unwrap_or_else(|| {
94 let config = LoggingConfig::default();
96 let logger = Arc::new(Logger::new(config).expect("Failed to create default logger"));
97 let _ = LOGGER.set(logger.clone());
98 logger
99 })
100}
101
102pub struct Logger {
104 config: LoggingConfig,
105 metrics_collector: Arc<MetricsCollector>,
106 event_buffer: Arc<Mutex<Vec<LogEvent>>>,
107 rate_limiter: Arc<RateLimiter>,
108}
109
110impl Logger {
111 pub fn new(config: LoggingConfig) -> Result<Self, LoggingError> {
113 let rate_limit = config.rate_limit_per_second;
114 let buffer_size = config.event_buffer_size;
115 Ok(Self {
116 config,
117 metrics_collector: Arc::new(MetricsCollector::new()),
118 event_buffer: Arc::new(Mutex::new(Vec::with_capacity(buffer_size))),
119 rate_limiter: Arc::new(RateLimiter::new(rate_limit, Duration::from_secs(1))),
120 })
121 }
122
123 fn use_json(&self) -> bool {
125 self.config.json_output
126 }
127
128 pub fn log_event(&self, event: LogEvent) {
130 if !self.rate_limiter.should_log(event.level) {
131 return;
132 }
133
134 if let Ok(mut buffer) = self.event_buffer.lock() {
136 if buffer.len() < 10000 {
137 buffer.push(event.clone());
138 }
139 }
140
141 match event.level {
143 Level::ERROR => error!("{} - {}", event.target, event.message),
144 Level::WARN => warn!("{} - {}", event.target, event.message),
145 Level::INFO => info!("{} - {}", event.target, event.message),
146 Level::DEBUG => debug!("{} - {}", event.target, event.message),
147 Level::TRACE => trace!("{} - {}", event.target, event.message),
148 }
149
150 self.metrics_collector.record_event(&event);
152 }
153
154 pub fn recent_events(&self, count: usize) -> Vec<LogEvent> {
156 match self.event_buffer.lock() {
157 Ok(buffer) => buffer.iter().rev().take(count).cloned().collect(),
158 _ => Vec::new(),
159 }
160 }
161
162 pub fn metrics_summary(&self) -> MetricsSummary {
164 self.metrics_collector.summary()
165 }
166}
167
168#[derive(Debug, Clone)]
170pub struct LoggingConfig {
171 pub json_output: bool,
173 pub rate_limit_per_second: u64,
175 pub component_levels: HashMap<String, Level>,
177 pub collect_metrics: bool,
179 pub event_buffer_size: usize,
181}
182
183impl Default for LoggingConfig {
184 fn default() -> Self {
185 Self {
186 json_output: false,
187 rate_limit_per_second: 1000,
188 component_levels: HashMap::new(),
189 collect_metrics: true,
190 event_buffer_size: 10000,
191 }
192 }
193}
194
195#[derive(Debug, Clone)]
197pub struct LogEvent {
198 pub timestamp: Instant,
200 pub level: Level,
202 pub target: String,
204 pub message: String,
206 pub fields: HashMap<String, String>,
208 pub span_id: Option<String>,
210}
211
212#[derive(Debug, Clone, Copy)]
214pub enum ConnectionRole {
215 Client,
217 Server,
219}
220
221#[derive(Debug, Clone)]
223pub struct ConnectionInfo {
224 pub id: ConnectionId,
226 pub remote_addr: SocketAddr,
228 pub role: ConnectionRole,
230}
231
232#[derive(Debug)]
234pub struct FrameInfo {
235 pub frame_type: FrameType,
237 pub size: usize,
239 pub packet_number: Option<u64>,
241}
242
243#[derive(Debug)]
245pub struct TransportParamInfo {
246 pub(crate) param_id: TransportParameterId,
247 pub value: Option<Vec<u8>>,
249 pub side: Side,
251}
252
253#[derive(Debug)]
255pub struct NatTraversalInfo {
256 pub role: NatTraversalRole,
258 pub remote_addr: SocketAddr,
260 pub candidate_count: usize,
262}
263
264#[derive(Debug, Default)]
266pub struct ErrorContext {
267 pub component: &'static str,
269 pub operation: &'static str,
271 pub connection_id: Option<ConnectionId>,
273 pub additional_fields: Vec<(&'static str, &'static str)>,
275}
276
277#[derive(Debug, Default)]
279pub struct WarningContext {
280 pub component: &'static str,
282 pub details: Vec<(&'static str, &'static str)>,
284}
285
286#[derive(Debug, Default)]
288pub struct InfoContext {
289 pub component: &'static str,
291 pub details: Vec<(&'static str, &'static str)>,
293}
294
295#[derive(Debug, Default)]
297pub struct DebugContext {
298 pub component: &'static str,
300 pub details: Vec<(&'static str, &'static str)>,
302}
303
304#[derive(Debug, Default)]
306pub struct TraceContext {
307 pub component: &'static str,
309 pub details: Vec<(&'static str, &'static str)>,
311}
312
313#[derive(Debug, thiserror::Error)]
315pub enum LoggingError {
316 #[error("Logging system already initialized")]
318 AlreadyInitialized,
319 #[error("Failed to initialize tracing subscriber: {0}")]
321 SubscriberError(String),
322}
323
324pub struct RateLimiter {
326 max_events: u64,
328 window: Duration,
330 events_in_window: AtomicU64,
332 window_start: Mutex<Instant>,
334}
335
336impl RateLimiter {
337 pub fn new(max_events: u64, window: Duration) -> Self {
339 Self {
340 max_events,
341 window,
342 events_in_window: AtomicU64::new(0),
343 window_start: Mutex::new(Instant::now()),
344 }
345 }
346
347 #[allow(clippy::unwrap_used, clippy::expect_used)]
349 pub fn should_log(&self, level: Level) -> bool {
350 if level == Level::ERROR {
352 return true;
353 }
354
355 let now = Instant::now();
356 let mut window_start = self
357 .window_start
358 .lock()
359 .expect("Mutex poisoning is unexpected in normal operation");
360
361 if now.duration_since(*window_start) > self.window {
363 *window_start = now;
364 self.events_in_window.store(0, Ordering::Relaxed);
365 }
366
367 let current = self.events_in_window.fetch_add(1, Ordering::Relaxed);
369 current < self.max_events
370 }
371}
372
373pub fn log_error(message: &str, context: ErrorContext) {
377 let mut fields = HashMap::new();
378 fields.insert("component".to_string(), context.component.to_string());
379 fields.insert("operation".to_string(), context.operation.to_string());
380
381 if let Some(conn_id) = context.connection_id {
382 fields.insert("conn_id".to_string(), format!("{conn_id:?}"));
383 }
384
385 for (key, value) in context.additional_fields {
386 fields.insert(key.to_string(), value.to_string());
387 }
388
389 logger().log_event(LogEvent {
390 timestamp: Instant::now(),
391 level: Level::ERROR,
392 target: format!("ant_quic::{}", context.component),
393 message: message.to_string(),
394 fields,
395 span_id: None,
396 });
397}
398
399pub fn log_warning(message: &str, context: WarningContext) {
401 let mut fields = HashMap::new();
402 fields.insert("component".to_string(), context.component.to_string());
403
404 for (key, value) in context.details {
405 fields.insert(key.to_string(), value.to_string());
406 }
407
408 logger().log_event(LogEvent {
409 timestamp: Instant::now(),
410 level: Level::WARN,
411 target: format!("ant_quic::{}", context.component),
412 message: message.to_string(),
413 fields,
414 span_id: None,
415 });
416}
417
418pub fn log_info(message: &str, context: InfoContext) {
420 let mut fields = HashMap::new();
421 fields.insert("component".to_string(), context.component.to_string());
422
423 for (key, value) in context.details {
424 fields.insert(key.to_string(), value.to_string());
425 }
426
427 logger().log_event(LogEvent {
428 timestamp: Instant::now(),
429 level: Level::INFO,
430 target: format!("ant_quic::{}", context.component),
431 message: message.to_string(),
432 fields,
433 span_id: None,
434 });
435}
436
437pub fn log_debug(message: &str, context: DebugContext) {
439 let mut fields = HashMap::new();
440 fields.insert("component".to_string(), context.component.to_string());
441
442 for (key, value) in context.details {
443 fields.insert(key.to_string(), value.to_string());
444 }
445
446 logger().log_event(LogEvent {
447 timestamp: Instant::now(),
448 level: Level::DEBUG,
449 target: format!("ant_quic::{}", context.component),
450 message: message.to_string(),
451 fields,
452 span_id: None,
453 });
454}
455
456pub fn log_trace(message: &str, context: TraceContext) {
458 let mut fields = HashMap::new();
459 fields.insert("component".to_string(), context.component.to_string());
460
461 for (key, value) in context.details {
462 fields.insert(key.to_string(), value.to_string());
463 }
464
465 logger().log_event(LogEvent {
466 timestamp: Instant::now(),
467 level: Level::TRACE,
468 target: format!("ant_quic::{}", context.component),
469 message: message.to_string(),
470 fields,
471 span_id: None,
472 });
473}
474
475pub fn create_connection_span(conn_id: &ConnectionId) -> Span {
477 tracing::span!(
478 Level::DEBUG,
479 "connection",
480 conn_id = %format!("{:?}", conn_id),
481 )
482}
483
484pub fn create_frame_span(frame_type: FrameType) -> Span {
486 tracing::span!(
487 Level::TRACE,
488 "frame",
489 frame_type = ?frame_type,
490 )
491}