Add basic features to CursiveLogger#719
Conversation
cursive-core/src/logger.rs
Outdated
| pub fn init(self) { | ||
| reserve_logs(self.log_size); | ||
| log::set_logger(Box::leak(Box::new(self))).unwrap(); | ||
| log::set_max_level(log::LevelFilter::Trace); |
There was a problem hiding this comment.
We could set the maximum of (int, ext) levels to avoid logging trace if it wouldn't be seen anyway.
cursive-core/src/logger.rs
Outdated
| /// Installs the logger with log. Calling twice will panic. | ||
| pub fn init(self) { | ||
| reserve_logs(self.log_size); | ||
| log::set_logger(Box::leak(Box::new(self))).unwrap(); |
There was a problem hiding this comment.
We may want to use set_boxed_logger, which hides the leaking away so it feels less dirty (even though it's really the same):
https://docs.rs/log/latest/log/fn.set_boxed_logger.html
I guess a "leak-free" version would keep CursiveLogger zero-sized and rely on a global/singleton state instead, but that may be more complex, with quite limited benefits (prevents leaks if init() is called repeatedly).
There was a problem hiding this comment.
Yeah, I originally had used set_boxed_logger but that's hidden behind the std feature flag for log and I didn't want to change the dependencies unless I had to.
As written, calling init() multiple times panics anyways, so I don't think we have to worry about leaks. Having everything kept in global state might not be a bad idea since we shouldn't expect anyone to create multiple CursiveLogger objects anyways.
I made another branch for this: cursivelogger_global. It is a bit more complicated and I don't know if I like the API ergonomics. One plus though is being able to change the log filter levels after initialization. I don't know if that's worth it.
There was a problem hiding this comment.
Why the Mutex<Cell<...>> in that branch? Could a simple mutex work too? Or even RwLock, might make the read-only case faster.
There was a problem hiding this comment.
I think I like this global branch! No feature change for the log dependency, uses lazy_static that we were already using...
There was a problem hiding this comment.
Yeah, both work, you're right. I don't know what I was thinking with Mutex<Cell<...>>. Since LOGS is a Mutex anyway I'm not sure there's much of a speed difference.
There was a problem hiding this comment.
I created a PR for cursivelogger_global. There is a tiny issue with set_log_size though.
cursive-core/src/logger.rs
Outdated
| fn enabled(&self, _metadata: &log::Metadata) -> bool { | ||
| true | ||
| fn enabled(&self, metadata: &log::Metadata) -> bool { | ||
| if metadata.target().contains("cursive_core") { |
There was a problem hiding this comment.
Maybe we should be more restrictive, requiring this to be a prefix of the target?
|
Thanks for the work! Just a couple nitpicks. |
* Make CursiveLogger use globals * Avoid duplicate log queue reservations * Check log queue size on init, get rid of `LOG_SIZE`
|
@gyscos |
There was a problem hiding this comment.
Maybe we could use VecDeque::with_capacity from lazy_static! rather than manually calling reserve_logs. It'd still only be called on the first use (so no wasted memory if users don't use this logger).
Not sure we even need to expose reserve_logs? 🤷
What about the change to not having a set_log_size() function and just using reserve_logs()? (blaisdellma#1 (comment))
Yeah let's keep the API minimal, any power user should use a real serious solution.
cursive-core/src/logger.rs
Outdated
| } | ||
|
|
||
| /// Sets the internal log filter level. | ||
| pub fn set_int_filter_level(level: log::LevelFilter) { |
There was a problem hiding this comment.
Nit: could we call it set_internal_filter_level, to prevent any ambiguity around int?
Let's also extend set_external_filter_level for consistency.
I don't care as much about the lazy static global as it's not public.
cursive-core/src/logger.rs
Outdated
| /// If `RUST_LOG` is set, then both internal and external log levels are set to match. | ||
| /// If `CURSIVE_LOG` is set, then the internal log level is set to match with precedence over | ||
| /// `RUST_LOG`. | ||
| pub fn set_filter_levels_with_env() { |
There was a problem hiding this comment.
Nit
| pub fn set_filter_levels_with_env() { | |
| pub fn set_filter_levels_from_env() { |
cursive-core/src/logger.rs
Outdated
| /// `RUST_LOG`. | ||
| pub fn set_filter_levels_with_env() { | ||
| if let Ok(rust_log) = std::env::var("RUST_LOG") { | ||
| if let Ok(filter_level) = log::LevelFilter::from_str(&rust_log) { |
There was a problem hiding this comment.
I'd log (ha!) a warning in case we can't parse the level, rather than silently ignoring it.
There was a problem hiding this comment.
This is meant to be called before the logger is initialized, so any logged warning would be ignored. I guess you could call it after initialization, but then when initializing the log::set_max_level() would be based on the default filter levels (which is fine, cause it defaults to trace). I'll add it anyway.
I'm personally in favor of not making breaking changes to the API unless necessary. I don't know that it hurts to have it there, but it's your call. |
|
Thanks again for all the work and updates, and sorry for the trouble! |
Hello,
This is my attempt at following the first of my suggestions in #714 .
I tried to leave as much alone as I could so that there would be no breaking changes with as many useful features as possible. Anyone using the logger now shouldn't have to change anything and get the exact same behavior. There shouldn't be any breaking changes to the existing API, either in function signatures or behavior.
Logging would use two different filter levels, one each for logs generated internally by cursive and externally by whatever else. This solves the issue in #714 as the debug messages won't clog the log with all the redraws from scrolling (assuming the filter levels are set appropriately). These levels are set either directly or by the environment variables
RUST_LOGandCURSIVE_LOG. I also added the ability to change the log queue size too. I hope this meets option 2 in #467 as a "good enough" feature set.Two small examples:
The whole
Box::leak(Box::new(self))thing is to avoid having to add thestdfeature flag tologand changeCargo.toml. This is exactly howloghandlesset_boxed_loggerinternally.