From 0d2ca8aa44c4ec2a0f3b702e4a09fc1c192dfec9 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 7 Aug 2026 15:17:12 +0800 Subject: [PATCH] log_throttle: add throttled_log!, the general per-call-site form The type alone still needs a static plus an `if let` at every use, which is why the codebase kept hand-rolling equivalents. The macro declares the static for itself, so adding a bounded site is one line, and it appends the multiplicity only when there is one to report - an isolated event logs exactly as it would unthrottled. Count semantics stay inclusive (the reported number is the total this line stands for, first occurrence = 1), so a reader needs no arithmetic; the type's docs now point at the macro and say when to reach past it. Also rustfmt the module and webrtc.rs, which had drifted (no CI gate enforces it on this branch). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01ExUfAkYbq8UC9pQCiLy8TQ --- src/log_throttle.rs | 37 ++++++++++++++++++++++++++++++++++++- src/webrtc.rs | 29 +++++++++++++++-------------- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/log_throttle.rs b/src/log_throttle.rs index 02bb7f208..b8043ff93 100644 --- a/src/log_throttle.rs +++ b/src/log_throttle.rs @@ -9,6 +9,10 @@ use std::time::{Duration, Instant}; /// hide real faults, so keep one line per interval and carry the count of everything /// suppressed since the last one. /// +/// Prefer the [`throttled_log!`](crate::throttled_log) macro, which declares the static for +/// you. Reach for this type directly only when the count belongs somewhere other than the end +/// of the line, or when the decision drives more than a log call. +/// /// Declare one per site (they do not share counts): /// /// ```ignore @@ -67,6 +71,33 @@ impl LogThrottle { } } +/// Log at most one line per interval from this call site, suffixed with the number of +/// occurrences it stands for. +/// +/// Each expansion declares its own hidden static, so two sites never share a count and +/// adding one is a single line: +/// +/// ```ignore +/// throttled_log!(Duration::from_secs(5), warn, "rejected ipc peer {peer_pid:?}"); +/// ``` +/// +/// An isolated event logs unchanged; a burst collapses to `... (x47)`. The count includes +/// the occurrence being reported, so it reads as a total rather than as "and N more". +#[macro_export] +macro_rules! throttled_log { + ($interval:expr, $level:ident, $($arg:tt)+) => {{ + static THROTTLE: $crate::log_throttle::LogThrottle = + $crate::log_throttle::LogThrottle::new($interval); + if let Some(n) = THROTTLE.due() { + if n > 1 { + $crate::log::$level!("{} (x{})", format_args!($($arg)+), n); + } else { + $crate::log::$level!("{}", format_args!($($arg)+)); + } + } + }}; +} + #[cfg(test)] mod tests { use super::*; @@ -83,7 +114,11 @@ mod tests { // The send side succeeding must not hand the recv side a fresh emit slot. assert_eq!(recv.due(), None); } - assert_eq!(send.due(), Some(1), "the other direction keeps its own slot"); + assert_eq!( + send.due(), + Some(1), + "the other direction keeps its own slot" + ); } #[test] diff --git a/src/webrtc.rs b/src/webrtc.rs index 9ecd98da0..f3c8cce76 100644 --- a/src/webrtc.rs +++ b/src/webrtc.rs @@ -461,9 +461,8 @@ impl WebRTCStream { // Only tear down on the terminal states so a short network blip (Wi-Fi roam, // sleep/wake, cell handover) does not permanently kill an established session. RTCPeerConnectionState::Failed | RTCPeerConnectionState::Closed => { - let _ = on_connection_notify.send(WebRTCConnectionState::Closed( - s.to_string(), - )); + let _ = + on_connection_notify.send(WebRTCConnectionState::Closed(s.to_string())); log::debug!("WebRTC session closing due to {}", s); let _ = stream_for_close2.lock().await.close().await; log::debug!("WebRTC session stream closed"); @@ -698,9 +697,7 @@ impl WebRTCStream { == RTCIceCandidateType::Relay ) }; - Some( - is_relay(&pair.local_candidate_id) || is_relay(&pair.remote_candidate_id), - ) + Some(is_relay(&pair.local_candidate_id) || is_relay(&pair.remote_candidate_id)) } #[inline] @@ -945,7 +942,9 @@ impl WebRTCStream { FRAG_END => None, FRAG_MORE if n > 1 => None, FRAG_MORE => Some("FRAG_MORE fragment carries no payload".to_owned()), - other => Some(format!("fragment header {other} is neither FRAG_END nor FRAG_MORE")), + other => Some(format!( + "fragment header {other} is neither FRAG_END nor FRAG_MORE" + )), }; if let Some(why) = bad { *acc = BytesMut::new(); @@ -1068,10 +1067,7 @@ mod tests { "turn:example.com:3478" ); assert_eq!(WebRTCStream::get_ice_servers().len(), 2); - config::Config::set_option( - "ice-servers".to_string(), - "".to_string(), - ); + config::Config::set_option("ice-servers".to_string(), "".to_string()); } #[test] @@ -1362,7 +1358,11 @@ IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDY0MDA4XHJcbmE9ZW5kLW9mLWNhbmRpZGF0ZXNc let big = vec![0xABu8; 200_000]; offerer.send_raw(big.clone()).await.unwrap(); let got = answerer.next().await.unwrap().unwrap(); - assert_eq!(got.len(), big.len(), "large message must survive fragmentation"); + assert_eq!( + got.len(), + big.len(), + "large message must survive fragmentation" + ); assert_eq!(&got[..], &big[..]); // Reverse direction. @@ -1407,7 +1407,9 @@ IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDY0MDA4XHJcbmE9ZW5kLW9mLWNhbmRpZGF0ZXNc lead.put_u8(FRAG_MORE); lead.put_slice(b"leading!"); dc.write(&lead.freeze()).await.unwrap(); - dc.write(&bytes::Bytes::copy_from_slice(frame)).await.unwrap(); + dc.write(&bytes::Bytes::copy_from_slice(frame)) + .await + .unwrap(); let err = answerer .next() @@ -1472,5 +1474,4 @@ IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDY0MDA4XHJcbmE9ZW5kLW9mLWNhbmRpZGF0ZXNc .await .expect("concurrent WebRTC sends did not complete in time"); } - }