Skip to content

Commit 622e887

Browse files
mxgreynwn
andauthored
Address feedback for logging PR
Signed-off-by: Michael X. Grey <[email protected]> Signed-off-by: Michael X. Grey <[email protected]> Signed-off-by: Michael X. Grey <[email protected]> Co-authored-by: Nathan Wiebe Neufeldt <[email protected]>
1 parent 196569c commit 622e887

File tree

4 files changed

+124
-24
lines changed

4 files changed

+124
-24
lines changed

rclrs/src/logging.rs

Lines changed: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ macro_rules! log {
8181
// Note: that issue appears to be specific to jetbrains intellisense however,
8282
// observed same/similar behaviour with rust-analyzer/rustc
8383
use std::sync::{Once, OnceLock, Mutex};
84-
use std::time::SystemTime;
84+
use std::time::{SystemTime, Instant};
8585

8686
// We wrap the functional body of the macro inside of a closure which
8787
// we immediately trigger. This allows us to use `return` to exit the
@@ -128,19 +128,64 @@ macro_rules! log {
128128
// of that interval.
129129
let throttle = params.get_throttle();
130130
if throttle > std::time::Duration::ZERO {
131-
static LAST_LOG_TIME: OnceLock<Mutex<SystemTime>> = OnceLock::new();
132-
let last_log_time = LAST_LOG_TIME.get_or_init(|| {
133-
Mutex::new(std::time::SystemTime::now())
134-
});
135-
136-
if !first_time {
137-
let now = std::time::SystemTime::now();
138-
let mut previous = last_log_time.lock().unwrap();
139-
if now >= *previous + throttle {
140-
*previous = now;
141-
} else {
142-
// We are still inside the throttle interval, so just exit here.
143-
return;
131+
match params.get_throttle_clock() {
132+
$crate::ThrottleClock::SteadyTime => {
133+
static LAST_LOG_STEADY_TIME: OnceLock<Mutex<Instant>> = OnceLock::new();
134+
let last_log_time = LAST_LOG_STEADY_TIME.get_or_init(|| {
135+
Mutex::new(Instant::now())
136+
});
137+
138+
if !first_time {
139+
let now = Instant::now();
140+
let mut previous = last_log_time.lock().unwrap();
141+
if now >= *previous + throttle {
142+
*previous = now;
143+
} else {
144+
// We are still inside the throttle interval, so just exit here.
145+
return;
146+
}
147+
}
148+
}
149+
$crate::ThrottleClock::SystemTime => {
150+
static LAST_LOG_SYSTEM_TIME: OnceLock<Mutex<SystemTime>> = OnceLock::new();
151+
let last_log_time = LAST_LOG_SYSTEM_TIME.get_or_init(|| {
152+
Mutex::new(SystemTime::now())
153+
});
154+
155+
if !first_time {
156+
let now = SystemTime::now();
157+
let mut previous = last_log_time.lock().unwrap();
158+
if now >= *previous + throttle {
159+
*previous = now;
160+
} else {
161+
// We are still inside the throttle interval, so just exit here.
162+
return;
163+
}
164+
}
165+
}
166+
$crate::ThrottleClock::Clock(clock) => {
167+
static LAST_LOG_CLOCK_TIME: OnceLock<Mutex<$crate::Time>> = OnceLock::new();
168+
let last_log_time = LAST_LOG_CLOCK_TIME.get_or_init(|| {
169+
Mutex::new(clock.now())
170+
});
171+
172+
if !first_time {
173+
let now = clock.now();
174+
let mut previous = last_log_time.lock().unwrap();
175+
176+
let new_interval = !now.compare_with(
177+
&(previous.clone() + throttle),
178+
|now, interval| now < interval,
179+
)
180+
.is_some_and(|eval| eval);
181+
182+
if new_interval {
183+
*previous = now;
184+
} else {
185+
// We are still inside the throttle interval, so just exit here.
186+
return;
187+
}
188+
}
144189
}
145190
}
146191
}
@@ -592,8 +637,26 @@ mod tests {
592637
assert_eq!(last_severity(), LogSeverity::Error);
593638
assert_eq!(count_message("error for custom logger"), 2);
594639

595-
reset_logging_output_handler();
640+
// Test whether throttling works correctly with a ROS clock
641+
let (clock, source) = Clock::with_source();
642+
source.set_ros_time_override(0);
643+
644+
for i in 0..15 {
645+
log!(
646+
"logger"
647+
.throttle(Duration::from_nanos(10))
648+
.throttle_clock(ThrottleClock::Clock(&clock)),
649+
"custom clock throttled message",
650+
);
651+
source.set_ros_time_override(i);
652+
}
596653

654+
// The throttle interval is 10ns and the loop shifted the time from 0ns
655+
// to 14ns, triggering the log macro once per nanosecond. That means we
656+
// should see two messages in the log.
657+
assert_eq!(count_message("custom clock throttled message"), 2);
658+
659+
reset_logging_output_handler();
597660
Ok(())
598661
}
599662

rclrs/src/logging/log_params.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::rcl_bindings::RCUTILS_LOG_SEVERITY;
1+
use crate::{rcl_bindings::RCUTILS_LOG_SEVERITY, Clock};
22
use std::{borrow::Borrow, ffi::CString, time::Duration};
33

44
/// These parameters determine the behavior of an instance of logging.
@@ -16,6 +16,8 @@ pub struct LogParams<'a> {
1616
/// e.g. if `log!(logger.throttle(Duration::from_secs(1)), "message");` is called every 10ms, it will
1717
/// nevertheless only be published once per second.
1818
throttle: Duration,
19+
/// Specify a clock to use for throttling. By default this will be [`ThrottleClock::SteadyTime`].
20+
throttle_clock: ThrottleClock<'a>,
1921
/// The log message will only published if the specified expression evaluates to true
2022
only_if: bool,
2123
}
@@ -28,6 +30,7 @@ impl<'a> LogParams<'a> {
2830
severity: Default::default(),
2931
occurs: Default::default(),
3032
throttle: Duration::new(0, 0),
33+
throttle_clock: Default::default(),
3134
only_if: true,
3235
}
3336
}
@@ -52,6 +55,11 @@ impl<'a> LogParams<'a> {
5255
self.throttle
5356
}
5457

58+
/// Get the throttle clock
59+
pub fn get_throttle_clock(&self) -> ThrottleClock<'a> {
60+
self.throttle_clock
61+
}
62+
5563
/// Get the arbitrary filter set by the user
5664
pub fn get_user_filter(&self) -> bool {
5765
self.only_if
@@ -98,6 +106,13 @@ pub trait ToLogParams<'a>: Sized {
98106
params
99107
}
100108

109+
/// Set the clock that will be used to control [throttling][Self::throttle].
110+
fn throttle_clock(self, clock: ThrottleClock<'a>) -> LogParams<'a> {
111+
let mut params = self.to_log_params();
112+
params.throttle_clock = clock;
113+
params
114+
}
115+
101116
/// The log will not be published if a `false` expression is passed into
102117
/// this function.
103118
///
@@ -239,6 +254,18 @@ pub enum LogOccurrence {
239254
SkipFirst,
240255
}
241256

257+
/// This parameter can specify a type of clock for a logger to use when throttling.
258+
#[derive(Debug, Default, Clone, Copy)]
259+
pub enum ThrottleClock<'a> {
260+
/// Use [`std::time::Instant`] as a clock.
261+
#[default]
262+
SteadyTime,
263+
/// Use [`std::time::SystemTime`] as a clock.
264+
SystemTime,
265+
/// Use some [`Clock`] as a clock.
266+
Clock(&'a Clock),
267+
}
268+
242269
impl Default for LogOccurrence {
243270
fn default() -> Self {
244271
Self::All

rclrs/src/logging/logger.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{borrow::Borrow, ffi::CString};
1+
use std::{borrow::Borrow, ffi::CString, sync::Arc};
22

33
use crate::{
44
rcl_bindings::{rcutils_logging_set_default_logger_level, rcutils_logging_set_logger_level},
@@ -21,26 +21,30 @@ use crate::{
2121
///
2222
/// [1]: crate::log
2323
/// [2]: crate::Node::logger
24+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
2425
pub struct Logger {
25-
name: Box<str>,
26-
c_name: CString,
26+
name: Arc<str>,
27+
c_name: Arc<CString>,
2728
}
2829

2930
impl Logger {
3031
/// Create a new logger with the given name.
3132
pub fn new(name: impl Borrow<str>) -> Result<Logger, RclrsError> {
32-
let name: Box<str> = name.borrow().into();
33-
let c_name = match CString::new(name.clone().into_string()) {
33+
let name: Arc<str> = name.borrow().into();
34+
let c_name = match CString::new((*name).to_owned()) {
3435
Ok(c_name) => c_name,
3536
Err(err) => {
3637
return Err(RclrsError::StringContainsNul {
37-
s: name.into_string(),
38+
s: (*name).to_owned(),
3839
err,
3940
});
4041
}
4142
};
4243

43-
Ok(Self { name, c_name })
44+
Ok(Self {
45+
name,
46+
c_name: Arc::new(c_name),
47+
})
4448
}
4549

4650
/// Create a new logger which will be a child of this logger.
@@ -99,3 +103,9 @@ impl Default for Logger {
99103
Self::new("").unwrap()
100104
}
101105
}
106+
107+
impl std::hash::Hash for Logger {
108+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
109+
self.name.hash(state);
110+
}
111+
}

rclrs/src/node.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ pub struct Node {
8282
pub(crate) struct NodeHandle {
8383
pub(crate) rcl_node: Mutex<rcl_node_t>,
8484
pub(crate) context_handle: Arc<ContextHandle>,
85-
/// In the humbe distro, rcl is sensitive to the address of the rcl_node_t
85+
/// In the humble distro, rcl is sensitive to the address of the rcl_node_t
8686
/// object being moved (this issue seems to be gone in jazzy), so we need
8787
/// to initialize the rcl_node_t in-place inside this struct. In the event
8888
/// that the initialization fails (e.g. it was created with an invalid name)

0 commit comments

Comments
 (0)