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::{Poller, BUFFER};
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<P: imxrt_usbd::Peripherals>(
89    peripherals: P,
90    interrupts: super::Interrupts,
91    frontend_config: &LoggingConfig,
92    backend_config: &crate::UsbdConfig,
93) -> Result<Poller, crate::AlreadySetError<P>> {
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    // Safety: both can only be called once. We use try_split
100    // (above) to meet that requirement. If that method is called
101    // more than once, subsequent calls are an error.
102    critical_section::with(|_| unsafe {
103        if frontend::init(producer, frontend_config).is_err() {
104            return Err(crate::AlreadySetError::new(peripherals));
105        }
106        crate::usbd::init(peripherals, interrupts, consumer, backend_config);
107        Ok(Poller::new(crate::usbd::VTABLE))
108    })
109}
110
111/// Initialize a USB logger with the `log` frontend.
112///
113/// This function uses default configurations for the frontend and backend.
114/// See the crate-level documentation to understand how the USB device backend works.
115#[cfg(feature = "usbd")]
116pub fn usbd<P: imxrt_usbd::Peripherals>(
117    peripherals: P,
118    interrupts: super::Interrupts,
119) -> Result<Poller, crate::AlreadySetError<P>> {
120    usbd_with_config(
121        peripherals,
122        interrupts,
123        &LoggingConfig::default(),
124        &crate::UsbdConfigBuilder::new().build(),
125    )
126}
127
128/// Initialize a LPUART & DMA logger with the `log` frontend and custom configurations.
129///
130/// See the crate-level documentation to understand how the LPUART backend works.
131#[cfg(feature = "lpuart")]
132pub fn lpuart_with_config<P, const LPUART: u8>(
133    lpuart: Lpuart<P, LPUART>,
134    dma_channel: Channel,
135    interrupts: crate::Interrupts,
136    frontend_config: &LoggingConfig,
137) -> Result<Poller, crate::AlreadySetError<(Lpuart<P, LPUART>, Channel)>> {
138    let (producer, consumer) = match BUFFER.try_split() {
139        Ok((prod, cons)) => (prod, cons),
140        Err(_) => return Err(crate::AlreadySetError::new((lpuart, dma_channel))),
141    };
142
143    // Safety: all of this can only happen once. We use try_split
144    // to meet that requirement.
145    critical_section::with(|_| unsafe {
146        if frontend::init(producer, frontend_config).is_err() {
147            return Err(crate::AlreadySetError::new((lpuart, dma_channel)));
148        }
149        crate::lpuart::init(lpuart, dma_channel, consumer, interrupts);
150        Ok(Poller::new(crate::lpuart::VTABLE))
151    })
152}
153
154/// Initialize a LPUART & DMA logger with the `log` frontend.
155///
156/// This function uses default configurations for the frontend.
157/// See the crate-level documentation to understand how the LPUART backend works.
158#[cfg(feature = "lpuart")]
159pub fn lpuart<P, const LPUART: u8>(
160    lpuart: Lpuart<P, LPUART>,
161    dma_channel: Channel,
162    interrupts: crate::Interrupts,
163) -> Result<Poller, crate::AlreadySetError<(Lpuart<P, LPUART>, Channel)>> {
164    lpuart_with_config(lpuart, dma_channel, interrupts, &LoggingConfig::default())
165}