log_to_defmt/lib.rs
1#![no_std]
2//! This is a logging adapter that acts as an implementation of the [log crate] and hands the
3//! rendered messages off to the [defmt crate].
4//!
5//! Using this is generally not recommended: Once the log infrastructure is pulled into a project,
6//! Rust's string formatting is brought in, avoiding which is a big part of the point of defmt.
7//!
8//! However, this can be useful during development, when debugging a particular library (that
9//! optionally produces messages through log) on a platform for which a defmt output has already
10//! been established.
11//!
12//! [log crate]: https://crates.io/crates/log
13//! [defmt crate]: https://crates.io/crates/defmt
14//!
15//! ### Maturity
16//!
17//! The current implementation of this takes a huge amount of shortcuts: not only does it not
18//! convert log levels, it also hardwires some to the most verbose level, discards lots of
19//! information that would otherwise be available, and uses a fixed size buffer.
20//!
21//! Future iterations of this crate are expected to address these on demand; for example, this
22//! could gain a build time configuration mechanism for the maximum expected length, or an `alloc`
23//! feature that renders the log messages to a `Vec` instead of a `heapless::Vec` before handing
24//! them off to defmt as a slice.
25//!
26//! The crate will likely introduce such features (altering its behavior) without declaring
27//! breaking changes; users are advised to configure their error levels to match which messages
28//! they expect to see.
29
30/// Set up log output to be forwarded to defmt
31///
32/// If the global logger can not be set, this fails silently (in the API sense), but does report to
33/// defmt.
34pub fn setup() {
35 struct DefmtLogger;
36
37 impl log::Log for DefmtLogger {
38 fn enabled(&self, _: &log::Metadata) -> bool {
39 true
40 }
41
42 fn log(&self, record: &log::Record) {
43 use core::fmt::Write;
44 let mut rendered = heapless::Vec::<u8, 200>::new();
45 match write!(rendered, "{}", record.args()) {
46 Ok(()) => defmt::info!("{}", core::str::from_utf8(&rendered).unwrap()),
47 Err(_) => defmt::info!(
48 "{:?}...",
49 core::str::from_utf8(&rendered).map_err(|_| "Truncated at UTF-8 boundary")
50 ),
51 }
52 }
53
54 fn flush(&self) {}
55 }
56
57 if log::set_logger(&DefmtLogger).is_err() {
58 defmt::error!("Could not log::set_logger, continuing without output from there");
59 } else {
60 defmt::info!("Should be up now");
61 }
62 log::set_max_level(log::LevelFilter::Debug);
63}