base: Add structured logging rate limiter and thermald integration (P50)

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.
This commit is contained in:
2026-05-20 18:14:47 +03:00
parent 3890840001
commit fb2de33c6d
2 changed files with 95 additions and 0 deletions
@@ -0,0 +1,93 @@
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);
+ });
+2
View File
@@ -99,6 +99,8 @@ patches = [
"P48-acpid-fan-support.patch",
# P49: Add IRQ affinity logging and CPU tracking to pcid
"P49-irq-affinity-logging.patch",
# P50: Add structured logging rate limiter and thermald integration
"P50-structured-logging.patch",
]
[package]