fb2de33c6d
Add RateLimitedLog to common::logger for per-message rate limiting with "last message repeated N times" warnings. Add structured_log! macro for key=value formatted logs. Update thermald to rate-limit the max-temp summary line (30s interval) to reduce log volume.
94 lines
3.4 KiB
Diff
94 lines
3.4 KiB
Diff
diff --git a/drivers/common/src/lib.rs b/drivers/common/src/lib.rs
|
|
index a55014b9..6b7ab2fe 100644
|
|
--- a/drivers/common/src/lib.rs
|
|
+++ b/drivers/common/src/lib.rs
|
|
@@ -25 +25 @@ pub mod timeout;
|
|
-pub use logger::{file_level, output_level, setup_logging};
|
|
+pub use logger::{file_level, output_level, setup_logging, RateLimitedLog};
|
|
diff --git a/drivers/common/src/logger.rs b/drivers/common/src/logger.rs
|
|
index a531edd9..8b65f6bd 100644
|
|
--- a/drivers/common/src/logger.rs
|
|
+++ b/drivers/common/src/logger.rs
|
|
@@ -0,0 +1,2 @@
|
|
+use std::cell::RefCell;
|
|
+use std::collections::HashMap;
|
|
@@ -71,0 +74,65 @@ pub fn setup_logging(
|
|
+/// A simple per-message rate limiter to prevent log spam.
|
|
+///
|
|
+/// Tracks the last emission time for each unique message key. If the same
|
|
+/// key is logged again within `interval`, the message is suppressed and a
|
|
+/// "last message repeated N times" warning is emitted instead.
|
|
+pub struct RateLimitedLog {
|
|
+ interval: std::time::Duration,
|
|
+ last_emission: RefCell<HashMap<String, std::time::Instant>>,
|
|
+ suppress_count: RefCell<HashMap<String, u64>>,
|
|
+}
|
|
+
|
|
+impl RateLimitedLog {
|
|
+ pub fn new(interval_secs: u64) -> Self {
|
|
+ Self {
|
|
+ interval: std::time::Duration::from_secs(interval_secs),
|
|
+ last_emission: RefCell::new(HashMap::new()),
|
|
+ suppress_count: RefCell::new(HashMap::new()),
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /// Log a message through the rate limiter.
|
|
+ pub fn log(&self, key: &str, log_fn: impl FnOnce()) {
|
|
+ let now = std::time::Instant::now();
|
|
+ let mut last_map = self.last_emission.borrow_mut();
|
|
+ let mut count_map = self.suppress_count.borrow_mut();
|
|
+
|
|
+ if let Some(last) = last_map.get(key) {
|
|
+ if now.duration_since(*last) < self.interval {
|
|
+ *count_map.entry(key.to_string()).or_insert(0) += 1;
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if let Some(count) = count_map.remove(key) {
|
|
+ if count > 0 {
|
|
+ log::warn!("RateLimitedLog: last message '{}' repeated {} times", key, count);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ last_map.insert(key.to_string(), now);
|
|
+ log_fn();
|
|
+ }
|
|
+}
|
|
+
|
|
+/// Format a structured log message with key=value pairs.
|
|
+///
|
|
+/// Example: `structured_log!("thermald", "event=temperature_read", "zone=CPU", "temp=45.2")`
|
|
+/// produces: `thermald: event=temperature_read zone=CPU temp=45.2`
|
|
+#[macro_export]
|
|
+macro_rules! structured_log {
|
|
+ ($source:expr, $($key:expr),+ $(,)?) => {
|
|
+ {
|
|
+ let mut msg = String::new();
|
|
+ msg.push_str($source);
|
|
+ msg.push_str(": ");
|
|
+ $(
|
|
+ msg.push_str($key);
|
|
+ msg.push(' ');
|
|
+ )+
|
|
+ msg.pop();
|
|
+ log::info!("{}", msg);
|
|
+ }
|
|
+ };
|
|
+}
|
|
+
|
|
diff --git a/drivers/thermald/src/main.rs b/drivers/thermald/src/main.rs
|
|
index b8d271b5..e64cf162 100644
|
|
--- a/drivers/thermald/src/main.rs
|
|
+++ b/drivers/thermald/src/main.rs
|
|
@@ -86,0 +87,2 @@ fn main() -> Result<()> {
|
|
+ let rate_limiter = common::RateLimitedLog::new(30);
|
|
+
|
|
@@ -138 +140,4 @@ fn main() -> Result<()> {
|
|
- log::info!("thermald: max temp = {:.1}C from {}", max_temp, max_source);
|
|
+ let source = max_source.clone();
|
|
+ rate_limiter.log(&format!("max_temp_{:.0}", max_temp), || {
|
|
+ log::info!("thermald: max temp = {:.1}C from {}", max_temp, source);
|
|
+ });
|