// Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! A lightweight logging facade. //! //! The `log` crate provides a single logging API that abstracts over the //! actual logging implementation. Libraries can use the logging API provided //! by this crate, and the consumer of those libraries can choose the logging //! implementation that is most suitable for its use case. //! //! If no logging implementation is selected, the facade falls back to a "noop" //! implementation that ignores all log messages. The overhead in this case //! is very small - just an integer load, comparison and jump. //! //! A log request consists of a _target_, a _level_, and a _body_. A target is a //! string which defaults to the module path of the location of the log request, //! though that default may be overridden. Logger implementations typically use //! the target to filter requests based on some user configuration. //! //! # Usage //! //! The basic use of the log crate is through the five logging macros: [`error!`], //! [`warn!`], [`info!`], [`debug!`] and [`trace!`] //! where `error!` represents the highest-priority log messages //! and `trace!` the lowest. The log messages are filtered by configuring //! the log level to exclude messages with a lower priority. //! Each of these macros accept format strings similarly to [`println!`]. //! //! //! [`error!`]: ./macro.error.html //! [`warn!`]: ./macro.warn.html //! [`info!`]: ./macro.info.html //! [`debug!`]: ./macro.debug.html //! [`trace!`]: ./macro.trace.html //! [`println!`]: https://doc.rust-lang.org/stable/std/macro.println.html //! //! ## In libraries //! //! Libraries should link only to the `log` crate, and use the provided //! macros to log whatever information will be useful to downstream consumers. //! //! ### Examples //! //! ```edition2018 //! # #[derive(Debug)] pub struct Yak(String); //! # impl Yak { fn shave(&mut self, _: u32) {} } //! # fn find_a_razor() -> Result { Ok(1) } //! use log::{info, warn}; //! //! pub fn shave_the_yak(yak: &mut Yak) { //! info!(target: "yak_events", "Commencing yak shaving for {:?}", yak); //! //! loop { //! match find_a_razor() { //! Ok(razor) => { //! info!("Razor located: {}", razor); //! yak.shave(razor); //! break; //! } //! Err(err) => { //! warn!("Unable to locate a razor: {}, retrying", err); //! } //! } //! } //! } //! # fn main() {} //! ``` //! //! ## In executables //! //! Executables should choose a logging implementation and initialize it early in the //! runtime of the program. Logging implementations will typically include a //! function to do this. Any log messages generated before //! the implementation is initialized will be ignored. //! //! The executable itself may use the `log` crate to log as well. //! //! ### Warning //! //! The logging system may only be initialized once. //! //! ## Structured logging //! //! If you enable the `kv_unstable` feature you can associate structured values //! with your log records. If we take the example from before, we can include //! some additional context besides what's in the formatted message: //! //! ```edition2018 //! # #[macro_use] extern crate serde; //! # #[derive(Debug, Serialize)] pub struct Yak(String); //! # impl Yak { fn shave(&mut self, _: u32) {} } //! # fn find_a_razor() -> Result { Ok(1) } //! # #[cfg(feature = "kv_unstable_serde")] //! # fn main() { //! use log::{info, warn, as_serde, as_error}; //! //! pub fn shave_the_yak(yak: &mut Yak) { //! info!(target: "yak_events", yak = as_serde!(yak); "Commencing yak shaving"); //! //! loop { //! match find_a_razor() { //! Ok(razor) => { //! info!(razor = razor; "Razor located"); //! yak.shave(razor); //! break; //! } //! Err(err) => { //! warn!(err = as_error!(err); "Unable to locate a razor, retrying"); //! } //! } //! } //! } //! # } //! # #[cfg(not(feature = "kv_unstable_serde"))] //! # fn main() {} //! ``` //! //! # Available logging implementations //! //! In order to produce log output executables have to use //! a logger implementation compatible with the facade. //! There are many available implementations to choose from, //! here are some of the most popular ones: //! //! * Simple minimal loggers: //! * [env_logger] //! * [simple_logger] //! * [simplelog] //! * [pretty_env_logger] //! * [stderrlog] //! * [flexi_logger] //! * [call_logger] //! * [structured-logger] //! * Complex configurable frameworks: //! * [log4rs] //! * [fern] //! * Adaptors for other facilities: //! * [syslog] //! * [slog-stdlog] //! * [systemd-journal-logger] //! * [android_log] //! * [win_dbg_logger] //! * [db_logger] //! * [log-to-defmt] //! * For WebAssembly binaries: //! * [console_log] //! * For dynamic libraries: //! * You may need to construct an FFI-safe wrapper over `log` to initialize in your libraries //! //! # Implementing a Logger //! //! Loggers implement the [`Log`] trait. Here's a very basic example that simply //! logs all messages at the [`Error`][level_link], [`Warn`][level_link] or //! [`Info`][level_link] levels to stdout: //! //! ```edition2018 //! use log::{Record, Level, Metadata}; //! //! struct SimpleLogger; //! //! impl log::Log for SimpleLogger { //! fn enabled(&self, metadata: &Metadata) -> bool { //! metadata.level() <= Level::Info //! } //! //! fn log(&self, record: &Record) { //! if self.enabled(record.metadata()) { //! println!("{} - {}", record.level(), record.args()); //! } //! } //! //! fn flush(&self) {} //! } //! //! # fn main() {} //! ``` //! //! Loggers are installed by calling the [`set_logger`] function. The maximum //! log level also needs to be adjusted via the [`set_max_level`] function. The //! logging facade uses this as an optimization to improve performance of log //! messages at levels that are disabled. It's important to set it, as it //! defaults to [`Off`][filter_link], so no log messages will ever be captured! //! In the case of our example logger, we'll want to set the maximum log level //! to [`Info`][filter_link], since we ignore any [`Debug`][level_link] or //! [`Trace`][level_link] level log messages. A logging implementation should //! provide a function that wraps a call to [`set_logger`] and //! [`set_max_level`], handling initialization of the logger: //! //! ```edition2018 //! # use log::{Level, Metadata}; //! # struct SimpleLogger; //! # impl log::Log for SimpleLogger { //! # fn enabled(&self, _: &Metadata) -> bool { false } //! # fn log(&self, _: &log::Record) {} //! # fn flush(&self) {} //! # } //! # fn main() {} //! use log::{SetLoggerError, LevelFilter}; //! //! static LOGGER: SimpleLogger = SimpleLogger; //! //! pub fn init() -> Result<(), SetLoggerError> { //! log::set_logger(&LOGGER) //! .map(|()| log::set_max_level(LevelFilter::Info)) //! } //! ``` //! //! Implementations that adjust their configurations at runtime should take care //! to adjust the maximum log level as well. //! //! # Use with `std` //! //! `set_logger` requires you to provide a `&'static Log`, which can be hard to //! obtain if your logger depends on some runtime configuration. The //! `set_boxed_logger` function is available with the `std` Cargo feature. It is //! identical to `set_logger` except that it takes a `Box` rather than a //! `&'static Log`: //! //! ```edition2018 //! # use log::{Level, LevelFilter, Log, SetLoggerError, Metadata}; //! # struct SimpleLogger; //! # impl log::Log for SimpleLogger { //! # fn enabled(&self, _: &Metadata) -> bool { false } //! # fn log(&self, _: &log::Record) {} //! # fn flush(&self) {} //! # } //! # fn main() {} //! # #[cfg(feature = "std")] //! pub fn init() -> Result<(), SetLoggerError> { //! log::set_boxed_logger(Box::new(SimpleLogger)) //! .map(|()| log::set_max_level(LevelFilter::Info)) //! } //! ``` //! //! # Compile time filters //! //! Log levels can be statically disabled at compile time via Cargo features. Log invocations at //! disabled levels will be skipped and will not even be present in the resulting binary. //! This level is configured separately for release and debug builds. The features are: //! //! * `max_level_off` //! * `max_level_error` //! * `max_level_warn` //! * `max_level_info` //! * `max_level_debug` //! * `max_level_trace` //! * `release_max_level_off` //! * `release_max_level_error` //! * `release_max_level_warn` //! * `release_max_level_info` //! * `release_max_level_debug` //! * `release_max_level_trace` //! //! These features control the value of the `STATIC_MAX_LEVEL` constant. The logging macros check //! this value before logging a message. By default, no levels are disabled. //! //! Libraries should avoid using the max level features because they're global and can't be changed //! once they're set. //! //! For example, a crate can disable trace level logs in debug builds and trace, debug, and info //! level logs in release builds with the following configuration: //! //! ```toml //! [dependencies] //! log = { version = "0.4", features = ["max_level_debug", "release_max_level_warn"] } //! ``` //! # Crate Feature Flags //! //! The following crate feature flags are available in addition to the filters. They are //! configured in your `Cargo.toml`. //! //! * `std` allows use of `std` crate instead of the default `core`. Enables using `std::error` and //! `set_boxed_logger` functionality. //! * `serde` enables support for serialization and deserialization of `Level` and `LevelFilter`. //! //! ```toml //! [dependencies] //! log = { version = "0.4", features = ["std", "serde"] } //! ``` //! //! # Version compatibility //! //! The 0.3 and 0.4 versions of the `log` crate are almost entirely compatible. Log messages //! made using `log` 0.3 will forward transparently to a logger implementation using `log` 0.4. Log //! messages made using `log` 0.4 will forward to a logger implementation using `log` 0.3, but the //! module path and file name information associated with the message will unfortunately be lost. //! //! [`Log`]: trait.Log.html //! [level_link]: enum.Level.html //! [filter_link]: enum.LevelFilter.html //! [`set_logger`]: fn.set_logger.html //! [`set_max_level`]: fn.set_max_level.html //! [`try_set_logger_raw`]: fn.try_set_logger_raw.html //! [`shutdown_logger_raw`]: fn.shutdown_logger_raw.html //! [env_logger]: https://docs.rs/env_logger/*/env_logger/ //! [simple_logger]: https://github.com/borntyping/rust-simple_logger //! [simplelog]: https://github.com/drakulix/simplelog.rs //! [pretty_env_logger]: https://docs.rs/pretty_env_logger/*/pretty_env_logger/ //! [stderrlog]: https://docs.rs/stderrlog/*/stderrlog/ //! [flexi_logger]: https://docs.rs/flexi_logger/*/flexi_logger/ //! [call_logger]: https://docs.rs/call_logger/*/call_logger/ //! [syslog]: https://docs.rs/syslog/*/syslog/ //! [slog-stdlog]: https://docs.rs/slog-stdlog/*/slog_stdlog/ //! [log4rs]: https://docs.rs/log4rs/*/log4rs/ //! [fern]: https://docs.rs/fern/*/fern/ //! [systemd-journal-logger]: https://docs.rs/systemd-journal-logger/*/systemd_journal_logger/ //! [android_log]: https://docs.rs/android_log/*/android_log/ //! [win_dbg_logger]: https://docs.rs/win_dbg_logger/*/win_dbg_logger/ //! [db_logger]: https://docs.rs/db_logger/*/db_logger/ //! [log-to-defmt]: https://docs.rs/log-to-defmt/*/log_to_defmt/ //! [console_log]: https://docs.rs/console_log/*/console_log/ //! [structured-logger]: https://docs.rs/structured-logger/latest/structured_logger/ #![doc( html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", html_root_url = "https://docs.rs/log/0.4.20" )] #![warn(missing_docs)] #![deny(missing_debug_implementations, unconditional_recursion)] #![cfg_attr(all(not(feature = "std"), not(test)), no_std)] // When compiled for the rustc compiler itself we want to make sure that this is // an unstable crate #![cfg_attr(rustbuild, feature(staged_api, rustc_private))] #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] #[cfg(all(not(feature = "std"), not(test)))] extern crate core as std; use std::cmp; #[cfg(feature = "std")] use std::error; use std::fmt; use std::mem; use std::str::FromStr; #[macro_use] mod macros; mod serde; #[cfg(feature = "kv_unstable")] pub mod kv; #[cfg(target_has_atomic = "ptr")] use std::sync::atomic::{AtomicUsize, Ordering}; #[cfg(not(target_has_atomic = "ptr"))] use std::cell::Cell; #[cfg(not(target_has_atomic = "ptr"))] use std::sync::atomic::Ordering; #[cfg(not(target_has_atomic = "ptr"))] struct AtomicUsize { v: Cell, } #[cfg(not(target_has_atomic = "ptr"))] impl AtomicUsize { const fn new(v: usize) -> AtomicUsize { AtomicUsize { v: Cell::new(v) } } fn load(&self, _order: Ordering) -> usize { self.v.get() } fn store(&self, val: usize, _order: Ordering) { self.v.set(val) } #[cfg(target_has_atomic = "ptr")] fn compare_exchange( &self, current: usize, new: usize, _success: Ordering, _failure: Ordering, ) -> Result { let prev = self.v.get(); if current == prev { self.v.set(new); } Ok(prev) } } // Any platform without atomics is unlikely to have multiple cores, so // writing via Cell will not be a race condition. #[cfg(not(target_has_atomic = "ptr"))] unsafe impl Sync for AtomicUsize {} // The LOGGER static holds a pointer to the global logger. It is protected by // the STATE static which determines whether LOGGER has been initialized yet. static mut LOGGER: &dyn Log = &NopLogger; static STATE: AtomicUsize = AtomicUsize::new(0); // There are three different states that we care about: the logger's // uninitialized, the logger's initializing (set_logger's been called but // LOGGER hasn't actually been set yet), or the logger's active. const UNINITIALIZED: usize = 0; const INITIALIZING: usize = 1; const INITIALIZED: usize = 2; static MAX_LOG_LEVEL_FILTER: AtomicUsize = AtomicUsize::new(0); static LOG_LEVEL_NAMES: [&str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; static SET_LOGGER_ERROR: &str = "attempted to set a logger after the logging system \ was already initialized"; static LEVEL_PARSE_ERROR: &str = "attempted to convert a string that doesn't match an existing log level"; /// An enum representing the available verbosity levels of the logger. /// /// Typical usage includes: checking if a certain `Level` is enabled with /// [`log_enabled!`](macro.log_enabled.html), specifying the `Level` of /// [`log!`](macro.log.html), and comparing a `Level` directly to a /// [`LevelFilter`](enum.LevelFilter.html). #[repr(usize)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub enum Level { /// The "error" level. /// /// Designates very serious errors. // This way these line up with the discriminants for LevelFilter below // This works because Rust treats field-less enums the same way as C does: // https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-field-less-enumerations Error = 1, /// The "warn" level. /// /// Designates hazardous situations. Warn, /// The "info" level. /// /// Designates useful information. Info, /// The "debug" level. /// /// Designates lower priority information. Debug, /// The "trace" level. /// /// Designates very low priority, often extremely verbose, information. Trace, } impl PartialEq for Level { #[inline] fn eq(&self, other: &LevelFilter) -> bool { *self as usize == *other as usize } } impl PartialOrd for Level { #[inline] fn partial_cmp(&self, other: &LevelFilter) -> Option { Some((*self as usize).cmp(&(*other as usize))) } } fn ok_or(t: Option, e: E) -> Result { match t { Some(t) => Ok(t), None => Err(e), } } impl FromStr for Level { type Err = ParseLevelError; fn from_str(level: &str) -> Result { ok_or( LOG_LEVEL_NAMES .iter() .position(|&name| name.eq_ignore_ascii_case(level)) .into_iter() .filter(|&idx| idx != 0) .map(|idx| Level::from_usize(idx).unwrap()) .next(), ParseLevelError(()), ) } } impl fmt::Display for Level { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.pad(self.as_str()) } } impl Level { fn from_usize(u: usize) -> Option { match u { 1 => Some(Level::Error), 2 => Some(Level::Warn), 3 => Some(Level::Info), 4 => Some(Level::Debug), 5 => Some(Level::Trace), _ => None, } } /// Returns the most verbose logging level. #[inline] pub fn max() -> Level { Level::Trace } /// Converts the `Level` to the equivalent `LevelFilter`. #[inline] pub fn to_level_filter(&self) -> LevelFilter { LevelFilter::from_usize(*self as usize).unwrap() } /// Returns the string representation of the `Level`. /// /// This returns the same string as the `fmt::Display` implementation. pub fn as_str(&self) -> &'static str { LOG_LEVEL_NAMES[*self as usize] } /// Iterate through all supported logging levels. /// /// The order of iteration is from more severe to less severe log messages. /// /// # Examples /// /// ``` /// use log::Level; /// /// let mut levels = Level::iter(); /// /// assert_eq!(Some(Level::Error), levels.next()); /// assert_eq!(Some(Level::Trace), levels.last()); /// ``` pub fn iter() -> impl Iterator { (1..6).map(|i| Self::from_usize(i).unwrap()) } } /// An enum representing the available verbosity level filters of the logger. /// /// A `LevelFilter` may be compared directly to a [`Level`]. Use this type /// to get and set the maximum log level with [`max_level()`] and [`set_max_level`]. /// /// [`Level`]: enum.Level.html /// [`max_level()`]: fn.max_level.html /// [`set_max_level`]: fn.set_max_level.html #[repr(usize)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub enum LevelFilter { /// A level lower than all log levels. Off, /// Corresponds to the `Error` log level. Error, /// Corresponds to the `Warn` log level. Warn, /// Corresponds to the `Info` log level. Info, /// Corresponds to the `Debug` log level. Debug, /// Corresponds to the `Trace` log level. Trace, } impl PartialEq for LevelFilter { #[inline] fn eq(&self, other: &Level) -> bool { other.eq(self) } } impl PartialOrd for LevelFilter { #[inline] fn partial_cmp(&self, other: &Level) -> Option { Some((*self as usize).cmp(&(*other as usize))) } } impl FromStr for LevelFilter { type Err = ParseLevelError; fn from_str(level: &str) -> Result { ok_or( LOG_LEVEL_NAMES .iter() .position(|&name| name.eq_ignore_ascii_case(level)) .map(|p| LevelFilter::from_usize(p).unwrap()), ParseLevelError(()), ) } } impl fmt::Display for LevelFilter { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.pad(self.as_str()) } } impl LevelFilter { fn from_usize(u: usize) -> Option { match u { 0 => Some(LevelFilter::Off), 1 => Some(LevelFilter::Error), 2 => Some(LevelFilter::Warn), 3 => Some(LevelFilter::Info), 4 => Some(LevelFilter::Debug), 5 => Some(LevelFilter::Trace), _ => None, } } /// Returns the most verbose logging level filter. #[inline] pub fn max() -> LevelFilter { LevelFilter::Trace } /// Converts `self` to the equivalent `Level`. /// /// Returns `None` if `self` is `LevelFilter::Off`. #[inline] pub fn to_level(&self) -> Option { Level::from_usize(*self as usize) } /// Returns the string representation of the `LevelFilter`. /// /// This returns the same string as the `fmt::Display` implementation. pub fn as_str(&self) -> &'static str { LOG_LEVEL_NAMES[*self as usize] } /// Iterate through all supported filtering levels. /// /// The order of iteration is from less to more verbose filtering. /// /// # Examples /// /// ``` /// use log::LevelFilter; /// /// let mut levels = LevelFilter::iter(); /// /// assert_eq!(Some(LevelFilter::Off), levels.next()); /// assert_eq!(Some(LevelFilter::Trace), levels.last()); /// ``` pub fn iter() -> impl Iterator { (0..6).map(|i| Self::from_usize(i).unwrap()) } } #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] enum MaybeStaticStr<'a> { Static(&'static str), Borrowed(&'a str), } impl<'a> MaybeStaticStr<'a> { #[inline] fn get(&self) -> &'a str { match *self { MaybeStaticStr::Static(s) => s, MaybeStaticStr::Borrowed(s) => s, } } } /// The "payload" of a log message. /// /// # Use /// /// `Record` structures are passed as parameters to the [`log`][method.log] /// method of the [`Log`] trait. Logger implementors manipulate these /// structures in order to display log messages. `Record`s are automatically /// created by the [`log!`] macro and so are not seen by log users. /// /// Note that the [`level()`] and [`target()`] accessors are equivalent to /// `self.metadata().level()` and `self.metadata().target()` respectively. /// These methods are provided as a convenience for users of this structure. /// /// # Example /// /// The following example shows a simple logger that displays the level, /// module path, and message of any `Record` that is passed to it. /// /// ```edition2018 /// struct SimpleLogger; /// /// impl log::Log for SimpleLogger { /// fn enabled(&self, _metadata: &log::Metadata) -> bool { /// true /// } /// /// fn log(&self, record: &log::Record) { /// if !self.enabled(record.metadata()) { /// return; /// } /// /// println!("{}:{} -- {}", /// record.level(), /// record.target(), /// record.args()); /// } /// fn flush(&self) {} /// } /// ``` /// /// [method.log]: trait.Log.html#tymethod.log /// [`Log`]: trait.Log.html /// [`log!`]: macro.log.html /// [`level()`]: struct.Record.html#method.level /// [`target()`]: struct.Record.html#method.target #[derive(Clone, Debug)] pub struct Record<'a> { metadata: Metadata<'a>, args: fmt::Arguments<'a>, module_path: Option>, file: Option>, line: Option, #[cfg(feature = "kv_unstable")] key_values: KeyValues<'a>, } // This wrapper type is only needed so we can // `#[derive(Debug)]` on `Record`. It also // provides a useful `Debug` implementation for // the underlying `Source`. #[cfg(feature = "kv_unstable")] #[derive(Clone)] struct KeyValues<'a>(&'a dyn kv::Source); #[cfg(feature = "kv_unstable")] impl<'a> fmt::Debug for KeyValues<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut visitor = f.debug_map(); self.0.visit(&mut visitor).map_err(|_| fmt::Error)?; visitor.finish() } } impl<'a> Record<'a> { /// Returns a new builder. #[inline] pub fn builder() -> RecordBuilder<'a> { RecordBuilder::new() } /// The message body. #[inline] pub fn args(&self) -> &fmt::Arguments<'a> { &self.args } /// Metadata about the log directive. #[inline] pub fn metadata(&self) -> &Metadata<'a> { &self.metadata } /// The verbosity level of the message. #[inline] pub fn level(&self) -> Level { self.metadata.level() } /// The name of the target of the directive. #[inline] pub fn target(&self) -> &'a str { self.metadata.target() } /// The module path of the message. #[inline] pub fn module_path(&self) -> Option<&'a str> { self.module_path.map(|s| s.get()) } /// The module path of the message, if it is a `'static` string. #[inline] pub fn module_path_static(&self) -> Option<&'static str> { match self.module_path { Some(MaybeStaticStr::Static(s)) => Some(s), _ => None, } } /// The source file containing the message. #[inline] pub fn file(&self) -> Option<&'a str> { self.file.map(|s| s.get()) } /// The module path of the message, if it is a `'static` string. #[inline] pub fn file_static(&self) -> Option<&'static str> { match self.file { Some(MaybeStaticStr::Static(s)) => Some(s), _ => None, } } /// The line containing the message. #[inline] pub fn line(&self) -> Option { self.line } /// The structured key-value pairs associated with the message. #[cfg(feature = "kv_unstable")] #[inline] pub fn key_values(&self) -> &dyn kv::Source { self.key_values.0 } /// Create a new [`RecordBuilder`](struct.RecordBuilder.html) based on this record. #[cfg(feature = "kv_unstable")] #[inline] pub fn to_builder(&self) -> RecordBuilder { RecordBuilder { record: Record { metadata: Metadata { level: self.metadata.level, target: self.metadata.target, }, args: self.args, module_path: self.module_path, file: self.file, line: self.line, key_values: self.key_values.clone(), }, } } } /// Builder for [`Record`](struct.Record.html). /// /// Typically should only be used by log library creators or for testing and "shim loggers". /// The `RecordBuilder` can set the different parameters of `Record` object, and returns /// the created object when `build` is called. /// /// # Examples /// /// ```edition2018 /// use log::{Level, Record}; /// /// let record = Record::builder() /// .args(format_args!("Error!")) /// .level(Level::Error) /// .target("myApp") /// .file(Some("server.rs")) /// .line(Some(144)) /// .module_path(Some("server")) /// .build(); /// ``` /// /// Alternatively, use [`MetadataBuilder`](struct.MetadataBuilder.html): /// /// ```edition2018 /// use log::{Record, Level, MetadataBuilder}; /// /// let error_metadata = MetadataBuilder::new() /// .target("myApp") /// .level(Level::Error) /// .build(); /// /// let record = Record::builder() /// .metadata(error_metadata) /// .args(format_args!("Error!")) /// .line(Some(433)) /// .file(Some("app.rs")) /// .module_path(Some("server")) /// .build(); /// ``` #[derive(Debug)] pub struct RecordBuilder<'a> { record: Record<'a>, } impl<'a> RecordBuilder<'a> { /// Construct new `RecordBuilder`. /// /// The default options are: /// /// - `args`: [`format_args!("")`] /// - `metadata`: [`Metadata::builder().build()`] /// - `module_path`: `None` /// - `file`: `None` /// - `line`: `None` /// /// [`format_args!("")`]: https://doc.rust-lang.org/std/macro.format_args.html /// [`Metadata::builder().build()`]: struct.MetadataBuilder.html#method.build #[inline] pub fn new() -> RecordBuilder<'a> { RecordBuilder { record: Record { args: format_args!(""), metadata: Metadata::builder().build(), module_path: None, file: None, line: None, #[cfg(feature = "kv_unstable")] key_values: KeyValues(&Option::None::<(kv::Key, kv::Value)>), }, } } /// Set [`args`](struct.Record.html#method.args). #[inline] pub fn args(&mut self, args: fmt::Arguments<'a>) -> &mut RecordBuilder<'a> { self.record.args = args; self } /// Set [`metadata`](struct.Record.html#method.metadata). Construct a `Metadata` object with [`MetadataBuilder`](struct.MetadataBuilder.html). #[inline] pub fn metadata(&mut self, metadata: Metadata<'a>) -> &mut RecordBuilder<'a> { self.record.metadata = metadata; self } /// Set [`Metadata::level`](struct.Metadata.html#method.level). #[inline] pub fn level(&mut self, level: Level) -> &mut RecordBuilder<'a> { self.record.metadata.level = level; self } /// Set [`Metadata::target`](struct.Metadata.html#method.target) #[inline] pub fn target(&mut self, target: &'a str) -> &mut RecordBuilder<'a> { self.record.metadata.target = target; self } /// Set [`module_path`](struct.Record.html#method.module_path) #[inline] pub fn module_path(&mut self, path: Option<&'a str>) -> &mut RecordBuilder<'a> { self.record.module_path = path.map(MaybeStaticStr::Borrowed); self } /// Set [`module_path`](struct.Record.html#method.module_path) to a `'static` string #[inline] pub fn module_path_static(&mut self, path: Option<&'static str>) -> &mut RecordBuilder<'a> { self.record.module_path = path.map(MaybeStaticStr::Static); self } /// Set [`file`](struct.Record.html#method.file) #[inline] pub fn file(&mut self, file: Option<&'a str>) -> &mut RecordBuilder<'a> { self.record.file = file.map(MaybeStaticStr::Borrowed); self } /// Set [`file`](struct.Record.html#method.file) to a `'static` string. #[inline] pub fn file_static(&mut self, file: Option<&'static str>) -> &mut RecordBuilder<'a> { self.record.file = file.map(MaybeStaticStr::Static); self } /// Set [`line`](struct.Record.html#method.line) #[inline] pub fn line(&mut self, line: Option) -> &mut RecordBuilder<'a> { self.record.line = line; self } /// Set [`key_values`](struct.Record.html#method.key_values) #[cfg(feature = "kv_unstable")] #[inline] pub fn key_values(&mut self, kvs: &'a dyn kv::Source) -> &mut RecordBuilder<'a> { self.record.key_values = KeyValues(kvs); self } /// Invoke the builder and return a `Record` #[inline] pub fn build(&self) -> Record<'a> { self.record.clone() } } impl<'a> Default for RecordBuilder<'a> { fn default() -> Self { Self::new() } } /// Metadata about a log message. /// /// # Use /// /// `Metadata` structs are created when users of the library use /// logging macros. /// /// They are consumed by implementations of the `Log` trait in the /// `enabled` method. /// /// `Record`s use `Metadata` to determine the log message's severity /// and target. /// /// Users should use the `log_enabled!` macro in their code to avoid /// constructing expensive log messages. /// /// # Examples /// /// ```edition2018 /// use log::{Record, Level, Metadata}; /// /// struct MyLogger; /// /// impl log::Log for MyLogger { /// fn enabled(&self, metadata: &Metadata) -> bool { /// metadata.level() <= Level::Info /// } /// /// fn log(&self, record: &Record) { /// if self.enabled(record.metadata()) { /// println!("{} - {}", record.level(), record.args()); /// } /// } /// fn flush(&self) {} /// } /// /// # fn main(){} /// ``` #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub struct Metadata<'a> { level: Level, target: &'a str, } impl<'a> Metadata<'a> { /// Returns a new builder. #[inline] pub fn builder() -> MetadataBuilder<'a> { MetadataBuilder::new() } /// The verbosity level of the message. #[inline] pub fn level(&self) -> Level { self.level } /// The name of the target of the directive. #[inline] pub fn target(&self) -> &'a str { self.target } } /// Builder for [`Metadata`](struct.Metadata.html). /// /// Typically should only be used by log library creators or for testing and "shim loggers". /// The `MetadataBuilder` can set the different parameters of a `Metadata` object, and returns /// the created object when `build` is called. /// /// # Example /// /// ```edition2018 /// let target = "myApp"; /// use log::{Level, MetadataBuilder}; /// let metadata = MetadataBuilder::new() /// .level(Level::Debug) /// .target(target) /// .build(); /// ``` #[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub struct MetadataBuilder<'a> { metadata: Metadata<'a>, } impl<'a> MetadataBuilder<'a> { /// Construct a new `MetadataBuilder`. /// /// The default options are: /// /// - `level`: `Level::Info` /// - `target`: `""` #[inline] pub fn new() -> MetadataBuilder<'a> { MetadataBuilder { metadata: Metadata { level: Level::Info, target: "", }, } } /// Setter for [`level`](struct.Metadata.html#method.level). #[inline] pub fn level(&mut self, arg: Level) -> &mut MetadataBuilder<'a> { self.metadata.level = arg; self } /// Setter for [`target`](struct.Metadata.html#method.target). #[inline] pub fn target(&mut self, target: &'a str) -> &mut MetadataBuilder<'a> { self.metadata.target = target; self } /// Returns a `Metadata` object. #[inline] pub fn build(&self) -> Metadata<'a> { self.metadata.clone() } } impl<'a> Default for MetadataBuilder<'a> { fn default() -> Self { Self::new() } } /// A trait encapsulating the operations required of a logger. pub trait Log: Sync + Send { /// Determines if a log message with the specified metadata would be /// logged. /// /// This is used by the `log_enabled!` macro to allow callers to avoid /// expensive computation of log message arguments if the message would be /// discarded anyway. /// /// # For implementors /// /// This method isn't called automatically by the `log!` macros. /// It's up to an implementation of the `Log` trait to call `enabled` in its own /// `log` method implementation to guarantee that filtering is applied. fn enabled(&self, metadata: &Metadata) -> bool; /// Logs the `Record`. /// /// # For implementors /// /// Note that `enabled` is *not* necessarily called before this method. /// Implementations of `log` should perform all necessary filtering /// internally. fn log(&self, record: &Record); /// Flushes any buffered records. fn flush(&self); } // Just used as a dummy initial value for LOGGER struct NopLogger; impl Log for NopLogger { fn enabled(&self, _: &Metadata) -> bool { false } fn log(&self, _: &Record) {} fn flush(&self) {} } impl Log for &'_ T where T: ?Sized + Log, { fn enabled(&self, metadata: &Metadata) -> bool { (**self).enabled(metadata) } fn log(&self, record: &Record) { (**self).log(record); } fn flush(&self) { (**self).flush(); } } #[cfg(feature = "std")] impl Log for std::boxed::Box where T: ?Sized + Log, { fn enabled(&self, metadata: &Metadata) -> bool { self.as_ref().enabled(metadata) } fn log(&self, record: &Record) { self.as_ref().log(record) } fn flush(&self) { self.as_ref().flush() } } #[cfg(feature = "std")] impl Log for std::sync::Arc where T: ?Sized + Log, { fn enabled(&self, metadata: &Metadata) -> bool { self.as_ref().enabled(metadata) } fn log(&self, record: &Record) { self.as_ref().log(record) } fn flush(&self) { self.as_ref().flush() } } /// Sets the global maximum log level. /// /// Generally, this should only be called by the active logging implementation. /// /// Note that `Trace` is the maximum level, because it provides the maximum amount of detail in the emitted logs. #[inline] #[cfg(target_has_atomic = "ptr")] pub fn set_max_level(level: LevelFilter) { MAX_LOG_LEVEL_FILTER.store(level as usize, Ordering::Relaxed); } /// A thread-unsafe version of [`set_max_level`]. /// /// This function is available on all platforms, even those that do not have /// support for atomics that is needed by [`set_max_level`]. /// /// In almost all cases, [`set_max_level`] should be preferred. /// /// # Safety /// /// This function is only safe to call when no other level setting function is /// called while this function still executes. /// /// This can be upheld by (for example) making sure that **there are no other /// threads**, and (on embedded) that **interrupts are disabled**. /// /// Is is safe to use all other logging functions while this function runs /// (including all logging macros). /// /// [`set_max_level`]: fn.set_max_level.html #[inline] pub unsafe fn set_max_level_racy(level: LevelFilter) { // `MAX_LOG_LEVEL_FILTER` uses a `Cell` as the underlying primitive when a // platform doesn't support `target_has_atomic = "ptr"`, so even though this looks the same // as `set_max_level` it may have different safety properties. MAX_LOG_LEVEL_FILTER.store(level as usize, Ordering::Relaxed); } /// Returns the current maximum log level. /// /// The [`log!`], [`error!`], [`warn!`], [`info!`], [`debug!`], and [`trace!`] macros check /// this value and discard any message logged at a higher level. The maximum /// log level is set by the [`set_max_level`] function. /// /// [`log!`]: macro.log.html /// [`error!`]: macro.error.html /// [`warn!`]: macro.warn.html /// [`info!`]: macro.info.html /// [`debug!`]: macro.debug.html /// [`trace!`]: macro.trace.html /// [`set_max_level`]: fn.set_max_level.html #[inline(always)] pub fn max_level() -> LevelFilter { // Since `LevelFilter` is `repr(usize)`, // this transmute is sound if and only if `MAX_LOG_LEVEL_FILTER` // is set to a usize that is a valid discriminant for `LevelFilter`. // Since `MAX_LOG_LEVEL_FILTER` is private, the only time it's set // is by `set_max_level` above, i.e. by casting a `LevelFilter` to `usize`. // So any usize stored in `MAX_LOG_LEVEL_FILTER` is a valid discriminant. unsafe { mem::transmute(MAX_LOG_LEVEL_FILTER.load(Ordering::Relaxed)) } } /// Sets the global logger to a `Box`. /// /// This is a simple convenience wrapper over `set_logger`, which takes a /// `Box` rather than a `&'static Log`. See the documentation for /// [`set_logger`] for more details. /// /// Requires the `std` feature. /// /// # Errors /// /// An error is returned if a logger has already been set. /// /// [`set_logger`]: fn.set_logger.html #[cfg(all(feature = "std", target_has_atomic = "ptr"))] pub fn set_boxed_logger(logger: Box) -> Result<(), SetLoggerError> { set_logger_inner(|| Box::leak(logger)) } /// Sets the global logger to a `&'static Log`. /// /// This function may only be called once in the lifetime of a program. Any log /// events that occur before the call to `set_logger` completes will be ignored. /// /// This function does not typically need to be called manually. Logger /// implementations should provide an initialization method that installs the /// logger internally. /// /// # Availability /// /// This method is available even when the `std` feature is disabled. However, /// it is currently unavailable on `thumbv6` targets, which lack support for /// some atomic operations which are used by this function. Even on those /// targets, [`set_logger_racy`] will be available. /// /// # Errors /// /// An error is returned if a logger has already been set. /// /// # Examples /// /// ```edition2018 /// use log::{error, info, warn, Record, Level, Metadata, LevelFilter}; /// /// static MY_LOGGER: MyLogger = MyLogger; /// /// struct MyLogger; /// /// impl log::Log for MyLogger { /// fn enabled(&self, metadata: &Metadata) -> bool { /// metadata.level() <= Level::Info /// } /// /// fn log(&self, record: &Record) { /// if self.enabled(record.metadata()) { /// println!("{} - {}", record.level(), record.args()); /// } /// } /// fn flush(&self) {} /// } /// /// # fn main(){ /// log::set_logger(&MY_LOGGER).unwrap(); /// log::set_max_level(LevelFilter::Info); /// /// info!("hello log"); /// warn!("warning"); /// error!("oops"); /// # } /// ``` /// /// [`set_logger_racy`]: fn.set_logger_racy.html #[cfg(target_has_atomic = "ptr")] pub fn set_logger(logger: &'static dyn Log) -> Result<(), SetLoggerError> { set_logger_inner(|| logger) } #[cfg(target_has_atomic = "ptr")] fn set_logger_inner(make_logger: F) -> Result<(), SetLoggerError> where F: FnOnce() -> &'static dyn Log, { let old_state = match STATE.compare_exchange( UNINITIALIZED, INITIALIZING, Ordering::SeqCst, Ordering::SeqCst, ) { Ok(s) | Err(s) => s, }; match old_state { UNINITIALIZED => { unsafe { LOGGER = make_logger(); } STATE.store(INITIALIZED, Ordering::SeqCst); Ok(()) } INITIALIZING => { while STATE.load(Ordering::SeqCst) == INITIALIZING { // TODO: replace with `hint::spin_loop` once MSRV is 1.49.0. #[allow(deprecated)] std::sync::atomic::spin_loop_hint(); } Err(SetLoggerError(())) } _ => Err(SetLoggerError(())), } } /// A thread-unsafe version of [`set_logger`]. /// /// This function is available on all platforms, even those that do not have /// support for atomics that is needed by [`set_logger`]. /// /// In almost all cases, [`set_logger`] should be preferred. /// /// # Safety /// /// This function is only safe to call when no other logger initialization /// function is called while this function still executes. /// /// This can be upheld by (for example) making sure that **there are no other /// threads**, and (on embedded) that **interrupts are disabled**. /// /// It is safe to use other logging functions while this function runs /// (including all logging macros). /// /// [`set_logger`]: fn.set_logger.html pub unsafe fn set_logger_racy(logger: &'static dyn Log) -> Result<(), SetLoggerError> { match STATE.load(Ordering::SeqCst) { UNINITIALIZED => { LOGGER = logger; STATE.store(INITIALIZED, Ordering::SeqCst); Ok(()) } INITIALIZING => { // This is just plain UB, since we were racing another initialization function unreachable!("set_logger_racy must not be used with other initialization functions") } _ => Err(SetLoggerError(())), } } /// The type returned by [`set_logger`] if [`set_logger`] has already been called. /// /// [`set_logger`]: fn.set_logger.html #[allow(missing_copy_implementations)] #[derive(Debug)] pub struct SetLoggerError(()); impl fmt::Display for SetLoggerError { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.write_str(SET_LOGGER_ERROR) } } // The Error trait is not available in libcore #[cfg(feature = "std")] impl error::Error for SetLoggerError {} /// The type returned by [`from_str`] when the string doesn't match any of the log levels. /// /// [`from_str`]: https://doc.rust-lang.org/std/str/trait.FromStr.html#tymethod.from_str #[allow(missing_copy_implementations)] #[derive(Debug, PartialEq, Eq)] pub struct ParseLevelError(()); impl fmt::Display for ParseLevelError { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.write_str(LEVEL_PARSE_ERROR) } } // The Error trait is not available in libcore #[cfg(feature = "std")] impl error::Error for ParseLevelError {} /// Returns a reference to the logger. /// /// If a logger has not been set, a no-op implementation is returned. pub fn logger() -> &'static dyn Log { if STATE.load(Ordering::SeqCst) != INITIALIZED { static NOP: NopLogger = NopLogger; &NOP } else { unsafe { LOGGER } } } // WARNING: this is not part of the crate's public API and is subject to change at any time #[doc(hidden)] pub mod __private_api; /// The statically resolved maximum log level. /// /// See the crate level documentation for information on how to configure this. /// /// This value is checked by the log macros, but not by the `Log`ger returned by /// the [`logger`] function. Code that manually calls functions on that value /// should compare the level against this value. /// /// [`logger`]: fn.logger.html pub const STATIC_MAX_LEVEL: LevelFilter = MAX_LEVEL_INNER; const MAX_LEVEL_INNER: LevelFilter = get_max_level_inner(); const fn get_max_level_inner() -> LevelFilter { #[allow(unreachable_code)] { #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] { return LevelFilter::Off; } #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] { return LevelFilter::Error; } #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] { return LevelFilter::Warn; } #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] { return LevelFilter::Info; } #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] { return LevelFilter::Debug; } #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] { return LevelFilter::Trace; } #[cfg(feature = "max_level_off")] { return LevelFilter::Off; } #[cfg(feature = "max_level_error")] { return LevelFilter::Error; } #[cfg(feature = "max_level_warn")] { return LevelFilter::Warn; } #[cfg(feature = "max_level_info")] { return LevelFilter::Info; } #[cfg(feature = "max_level_debug")] { return LevelFilter::Debug; } LevelFilter::Trace } } #[cfg(test)] mod tests { extern crate std; use super::{Level, LevelFilter, ParseLevelError}; use tests::std::string::ToString; #[test] fn test_levelfilter_from_str() { let tests = [ ("off", Ok(LevelFilter::Off)), ("error", Ok(LevelFilter::Error)), ("warn", Ok(LevelFilter::Warn)), ("info", Ok(LevelFilter::Info)), ("debug", Ok(LevelFilter::Debug)), ("trace", Ok(LevelFilter::Trace)), ("OFF", Ok(LevelFilter::Off)), ("ERROR", Ok(LevelFilter::Error)), ("WARN", Ok(LevelFilter::Warn)), ("INFO", Ok(LevelFilter::Info)), ("DEBUG", Ok(LevelFilter::Debug)), ("TRACE", Ok(LevelFilter::Trace)), ("asdf", Err(ParseLevelError(()))), ]; for &(s, ref expected) in &tests { assert_eq!(expected, &s.parse()); } } #[test] fn test_level_from_str() { let tests = [ ("OFF", Err(ParseLevelError(()))), ("error", Ok(Level::Error)), ("warn", Ok(Level::Warn)), ("info", Ok(Level::Info)), ("debug", Ok(Level::Debug)), ("trace", Ok(Level::Trace)), ("ERROR", Ok(Level::Error)), ("WARN", Ok(Level::Warn)), ("INFO", Ok(Level::Info)), ("DEBUG", Ok(Level::Debug)), ("TRACE", Ok(Level::Trace)), ("asdf", Err(ParseLevelError(()))), ]; for &(s, ref expected) in &tests { assert_eq!(expected, &s.parse()); } } #[test] fn test_level_as_str() { let tests = &[ (Level::Error, "ERROR"), (Level::Warn, "WARN"), (Level::Info, "INFO"), (Level::Debug, "DEBUG"), (Level::Trace, "TRACE"), ]; for (input, expected) in tests { assert_eq!(*expected, input.as_str()); } } #[test] fn test_level_show() { assert_eq!("INFO", Level::Info.to_string()); assert_eq!("ERROR", Level::Error.to_string()); } #[test] fn test_levelfilter_show() { assert_eq!("OFF", LevelFilter::Off.to_string()); assert_eq!("ERROR", LevelFilter::Error.to_string()); } #[test] fn test_cross_cmp() { assert!(Level::Debug > LevelFilter::Error); assert!(LevelFilter::Warn < Level::Trace); assert!(LevelFilter::Off < Level::Error); } #[test] fn test_cross_eq() { assert!(Level::Error == LevelFilter::Error); assert!(LevelFilter::Off != Level::Error); assert!(Level::Trace == LevelFilter::Trace); } #[test] fn test_to_level() { assert_eq!(Some(Level::Error), LevelFilter::Error.to_level()); assert_eq!(None, LevelFilter::Off.to_level()); assert_eq!(Some(Level::Debug), LevelFilter::Debug.to_level()); } #[test] fn test_to_level_filter() { assert_eq!(LevelFilter::Error, Level::Error.to_level_filter()); assert_eq!(LevelFilter::Trace, Level::Trace.to_level_filter()); } #[test] fn test_level_filter_as_str() { let tests = &[ (LevelFilter::Off, "OFF"), (LevelFilter::Error, "ERROR"), (LevelFilter::Warn, "WARN"), (LevelFilter::Info, "INFO"), (LevelFilter::Debug, "DEBUG"), (LevelFilter::Trace, "TRACE"), ]; for (input, expected) in tests { assert_eq!(*expected, input.as_str()); } } #[test] #[cfg(feature = "std")] fn test_error_trait() { use super::SetLoggerError; let e = SetLoggerError(()); assert_eq!( &e.to_string(), "attempted to set a logger after the logging system \ was already initialized" ); } #[test] fn test_metadata_builder() { use super::MetadataBuilder; let target = "myApp"; let metadata_test = MetadataBuilder::new() .level(Level::Debug) .target(target) .build(); assert_eq!(metadata_test.level(), Level::Debug); assert_eq!(metadata_test.target(), "myApp"); } #[test] fn test_metadata_convenience_builder() { use super::Metadata; let target = "myApp"; let metadata_test = Metadata::builder() .level(Level::Debug) .target(target) .build(); assert_eq!(metadata_test.level(), Level::Debug); assert_eq!(metadata_test.target(), "myApp"); } #[test] fn test_record_builder() { use super::{MetadataBuilder, RecordBuilder}; let target = "myApp"; let metadata = MetadataBuilder::new().target(target).build(); let fmt_args = format_args!("hello"); let record_test = RecordBuilder::new() .args(fmt_args) .metadata(metadata) .module_path(Some("foo")) .file(Some("bar")) .line(Some(30)) .build(); assert_eq!(record_test.metadata().target(), "myApp"); assert_eq!(record_test.module_path(), Some("foo")); assert_eq!(record_test.file(), Some("bar")); assert_eq!(record_test.line(), Some(30)); } #[test] fn test_record_convenience_builder() { use super::{Metadata, Record}; let target = "myApp"; let metadata = Metadata::builder().target(target).build(); let fmt_args = format_args!("hello"); let record_test = Record::builder() .args(fmt_args) .metadata(metadata) .module_path(Some("foo")) .file(Some("bar")) .line(Some(30)) .build(); assert_eq!(record_test.target(), "myApp"); assert_eq!(record_test.module_path(), Some("foo")); assert_eq!(record_test.file(), Some("bar")); assert_eq!(record_test.line(), Some(30)); } #[test] fn test_record_complete_builder() { use super::{Level, Record}; let target = "myApp"; let record_test = Record::builder() .module_path(Some("foo")) .file(Some("bar")) .line(Some(30)) .target(target) .level(Level::Error) .build(); assert_eq!(record_test.target(), "myApp"); assert_eq!(record_test.level(), Level::Error); assert_eq!(record_test.module_path(), Some("foo")); assert_eq!(record_test.file(), Some("bar")); assert_eq!(record_test.line(), Some(30)); } #[test] #[cfg(feature = "kv_unstable")] fn test_record_key_values_builder() { use super::Record; use kv::{self, Visitor}; struct TestVisitor { seen_pairs: usize, } impl<'kvs> Visitor<'kvs> for TestVisitor { fn visit_pair( &mut self, _: kv::Key<'kvs>, _: kv::Value<'kvs>, ) -> Result<(), kv::Error> { self.seen_pairs += 1; Ok(()) } } let kvs: &[(&str, i32)] = &[("a", 1), ("b", 2)]; let record_test = Record::builder().key_values(&kvs).build(); let mut visitor = TestVisitor { seen_pairs: 0 }; record_test.key_values().visit(&mut visitor).unwrap(); assert_eq!(2, visitor.seen_pairs); } #[test] #[cfg(feature = "kv_unstable")] fn test_record_key_values_get_coerce() { use super::Record; let kvs: &[(&str, &str)] = &[("a", "1"), ("b", "2")]; let record = Record::builder().key_values(&kvs).build(); assert_eq!( "2", record .key_values() .get("b".into()) .expect("missing key") .to_borrowed_str() .expect("invalid value") ); } // Test that the `impl Log for Foo` blocks work // This test mostly operates on a type level, so failures will be compile errors #[test] fn test_foreign_impl() { use super::Log; #[cfg(feature = "std")] use std::sync::Arc; fn assert_is_log() {} assert_is_log::<&dyn Log>(); #[cfg(feature = "std")] assert_is_log::>(); #[cfg(feature = "std")] assert_is_log::>(); // Assert these statements for all T: Log + ?Sized #[allow(unused)] fn forall() { #[cfg(feature = "std")] assert_is_log::>(); assert_is_log::<&T>(); #[cfg(feature = "std")] assert_is_log::>(); } } }