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}