The log! macro runs whenever the following conditions are met:
if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() {
The log_enabled! macro returns the following:
lvl <= $crate::STATIC_MAX_LEVEL
&& lvl <= $crate::max_level()
&& $crate::__private_api_enabled(lvl, $target)
that is, it also calls into the Log::enabled impl.
Because of this, there can be cases where logs will be output or not, depending on whether they're guarded behind a log_enabled!(), if the implementation of Log::enabled isn't consistent with what's in the Log::log implementation. That means that it's the implementation's duty to ensure that it's consistent with itself, which is an easy footgun, for example in Tokyo's tracing-log crate: tokio-rs/tracing@5fdbcbf.
Even the doc showing an example impl of Log suggests doing just that: https://docs.rs/log/0.4.14/log/#implementing-a-logger, so making it the default could avoid issues.
In particular, in the suggested implementation of Log::log:
if (self.enabled()) {
do_actual_logging(record);
}
The log record that was instantiated in the first place could be just thrown away just after being created, if it didn't pass the self.enabled() check. It's effectively a problem if the log contains lots of data that takes time to generate in the first place, because this data might be thrown away directly, even if it's created as an argument to a log macro. Having the equivalent of a log_enabled! check in the macro would avoid that problem too, by putting the costly computation into the if's body itself, instead of computing it and then only deferring to the Log impl to decide whether it's used or not.
Would people be inclined to have the equivalent of the log_enabled! check happen directly in the log! macro, to avoid running into those two pitfalls? It would make log_enabled! a bit less useful, but still useful in my opinion for cases where one doesn't want to use expression blocks inside log macros.
The
log!macro runs whenever the following conditions are met:The
log_enabled!macro returns the following:that is, it also calls into the
Log::enabledimpl.Because of this, there can be cases where logs will be output or not, depending on whether they're guarded behind a
log_enabled!(), if the implementation ofLog::enabledisn't consistent with what's in theLog::logimplementation. That means that it's the implementation's duty to ensure that it's consistent with itself, which is an easy footgun, for example in Tokyo'stracing-logcrate: tokio-rs/tracing@5fdbcbf.Even the doc showing an example impl of
Logsuggests doing just that: https://docs.rs/log/0.4.14/log/#implementing-a-logger, so making it the default could avoid issues.In particular, in the suggested implementation of
Log::log:The log record that was instantiated in the first place could be just thrown away just after being created, if it didn't pass the
self.enabled()check. It's effectively a problem if the log contains lots of data that takes time to generate in the first place, because this data might be thrown away directly, even if it's created as an argument to a log macro. Having the equivalent of alog_enabled!check in the macro would avoid that problem too, by putting the costly computation into theif's body itself, instead of computing it and then only deferring to theLogimpl to decide whether it's used or not.Would people be inclined to have the equivalent of the
log_enabled!check happen directly in thelog!macro, to avoid running into those two pitfalls? It would makelog_enabled!a bit less useful, but still useful in my opinion for cases where one doesn't want to use expression blocks insidelogmacros.