Skip to content

Commit

Permalink
Merge pull request #56 from probe-rs/feature/log
Browse files Browse the repository at this point in the history
Add log backend in order to deprecate the rtt-log crate
  • Loading branch information
t-moe authored Dec 2, 2024
2 parents ab94030 + 2f71e06 commit ab0fb98
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 20 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,15 @@ fn main() {
}
```

`rtt-target` also supports initializing multiple RTT channels, and even has a logger implementation
for [`defmt`](https://defmt.ferrous-systems.com/) that can be used in conjunction with arbitrary
channel setups. The `defmt` integration requires setting `features = ["defmt"]`, and the used
channel needs to be manually configured with `set_defmt_channel`.
`rtt-target` also supports initializing multiple RTT channels, and even has a logger implementations
for [`log`](https://docs.rs/log/latest/log/) and [`defmt`](https://defmt.ferrous-systems.com/) that can be used in conjunction with arbitrary
channel setups.

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`.

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`.

**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.

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

Expand Down
6 changes: 5 additions & 1 deletion rtt-target/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "rtt-target"
description = "Target side implementation of the RTT (Real-Time Transfer) I/O protocol"
version = "0.6.0"
version = "0.6.1"
edition = "2018"
readme = "../README.md"
keywords = ["no-std", "embedded", "debugging", "rtt"]
Expand All @@ -11,13 +11,17 @@ repository = "https://github.com/probe-rs/rtt-target"

[features]
default = []
log = ["dep:log", "dep:once_cell"]
log_racy_init = [] # use log::set_logger_racy instead of log::set_logger

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

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

[package.metadata.docs.rs]
all-features = true
Expand Down
8 changes: 4 additions & 4 deletions rtt-target/src/defmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ fn do_write(bytes: &[u8]) {
/// [`rtt_init`]: crate::rtt_init
#[macro_export]
macro_rules! rtt_init_defmt {
($mode:path, $size:expr) => {
($mode:path, $size:expr) => {{
let channels = $crate::rtt_init! {
up: {
0: {
Expand All @@ -92,14 +92,14 @@ macro_rules! rtt_init_defmt {
};

$crate::set_defmt_channel(channels.up.0);
};
}};

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

() => {
() => {{
use $crate::ChannelMode::NoBlockSkip;
$crate::rtt_init_defmt!(NoBlockSkip, 1024);
};
}};
}
32 changes: 25 additions & 7 deletions rtt-target/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,33 @@
//!
//! The `defmt` crate can be used to format messages in a way that is more efficient and more
//! informative than the standard `format!` macros. To use `defmt` with RTT, the `defmt` feature
//! must be enabled, and the channel must be set up with [`set_defmt_channel`].
//! must be enabled, and the channel must be set up with [`set_defmt_channel`] or you can use the
//! [`rtt_init_defmt`] macro to initialize rtt and defmt at once.
//!
//! ```toml
//! [dependencies]
//! defmt = { version = "0.3" }
//! rtt-target = { version = "0.6", features = ["defmt"] }
//! ```
//!
//! # Printing
//! # Log integration
//!
//! Rtt-target also supports integration with the `log` crate. The `log` feature must be enabled to
//! configure a logger that forwards log messages to RTT.
//! The logger can be initialized with `rtt_init_log!`.
//!
//! ```
//! use rtt_target::rtt_init_log;
//!
//! fn main() -> ! {
//! rtt_init_log!(); // Pass a log::LevelFilter as an argument to set the min log level different from Trace
//! loop {
//! log::info!("Hello, world!");
//! }
//! }
//! ```
//!
//! # Plain Printing
//!
//! For no-hassle output the [`rprint`] and [`rprintln`] macros are provided. They use a single down
//! channel defined at initialization time, and a critical section for synchronization, and they
Expand Down Expand Up @@ -116,11 +134,6 @@
//! Please note that because a critical section is used, printing into a blocking channel will cause
//! the application to block and freeze when the buffer is full.
//!
//! `rtt-target` also supports initializing multiple RTT channels, and even has a logger
//! implementation for [`defmt`](::defmt) that can be used in conjunction with arbitrary channel setups.
//! The `defmt` integration requires setting `features = ["defmt"]`, and the used channel needs
//! to be manually configured with [`set_defmt_channel`].
//!
//! # Reading
//!
//! The following example shows how to set up the RTT to read simple input sent from the host
Expand Down Expand Up @@ -155,6 +168,8 @@ use ufmt_write::uWrite;
pub mod debug;
#[cfg(feature = "defmt")]
mod defmt;
#[cfg(feature = "log")]
mod log;
/// Public due to access from macro
#[doc(hidden)]
pub mod rtt;
Expand All @@ -167,6 +182,9 @@ pub use print::*;
#[cfg(feature = "defmt")]
pub use defmt::set_defmt_channel;

#[cfg(feature = "log")]
pub use log::*;

/// RTT up (target to host) channel
///
/// Supports writing binary data directly, or writing strings via [`core::fmt`] macros such as
Expand Down
110 changes: 110 additions & 0 deletions rtt-target/src/log.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use crate::rprintln;
use once_cell::sync::OnceCell;

struct Logger {
level_filter: log::LevelFilter,
}

impl log::Log for Logger {
/// Returns if logger is enabled.
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.level() <= self.level_filter
}

/// Log the record.
fn log(&self, record: &log::Record) {
if self.enabled(record.metadata()) {
rprintln!(
"{:<5} [{}] {}",
record.level(),
record.target(),
record.args()
);
}
}

/// Flush buffered records.
fn flush(&self) {
// Nothing to do here
}
}

static LOGGER: OnceCell<Logger> = OnceCell::new();

/// Init the logger with maximum level (Trace).
///
/// Note: Normally there is no need to call this manually, use `rtt_init_log!` instead.
pub fn init_logger() {
init_logger_with_level(log::LevelFilter::Trace);
}

/// Init the logger with a specific level.
///
/// Note: Normally there is no need to call this manually, use `rtt_init_log!` instead.
pub fn init_logger_with_level(level_filter: log::LevelFilter) {
// Logger was already initialized.
if LOGGER.get().is_some() {
return;
}
let logger = LOGGER.get_or_init(|| Logger { level_filter });

// Use racy init if the feature is enabled or the target doesn't support atomic pointers.
#[cfg(any(not(target_has_atomic = "ptr"), feature = "log_racy_init"))]
unsafe {
init_racy(logger);
}

// Use the default init otherwise.
#[cfg(all(target_has_atomic = "ptr", not(feature = "log_racy_init")))]
init_default(logger);
}

#[cfg(all(target_has_atomic = "ptr", not(feature = "log_racy_init")))]
fn init_default(logger: &'static Logger) {
log::set_logger(logger).ok();
log::set_max_level(logger.level_filter);
}

// # Safety
//
// This function will call the unsafe functions [log::set_logger_racy] and
// [log::set_max_level_racy] if either the feature `log_racy_init` is enabled or the target doesn't
// support atomic pointers. The [once_cell::OnceCell] should ensure that this is only called
// once.
#[cfg(any(not(target_has_atomic = "ptr"), feature = "log_racy_init"))]
unsafe fn init_racy(logger: &'static Logger) {
log::set_logger_racy(logger).ok();
log::set_max_level_racy(logger.level_filter);
}

/// Initializes RTT with a single up channel, sets it as the print channel for the printing macros
/// and sets up a log backend with the given log level.
///
/// The optional arguments specify the level filter (default: `log::LevelFilter::Trace`),
/// the blocking mode (default: `NoBlockSkip`) and size of the buffer in bytes (default: 1024).
///
/// See [`rtt_init`] for more details.
///
/// [`rtt_init`]: crate::rtt_init
#[macro_export]
macro_rules! rtt_init_log {
($level:path, $mode:path, $size:expr) => {{
$crate::rtt_init_print!($mode, $size);
$crate::init_logger_with_level($level);
}};

($level:path, $mode:path) => {
$crate::rtt_init_log!($level, $mode, 1024);
};

($level:path) => {{
use $crate::ChannelMode::NoBlockSkip;
$crate::rtt_init_log!($level, NoBlockSkip, 1024);
}};

() => {{
use log::LevelFilter::Trace;
use $crate::ChannelMode::NoBlockSkip;
$crate::rtt_init_log!(Trace, NoBlockSkip, 1024);
}};
}
8 changes: 4 additions & 4 deletions rtt-target/src/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ macro_rules! rdbg {
/// [`rtt_init`]: crate::rtt_init
#[macro_export]
macro_rules! rtt_init_print {
($mode:path, $size:expr) => {
($mode:path, $size:expr) => {{
let channels = $crate::rtt_init! {
up: {
0: {
Expand All @@ -172,14 +172,14 @@ macro_rules! rtt_init_print {
};

$crate::set_print_channel(channels.up.0);
};
}};

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

() => {
() => {{
use $crate::ChannelMode::NoBlockSkip;
$crate::rtt_init_print!(NoBlockSkip, 1024);
};
}};
}

0 comments on commit ab0fb98

Please sign in to comment.