Skip to main content

imxrt_log/
log.rs

1//! Text-based logging frontend.
2//!
3//! The module provides a [`log`](https://crates.io/crates/log) implementation that
4//! transfers data using any supported backend.
5//!
6//! Strings are formatted and serialized to a buffer. Compile and runtime filters prevent formatting
7//! and serialization into the buffer. When it's time to copy the data into the circular buffer, the
8//! implementation takes a short critical section.
9//!
10//! See [`LoggingConfig`] to learn more about the runtime filters.
11//! See the `log` package documentation to learn about static filters.
12
13mod filters;
14mod frontend;
15
16pub use filters::Filter;
17use filters::Filters;
18
19use crate::{BUFFER, Poller};
20
21#[cfg(feature = "lpuart")]
22use imxrt_hal::{dma::channel::Channel, lpuart::Lpuart};
23
24/// Logging configuration
25///
26/// Use this to specify certain configurations of the logging
27/// system. By default, the max log level is the log level set at
28/// compile time. See the [compile time filters](https://docs.rs/log/0.4/log/#compile-time-filters)
29/// section for more information. The frontend also enables logging for all targets.
30/// Set the `filters` collection to specify log targets of interest.
31///
32/// If the default configuration is good for you, use `Default::default()`.
33///
34/// ```
35/// use imxrt_log::log::{Filter, LoggingConfig};
36///
37/// const I2C_LOGGING: Filter = ("i2c", None);
38/// const SPI_LOGGING: Filter = ("spi", Some(log::LevelFilter::Warn));
39/// const MOTOR_LOGGING: Filter = ("motor", Some(log::LevelFilter::Trace));
40///
41/// let config = LoggingConfig {
42///     max_level: log::LevelFilter::Debug,
43///     filters: &[
44///         I2C_LOGGING,
45///         SPI_LOGGING,
46///         MOTOR_LOGGING,
47///     ]
48/// };
49/// ```
50pub struct LoggingConfig {
51    /// The max log level for *all* logging
52    ///
53    /// This is the static max level. You may
54    /// override this to bypass the statically-assigned
55    /// max level
56    pub max_level: ::log::LevelFilter,
57    /// A list of filtered targets to log.
58    ///
59    /// If set to an empty slice (default), the logger performs no
60    /// filtering. Otherwise, the frontend filters the specified targets by
61    /// the accompanying log level. See [`Filter`](type.Filter.html) for
62    /// more information.
63    pub filters: &'static [Filter],
64}
65
66impl LoggingConfig {
67    /// Create a default logging config.
68    ///
69    /// Unlike `default()`, this works in `const` contexts.
70    pub const fn new() -> Self {
71        LoggingConfig {
72            max_level: ::log::STATIC_MAX_LEVEL,
73            filters: &[],
74        }
75    }
76}
77
78impl Default for LoggingConfig {
79    fn default() -> LoggingConfig {
80        Self::new()
81    }
82}
83
84/// Initialize a USB logger with the `log` frontend and custom configurations.
85///
86/// See the crate-level documentation to understand how the USB device backend works.
87#[cfg(feature = "usbd")]
88pub fn usbd_with_config<const N: u8>(
89    peripherals: imxrt_usbd::Instances<N>,
90    interrupts: super::Interrupts,
91    frontend_config: &LoggingConfig,
92    backend_config: &crate::UsbdConfig,
93) -> Result<Poller, crate::AlreadySetError<imxrt_usbd::Instances<N>>> {
94    let (producer, consumer) = match BUFFER.try_split() {
95        Ok((prod, cons)) => (prod, cons),
96        Err(_) => return Err(crate::AlreadySetError::new(peripherals)),
97    };
98
99    critical_section::with(|_| {
100        if frontend::init(producer, frontend_config).is_err() {
101            return Err(crate::AlreadySetError::new(peripherals));
102        }
103        let backend = crate::usbd::init(peripherals, interrupts, consumer, backend_config);
104        Ok(Poller::new(backend))
105    })
106}
107
108/// Initialize a USB logger with the `log` frontend.
109///
110/// This function uses default configurations for the frontend and backend.
111/// See the crate-level documentation to understand how the USB device backend works.
112#[cfg(feature = "usbd")]
113pub fn usbd<const N: u8>(
114    peripherals: imxrt_usbd::Instances<N>,
115    interrupts: super::Interrupts,
116) -> Result<Poller, crate::AlreadySetError<imxrt_usbd::Instances<N>>> {
117    usbd_with_config(
118        peripherals,
119        interrupts,
120        &LoggingConfig::default(),
121        &crate::UsbdConfigBuilder::new().build(),
122    )
123}
124
125/// Initialize a LPUART & DMA logger with the `log` frontend and custom configurations.
126///
127/// See the crate-level documentation to understand how the LPUART backend works.
128#[cfg(feature = "lpuart")]
129pub fn lpuart_with_config(
130    lpuart: Lpuart,
131    dma_channel: Channel,
132    interrupts: crate::Interrupts,
133    frontend_config: &LoggingConfig,
134) -> Result<Poller, crate::AlreadySetError<(Lpuart, Channel)>> {
135    let (producer, consumer) = match BUFFER.try_split() {
136        Ok((prod, cons)) => (prod, cons),
137        Err(_) => return Err(crate::AlreadySetError::new((lpuart, dma_channel))),
138    };
139
140    critical_section::with(|_| {
141        if frontend::init(producer, frontend_config).is_err() {
142            return Err(crate::AlreadySetError::new((lpuart, dma_channel)));
143        }
144        let backend = crate::lpuart::init(lpuart, dma_channel, consumer, interrupts);
145        Ok(Poller::new(backend))
146    })
147}
148
149/// Initialize a LPUART & DMA logger with the `log` frontend.
150///
151/// This function uses default configurations for the frontend.
152/// See the crate-level documentation to understand how the LPUART backend works.
153#[cfg(feature = "lpuart")]
154pub fn lpuart(
155    lpuart: Lpuart,
156    dma_channel: Channel,
157    interrupts: crate::Interrupts,
158) -> Result<Poller, crate::AlreadySetError<(Lpuart, Channel)>> {
159    lpuart_with_config(lpuart, dma_channel, interrupts, &LoggingConfig::default())
160}