defmt
defmt copied to clipboard
Feature flag to print in a non-deferred format.
I'm using defmt as my logging library. I like the efficiency that it enables. However sometimes, I want to be able to view the data without running a printer on the host machine to decode the data. I know that this is antithetical to the design of defmt, but there are several motivating reasons:
- Often, I cannot use probe-rs based tools, which means that I need to rely on other tools to read UART, for example
- Often these other tools expect utf-8 encoded data, or don't handle reconnection well, or print data other than the raw bytes to their stdout. Basically these tools tend to not work well as a source for defmt-print's pipe.
- Sometimes I cannot even get a stdout for the serial data. This happens for example when I'm debugging my hardware with sigrok and a logic analyzer. defmt is not one of the supported encodings for the serial data, I can really only pick hex, ascii, etc.
- Sometimes the binary that corresponds to the firmware is unknown. It would suck to have to tear out all of the logging infrastructure in the app and revert to something like
log
just to make this fix. Maybe my types don't even supportDebug
and thats going to require a big refactor! It would be nice to have the comfort of knowing that for sure I'll be able to usedefmt
and choose at the last minute before I ship my firmware whether the logs will be deferred or not.
For the exclusive purpose of enabling easier debugging in these scenarios, would it be possible to turn off the "deferred" part of defmt via a feature flag?
I'd recommend you to do something similar to what smoltcp
does (link):
#[cfg(not(test))]
#[cfg(feature = "log")]
macro_rules! net_log {
(trace, $($arg:expr),*) => { log::trace!($($arg),*) };
(debug, $($arg:expr),*) => { log::debug!($($arg),*) };
}
#[cfg(feature = "defmt")]
macro_rules! net_log {
(trace, $($arg:expr),*) => { defmt::trace!($($arg),*) };
(debug, $($arg:expr),*) => { defmt::debug!($($arg),*) };
}
Then you can select if you want to use defmt
or log
with a cargo feature of your application.
This won't let me use the same defmt
global logger though, right? I rely on those for transport of the serialized data. I would need to implement a new one for each transport type, that uses log
instead, which seems quite painful. And many types might not implement Debug
, they may only implement defmt::Format
This won't let me use the same
defmt
global logger though, right? I rely on those for transport of the serialized data. I would need to implement a new one for each transport type, that useslog
instead, which seems quite painful.
Regarding the transport, you could try out https://crates.io/crates/rtt-logger/, which seems to enable using log
together with rtt
.
And many types might not implement
Debug
, they may only implementdefmt::Format
As long as you are in control of the types this seems solvable, but I agree that this becomes annoying if it is a foreign type. Let me think about it.
Regarding the transport, you could try out https://crates.io/crates/rtt-logger/, which seems to enable using log together with rtt.
My firmware uses several different defmt
implementations. Some for UART some for RTT some for usb serial, and depending on the platform I'm targetting, these implementations may use entirely different crates via cfg gating. I don't think its really great for firmware authors to need to add even more dependencies and complicated cfg
gating when they could just reuse the existing defmt logger ecosystem.
This seems to be a pre-requisite to #716 (although I suspect that could be achieved by loading/embedding the decoder info into the binary).
Having the #[cfg(...)]
for logging is "servicable" but has limitations (e.g. slightly different formatting args, different traits required, ...) and it would be a nice QoL improvement if a crate could just use defmt for logging across all targets (device/host/serial console)
Motivation
...
- Remove duplication: redirecting-to/co-existance with log/tracing would remove the need to duplicate/
cfg!(...)
logging statements depending on the target in many cases
- currently I often end up with both log and defmt as optional dependencies and a couple of
#[cfg(...)]
statements to make it work (crate level logging modules + extras when formatting compatibility is an issue).cargo build --all-targets --all-features
could work which helps with having standard CI scripts