Custom log levels
Hi,
thank you all for writing a fantastic piece of software.
I am aware of https://github.com/rust-lang/log/issues/334.
My particular use case is having a log level "responsive" between warn and info.
https://github.com/rosenpass/rosenpass/pull/326
Our goal is to be:
- As silent as possible
- Provide essentially a single line of output that we would otherwise mark log level info
Essentially be responsive without polluting logs.
Some ideas how to – cleanly – implement custom log levels:
- Would it be possible to uniquely identify a particular log level through a constant variable? I.e. for instance
::log_level_critical::CRITICALor log level::rosenpass::util::log::level::RESPONSIVEwhich would be distinct from::log_level_responsive::RESPONSIVE. This would allow clean reasoning about custom log levels across crates. - Would it be possible to specify constraints about how log level relate to each other?
use log_custom_levels::{LogLevel, levels::INFO, Sort}; // enum Sort { // Lower, // Equal, // Higher, // } struct LogLevelResponsive { _phantom_private: () } impl LogLevel for LogLevelResponsive { fn log_level_priority<Otr: LogLevelResponsive>(otr: Otr) -> Sort { use Sort::*; match INFO.log_level_priority(otr) { Lower => Lower, Equal => Higher, Higher => Higher, } } } const RESPONSIVE : LogLevelResponsive = LogLevelResponsive { _phantom_private: () };
Hi @koraa 👋
That’s an interesting design direction. I don’t think we’re likely to complicate level management in log; it’s intended to be a direct and straightforward library and using a fixed set of coarse grained levels lets us do things like compile-time filtering.
This is something you could possibly use the existing target field on log records for. They’re populated with the module name by default but can be set to anything through the macros. If you wanted, you could write your own wrapper over the log macros that set it to your custom level and still get very cheap filtering.
I think we could also benefit from potentially enhancing our Metadata type, which is used for filtering, by adding key-values to it so you can filter on those too.
Hi @KodrAus
thanks for the detailed response!
An elegant way to add Key/Value type data to your metadata type would be great; this could directly be used to implement sublevels.
A mechanism based on static or dynamic polymorphism instead of a hash map would be optimal to avoid heap allocations during logging. This would also help to globally identify keys. If arbitrary strings where used as keys, there is a danger to get keys from different crates mixed up.
Anyway, thanks for the hard work on this crate!
An elegant way to add Key/Value type data to your metadata type would be great; this could directly be used to implement sublevels.
Take a look at the kv module documentation: https://docs.rs/log/latest/log/kv/index.html.
A mechanism based on static or dynamic polymorphism instead of a hash map would be optimal to avoid heap allocations during logging. This would also help to globally identify keys. If arbitrary strings where used as keys, there is a danger to get keys from different crates mixed up.
I've used special targets in my create to "add" additonal log levels. Some examples:
request!macro for logging requests: https://docs.rs/std-logger/latest/std_logger/macro.request.html- Using a target + log level to change the printed log level: https://github.com/Thomasdezeeuw/std-logger/blob/75f56b4e8c314aaee125cc1a05f05cf915cb12d8/src/format/gcloud.rs#L44-L49. In this case
target = "panic" && level = Errormeans acriticalerror.
Closing this in favour of key-values which are no stable and possibly adding new severities in #334.