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}