1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Logging over an i.MX RT serial interface
//!
//! The crate provides a [`log`](https://crates.io/crates/log) implementation that
//! transfers data over UART. It's an extension to the [`imxrt-hal`](https://crates.io/crates/imxrt-hal)
//! crate. The crate provides two logging implementations:
//!
//! - a simple, [blocking](blocking/index.html) interface. Useful for simple logging, and for safely logging in interrupt, fault, and
//!   panic handlers.
//! - a [DMA-based](dma/index.html), non-blocking interface. Useful for logging that needs to happen quickly, but does
//!   not need to be responsive. More complicated to set up than the blocking interface.
//!
//! The module-level documentation provides examples and recommended use-cases. To see some comparisons between the two,
//! see [Performance](#performance).
//!
//! # i.MX RT Compatibility
//!
//! This crate supports all of the same i.MX RT variants as the `imxrt-hal` crate.
//! To see the supported i.MX RT variants, check the [HAL's feature support](https://github.com/imxrt-rs/imxrt-rs#hal) list.
//!
//! **Note**: As of this writing, the HAL only supports one i.MX RT variant, the `"imxrt1062"`.
//! For convenience, the `"imxrt1062"` feature is enabled **by default**. This may change in the
//! future.
//!
//! # Performance
//!
//! We measured logging execution on a Teensy 4. We configured a UART peripheral following the examples in each module.
//! We measured the CPU clock cycles required to execute each logging statement.
//!
//! By default, a logging call resembling
//!
//! ```text
//! log::info!("Hello world! 3 + 2 = {}", 3 + 2);
//! ```
//!
//! Produces a message resembling
//!
//! ```text
//! [INFO log_uart]: Hello world! 3 + 2 = 5
//! ```
//!
//! where `INFO` describes the log level, `log_uart` describes the module, and the remainder
//! of the message is the serialized content.
//!
//! CPU clock cycles measure the elapse of cycle counts before and after a `log::info!()` macro.
//!
//! | Logging Invocation                                    | CPU Clock Cycles, Blocking | CPU Clock Cycles, DMA |
//! | ----------------------------------------------------- | -------------------------- | --------------------- |
//! | `log::info!("Hello world! 3 + 2 = {}", 3 + 2);`       | 2341330                    | 1871                  |
//! | `log::info!("Hello world! 3 + 2 = 5");`               | 2341273                    | 1683                  |
//! | `log::info!("");`                                     | 1197246                    | 1428                  |
//! | `log::info!(/* 100 character string */);`             | 6397302                    | 2463                  |
//!
//! Use this crate's examples to reproduce these measurements.

#![no_std]

pub mod blocking;
pub mod dma;
mod filters;

pub use filters::Filter;
use filters::Filters;

/// Logging configuration
///
/// Allows a user to specify certain configurations of the logging
/// system. By default, the max log level is the log level set at
/// compile time. See the [compile time filters](https://docs.rs/log/0.4.8/log/#compile-time-filters)
/// section for more information. We also enable logging for all targets.
/// Set the `filters` collection to specify log targets of interest.
///
/// If the default configuration is good for you, use `Default::default()`.
///
/// ```
/// use imxrt_uart_log::{Filter, LoggingConfig};
///
/// const I2C_LOGGING: Filter = ("i2c", None);
/// const SPI_LOGGING: Filter = ("spi", Some(log::LevelFilter::Warn));
/// const MOTOR_LOGGING: Filter = ("motor", Some(log::LevelFilter::Trace));
///
/// let config = LoggingConfig {
///     max_level: log::LevelFilter::Debug,
///     filters: &[
///         I2C_LOGGING,
///         SPI_LOGGING,
///         MOTOR_LOGGING,
///     ]
/// };
/// ```
pub struct LoggingConfig {
    /// The max log level for *all* logging
    ///
    /// By default, we select the static max level. Users may
    /// override this if they'd like to bypass the statically-assigned
    /// max level
    pub max_level: ::log::LevelFilter,
    /// A list of filtered targets to log.
    ///
    /// If set to an empty slice (default), the logger performs no
    /// filtering. Otherwise, we filter the specified targets by
    /// the accompanying log level. See [`Filter`](type.Filter.html) for
    /// more information.
    pub filters: &'static [Filter],
}

impl Default for LoggingConfig {
    fn default() -> LoggingConfig {
        LoggingConfig {
            max_level: ::log::STATIC_MAX_LEVEL,
            filters: &[],
        }
    }
}

/// An error that indicates the logger is already set
///
/// The error could propagate from one of the `init()` functions.
/// Or, it could propagate if the underlying logger was set through another logging
/// interface.
#[derive(Debug)]
pub struct SetLoggerError(());

impl From<::log::SetLoggerError> for SetLoggerError {
    fn from(_: ::log::SetLoggerError) -> SetLoggerError {
        SetLoggerError(())
    }
}