Skip to content

Commit ab0fb98

Browse files
authored
Merge pull request #56 from probe-rs/feature/log
Add log backend in order to deprecate the rtt-log crate
2 parents ab94030 + 2f71e06 commit ab0fb98

File tree

6 files changed

+157
-20
lines changed

6 files changed

+157
-20
lines changed

README.md

+9-4
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,15 @@ fn main() {
2727
}
2828
```
2929

30-
`rtt-target` also supports initializing multiple RTT channels, and even has a logger implementation
31-
for [`defmt`](https://defmt.ferrous-systems.com/) that can be used in conjunction with arbitrary
32-
channel setups. The `defmt` integration requires setting `features = ["defmt"]`, and the used
33-
channel needs to be manually configured with `set_defmt_channel`.
30+
`rtt-target` also supports initializing multiple RTT channels, and even has a logger implementations
31+
for [`log`](https://docs.rs/log/latest/log/) and [`defmt`](https://defmt.ferrous-systems.com/) that can be used in conjunction with arbitrary
32+
channel setups.
33+
34+
The `defmt` integration requires setting `features = ["defmt"]`. Furthermore, you have to either invoke `rtt_init_defmt!` or set up your channel(s) manually and invoke `set_defmt_channel` before using `defmt`.
35+
36+
The `log` integration requires setting `features = ["log"]`. Furthermore, you have to either invoke `rtt_init_log!` or set up your channel(s) manually and invoke `init_logger`/`init_logger_with_level` before using `log`.
37+
38+
**Note**: For your platform, particularly if you're using a multi-core MCU, external logger implementations might be better suited than the one provided by this crate via the `log`/`defmt` feature.
3439

3540
For more information, please check out the [documentation](https://docs.rs/rtt-target).
3641

rtt-target/Cargo.toml

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "rtt-target"
33
description = "Target side implementation of the RTT (Real-Time Transfer) I/O protocol"
4-
version = "0.6.0"
4+
version = "0.6.1"
55
edition = "2018"
66
readme = "../README.md"
77
keywords = ["no-std", "embedded", "debugging", "rtt"]
@@ -11,13 +11,17 @@ repository = "https://github.com/probe-rs/rtt-target"
1111

1212
[features]
1313
default = []
14+
log = ["dep:log", "dep:once_cell"]
15+
log_racy_init = [] # use log::set_logger_racy instead of log::set_logger
1416

1517
[dependencies]
1618
ufmt-write = "0.1.0"
1719
critical-section = "1.0.0"
1820
portable-atomic = { version = "1.6.0", default-features = false }
1921

2022
defmt = { version = "0.3.0", optional = true }
23+
log = {version = "0.4.22", optional = true}
24+
once_cell = { version = "1.20.2" , features = ["critical-section"], default-features = false, optional = true}
2125

2226
[package.metadata.docs.rs]
2327
all-features = true

rtt-target/src/defmt.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ fn do_write(bytes: &[u8]) {
8080
/// [`rtt_init`]: crate::rtt_init
8181
#[macro_export]
8282
macro_rules! rtt_init_defmt {
83-
($mode:path, $size:expr) => {
83+
($mode:path, $size:expr) => {{
8484
let channels = $crate::rtt_init! {
8585
up: {
8686
0: {
@@ -92,14 +92,14 @@ macro_rules! rtt_init_defmt {
9292
};
9393

9494
$crate::set_defmt_channel(channels.up.0);
95-
};
95+
}};
9696

9797
($mode:path) => {
9898
$crate::rtt_init_defmt!($mode, 1024);
9999
};
100100

101-
() => {
101+
() => {{
102102
use $crate::ChannelMode::NoBlockSkip;
103103
$crate::rtt_init_defmt!(NoBlockSkip, 1024);
104-
};
104+
}};
105105
}

rtt-target/src/lib.rs

+25-7
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,33 @@
6464
//!
6565
//! The `defmt` crate can be used to format messages in a way that is more efficient and more
6666
//! informative than the standard `format!` macros. To use `defmt` with RTT, the `defmt` feature
67-
//! must be enabled, and the channel must be set up with [`set_defmt_channel`].
67+
//! must be enabled, and the channel must be set up with [`set_defmt_channel`] or you can use the
68+
//! [`rtt_init_defmt`] macro to initialize rtt and defmt at once.
6869
//!
6970
//! ```toml
7071
//! [dependencies]
7172
//! defmt = { version = "0.3" }
7273
//! rtt-target = { version = "0.6", features = ["defmt"] }
7374
//! ```
7475
//!
75-
//! # Printing
76+
//! # Log integration
77+
//!
78+
//! Rtt-target also supports integration with the `log` crate. The `log` feature must be enabled to
79+
//! configure a logger that forwards log messages to RTT.
80+
//! The logger can be initialized with `rtt_init_log!`.
81+
//!
82+
//! ```
83+
//! use rtt_target::rtt_init_log;
84+
//!
85+
//! fn main() -> ! {
86+
//! rtt_init_log!(); // Pass a log::LevelFilter as an argument to set the min log level different from Trace
87+
//! loop {
88+
//! log::info!("Hello, world!");
89+
//! }
90+
//! }
91+
//! ```
92+
//!
93+
//! # Plain Printing
7694
//!
7795
//! For no-hassle output the [`rprint`] and [`rprintln`] macros are provided. They use a single down
7896
//! channel defined at initialization time, and a critical section for synchronization, and they
@@ -116,11 +134,6 @@
116134
//! Please note that because a critical section is used, printing into a blocking channel will cause
117135
//! the application to block and freeze when the buffer is full.
118136
//!
119-
//! `rtt-target` also supports initializing multiple RTT channels, and even has a logger
120-
//! implementation for [`defmt`](::defmt) that can be used in conjunction with arbitrary channel setups.
121-
//! The `defmt` integration requires setting `features = ["defmt"]`, and the used channel needs
122-
//! to be manually configured with [`set_defmt_channel`].
123-
//!
124137
//! # Reading
125138
//!
126139
//! The following example shows how to set up the RTT to read simple input sent from the host
@@ -155,6 +168,8 @@ use ufmt_write::uWrite;
155168
pub mod debug;
156169
#[cfg(feature = "defmt")]
157170
mod defmt;
171+
#[cfg(feature = "log")]
172+
mod log;
158173
/// Public due to access from macro
159174
#[doc(hidden)]
160175
pub mod rtt;
@@ -167,6 +182,9 @@ pub use print::*;
167182
#[cfg(feature = "defmt")]
168183
pub use defmt::set_defmt_channel;
169184

185+
#[cfg(feature = "log")]
186+
pub use log::*;
187+
170188
/// RTT up (target to host) channel
171189
///
172190
/// Supports writing binary data directly, or writing strings via [`core::fmt`] macros such as

rtt-target/src/log.rs

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use crate::rprintln;
2+
use once_cell::sync::OnceCell;
3+
4+
struct Logger {
5+
level_filter: log::LevelFilter,
6+
}
7+
8+
impl log::Log for Logger {
9+
/// Returns if logger is enabled.
10+
fn enabled(&self, metadata: &log::Metadata) -> bool {
11+
metadata.level() <= self.level_filter
12+
}
13+
14+
/// Log the record.
15+
fn log(&self, record: &log::Record) {
16+
if self.enabled(record.metadata()) {
17+
rprintln!(
18+
"{:<5} [{}] {}",
19+
record.level(),
20+
record.target(),
21+
record.args()
22+
);
23+
}
24+
}
25+
26+
/// Flush buffered records.
27+
fn flush(&self) {
28+
// Nothing to do here
29+
}
30+
}
31+
32+
static LOGGER: OnceCell<Logger> = OnceCell::new();
33+
34+
/// Init the logger with maximum level (Trace).
35+
///
36+
/// Note: Normally there is no need to call this manually, use `rtt_init_log!` instead.
37+
pub fn init_logger() {
38+
init_logger_with_level(log::LevelFilter::Trace);
39+
}
40+
41+
/// Init the logger with a specific level.
42+
///
43+
/// Note: Normally there is no need to call this manually, use `rtt_init_log!` instead.
44+
pub fn init_logger_with_level(level_filter: log::LevelFilter) {
45+
// Logger was already initialized.
46+
if LOGGER.get().is_some() {
47+
return;
48+
}
49+
let logger = LOGGER.get_or_init(|| Logger { level_filter });
50+
51+
// Use racy init if the feature is enabled or the target doesn't support atomic pointers.
52+
#[cfg(any(not(target_has_atomic = "ptr"), feature = "log_racy_init"))]
53+
unsafe {
54+
init_racy(logger);
55+
}
56+
57+
// Use the default init otherwise.
58+
#[cfg(all(target_has_atomic = "ptr", not(feature = "log_racy_init")))]
59+
init_default(logger);
60+
}
61+
62+
#[cfg(all(target_has_atomic = "ptr", not(feature = "log_racy_init")))]
63+
fn init_default(logger: &'static Logger) {
64+
log::set_logger(logger).ok();
65+
log::set_max_level(logger.level_filter);
66+
}
67+
68+
// # Safety
69+
//
70+
// This function will call the unsafe functions [log::set_logger_racy] and
71+
// [log::set_max_level_racy] if either the feature `log_racy_init` is enabled or the target doesn't
72+
// support atomic pointers. The [once_cell::OnceCell] should ensure that this is only called
73+
// once.
74+
#[cfg(any(not(target_has_atomic = "ptr"), feature = "log_racy_init"))]
75+
unsafe fn init_racy(logger: &'static Logger) {
76+
log::set_logger_racy(logger).ok();
77+
log::set_max_level_racy(logger.level_filter);
78+
}
79+
80+
/// Initializes RTT with a single up channel, sets it as the print channel for the printing macros
81+
/// and sets up a log backend with the given log level.
82+
///
83+
/// The optional arguments specify the level filter (default: `log::LevelFilter::Trace`),
84+
/// the blocking mode (default: `NoBlockSkip`) and size of the buffer in bytes (default: 1024).
85+
///
86+
/// See [`rtt_init`] for more details.
87+
///
88+
/// [`rtt_init`]: crate::rtt_init
89+
#[macro_export]
90+
macro_rules! rtt_init_log {
91+
($level:path, $mode:path, $size:expr) => {{
92+
$crate::rtt_init_print!($mode, $size);
93+
$crate::init_logger_with_level($level);
94+
}};
95+
96+
($level:path, $mode:path) => {
97+
$crate::rtt_init_log!($level, $mode, 1024);
98+
};
99+
100+
($level:path) => {{
101+
use $crate::ChannelMode::NoBlockSkip;
102+
$crate::rtt_init_log!($level, NoBlockSkip, 1024);
103+
}};
104+
105+
() => {{
106+
use log::LevelFilter::Trace;
107+
use $crate::ChannelMode::NoBlockSkip;
108+
$crate::rtt_init_log!(Trace, NoBlockSkip, 1024);
109+
}};
110+
}

rtt-target/src/print.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ macro_rules! rdbg {
160160
/// [`rtt_init`]: crate::rtt_init
161161
#[macro_export]
162162
macro_rules! rtt_init_print {
163-
($mode:path, $size:expr) => {
163+
($mode:path, $size:expr) => {{
164164
let channels = $crate::rtt_init! {
165165
up: {
166166
0: {
@@ -172,14 +172,14 @@ macro_rules! rtt_init_print {
172172
};
173173

174174
$crate::set_print_channel(channels.up.0);
175-
};
175+
}};
176176

177177
($mode:path) => {
178178
$crate::rtt_init_print!($mode, 1024);
179179
};
180180

181-
() => {
181+
() => {{
182182
use $crate::ChannelMode::NoBlockSkip;
183183
$crate::rtt_init_print!(NoBlockSkip, 1024);
184-
};
184+
}};
185185
}

0 commit comments

Comments
 (0)