Skip to main content

scrapling_spider/
logging.rs

1//! Thread-safe log-level counter for crawl diagnostics.
2//!
3//! The [`LogCounter`] keeps a running tally of log messages by level (debug,
4//! info, warning, error). It uses atomic integers so it can be safely shared
5//! across threads without a mutex.
6//!
7//! After the crawl completes, call [`LogCounter::counts`] and assign the
8//! result to [`CrawlStats::log_levels_counter`](crate::result::CrawlStats::log_levels_counter)
9//! to include log-level breakdowns in the final statistics.
10//!
11//! This is the Rust equivalent of the Python scrapling's `LogCounterHandler`.
12
13use std::collections::HashMap;
14use std::sync::Arc;
15use std::sync::atomic::{AtomicU64, Ordering};
16
17/// Counts log events by level for post-crawl diagnostics.
18///
19/// Thread-safe via `Arc<AtomicU64>` counters -- you can clone this struct and
20/// share it across threads or async tasks without synchronization concerns.
21/// Feed it log events via [`increment`](LogCounter::increment) and retrieve
22/// the final tallies via [`counts`](LogCounter::counts).
23#[derive(Debug, Default, Clone)]
24pub struct LogCounter {
25    debug: Arc<AtomicU64>,
26    info: Arc<AtomicU64>,
27    warn: Arc<AtomicU64>,
28    error: Arc<AtomicU64>,
29}
30
31impl LogCounter {
32    /// Creates a new counter with all levels at zero. This is equivalent to
33    /// `LogCounter::default()`.
34    pub fn new() -> Self {
35        Self::default()
36    }
37
38    /// Atomically increments the counter for the given log level. `TRACE`
39    /// events are grouped with `DEBUG` since they serve a similar diagnostic
40    /// purpose. This method uses `Relaxed` ordering because exact accuracy
41    /// is not critical for aggregate counters.
42    pub fn increment(&self, level: tracing::Level) {
43        match level {
44            tracing::Level::DEBUG | tracing::Level::TRACE => {
45                self.debug.fetch_add(1, Ordering::Relaxed);
46            }
47            tracing::Level::INFO => {
48                self.info.fetch_add(1, Ordering::Relaxed);
49            }
50            tracing::Level::WARN => {
51                self.warn.fetch_add(1, Ordering::Relaxed);
52            }
53            tracing::Level::ERROR => {
54                self.error.fetch_add(1, Ordering::Relaxed);
55            }
56        }
57    }
58
59    /// Returns a snapshot of all level counts as a string-keyed map. The keys
60    /// are `"debug"`, `"info"`, `"warning"`, and `"error"`. Assign the
61    /// returned map to [`CrawlStats::log_levels_counter`](crate::result::CrawlStats::log_levels_counter)
62    /// to include log statistics in the crawl output.
63    pub fn counts(&self) -> HashMap<String, u64> {
64        HashMap::from([
65            ("debug".into(), self.debug.load(Ordering::Relaxed)),
66            ("info".into(), self.info.load(Ordering::Relaxed)),
67            ("warning".into(), self.warn.load(Ordering::Relaxed)),
68            ("error".into(), self.error.load(Ordering::Relaxed)),
69        ])
70    }
71}