| // 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 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| //! A lightweight logging facade. |
| //! |
| //! A logging facade 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 |
| //! framework 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. |
| //! |
| //! # Use |
| //! |
| //! ## 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 |
| //! |
| //! ```rust |
| //! # #![allow(unstable)] |
| //! #[macro_use] |
| //! extern crate log; |
| //! |
| //! # #[derive(Debug)] pub struct Yak(String); |
| //! # impl Yak { fn shave(&self, _: u32) {} } |
| //! # fn find_a_razor() -> Result<u32, u32> { Ok(1) } |
| //! pub fn shave_the_yak(yak: &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 framework and initialize it early in the |
| //! runtime of the program. Logging frameworks will typically include a |
| //! function to do this. Any log messages generated before the framework 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. |
| //! |
| //! ### Examples |
| //! |
| //! ```rust,ignore |
| //! #[macro_use] |
| //! extern crate log; |
| //! extern crate my_logger; |
| //! |
| //! fn main() { |
| //! my_logger::init(); |
| //! |
| //! info!("starting up"); |
| //! |
| //! // ... |
| //! } |
| //! ``` |
| //! |
| //! # Logger implementations |
| //! |
| //! Loggers implement the `Log` trait. Here's a very basic example that simply |
| //! logs all messages at the `Error`, `Warn` or `Info` levels to stdout: |
| //! |
| //! ```rust |
| //! extern crate log; |
| //! |
| //! use log::{LogRecord, LogLevel, LogMetadata}; |
| //! |
| //! struct SimpleLogger; |
| //! |
| //! impl log::Log for SimpleLogger { |
| //! fn enabled(&self, metadata: &LogMetadata) -> bool { |
| //! metadata.level() <= LogLevel::Info |
| //! } |
| //! |
| //! fn log(&self, record: &LogRecord) { |
| //! if self.enabled(record.metadata()) { |
| //! println!("{} - {}", record.level(), record.args()); |
| //! } |
| //! } |
| //! } |
| //! |
| //! # fn main() {} |
| //! ``` |
| //! |
| //! Loggers are installed by calling the `set_logger` function. It takes a |
| //! closure which is provided a `MaxLogLevel` token and returns a `Log` trait |
| //! object. The `MaxLogLevel` token controls the global maximum log level. The |
| //! logging facade uses this as an optimization to improve performance of log |
| //! messages at levels that are disabled. In the case of our example logger, |
| //! we'll want to set the maximum log level to `Info`, since we ignore any |
| //! `Debug` or `Trace` level log messages. A logging framework should provide a |
| //! function that wraps a call to `set_logger`, handling initialization of the |
| //! logger: |
| //! |
| //! ```rust |
| //! # extern crate log; |
| //! # use log::{LogLevel, LogLevelFilter, SetLoggerError, LogMetadata}; |
| //! # struct SimpleLogger; |
| //! # impl log::Log for SimpleLogger { |
| //! # fn enabled(&self, _: &LogMetadata) -> bool { false } |
| //! # fn log(&self, _: &log::LogRecord) {} |
| //! # } |
| //! # fn main() {} |
| //! # #[cfg(feature = "use_std")] |
| //! pub fn init() -> Result<(), SetLoggerError> { |
| //! log::set_logger(|max_log_level| { |
| //! max_log_level.set(LogLevelFilter::Info); |
| //! Box::new(SimpleLogger) |
| //! }) |
| //! } |
| //! ``` |
| //! |
| //! # Use with `no_std` |
| //! |
| //! To use the `log` crate without depending on `libstd`, you need to specify |
| //! `default-features = false` when specifying the dependency in `Cargo.toml`. |
| //! This makes no difference to libraries using `log` since the logging API |
| //! remains the same. However executables will need to use the `set_logger_raw` |
| //! function to initialize a logger and the `shutdown_logger_raw` function to |
| //! shut down the global logger before exiting: |
| //! |
| //! ```rust |
| //! # extern crate log; |
| //! # use log::{LogLevel, LogLevelFilter, SetLoggerError, ShutdownLoggerError, |
| //! # LogMetadata}; |
| //! # struct SimpleLogger; |
| //! # impl log::Log for SimpleLogger { |
| //! # fn enabled(&self, _: &LogMetadata) -> bool { false } |
| //! # fn log(&self, _: &log::LogRecord) {} |
| //! # } |
| //! # impl SimpleLogger { |
| //! # fn flush(&self) {} |
| //! # } |
| //! # fn main() {} |
| //! pub fn init() -> Result<(), SetLoggerError> { |
| //! unsafe { |
| //! log::set_logger_raw(|max_log_level| { |
| //! static LOGGER: SimpleLogger = SimpleLogger; |
| //! max_log_level.set(LogLevelFilter::Info); |
| //! &SimpleLogger |
| //! }) |
| //! } |
| //! } |
| //! pub fn shutdown() -> Result<(), ShutdownLoggerError> { |
| //! log::shutdown_logger_raw().map(|logger| { |
| //! let logger = unsafe { &*(logger as *const SimpleLogger) }; |
| //! logger.flush(); |
| //! }) |
| //! } |
| //! ``` |
| |
| #![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://doc.rust-lang.org/log/")] |
| #![warn(missing_docs)] |
| #![cfg_attr(feature = "nightly", feature(panic_handler))] |
| |
| #![cfg_attr(not(feature = "use_std"), 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(not(feature = "use_std"))] |
| extern crate core as std; |
| extern crate log; |
| |
| use std::cmp; |
| #[cfg(feature = "use_std")] |
| use std::error; |
| use std::fmt; |
| use std::mem; |
| use std::ops::Deref; |
| use std::str::FromStr; |
| use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; |
| #[macro_use] |
| mod macros; |
| |
| // The setup here is a bit weird to make shutdown_logger_raw work. |
| // |
| // There are four 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), the logger's active, or the logger is |
| // shut down after calling shutdown_logger_raw. |
| // |
| // 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. |
| // |
| // The shutdown_logger_raw routine needs to make sure that no threads are |
| // actively logging before it returns. The number of actively logging threads is |
| // tracked in the REFCOUNT static. The routine first sets STATE back to |
| // INITIALIZING. All logging calls past that point will immediately return |
| // without accessing the logger. At that point, the at_exit routine just waits |
| // for the refcount to reach 0 before deallocating the logger. Note that the |
| // refcount does not necessarily monotonically decrease at this point, as new |
| // log calls still increment and decrement it, but the interval in between is |
| // small enough that the wait is really just for the active log calls to finish. |
| |
| static mut LOGGER: *const Log = &NopLogger; |
| static STATE: AtomicUsize = ATOMIC_USIZE_INIT; |
| static REFCOUNT: AtomicUsize = ATOMIC_USIZE_INIT; |
| |
| const INITIALIZING: usize = 1; |
| const INITIALIZED: usize = 2; |
| |
| static LOG_LEVEL_NAMES: [&'static str; 6] = ["OFF", "ERROR", "WARN", "INFO", |
| "DEBUG", "TRACE"]; |
| |
| /// An enum representing the available verbosity levels of the logging framework |
| /// |
| /// A `LogLevel` may be compared directly to a `LogLevelFilter`. |
| #[repr(usize)] |
| #[derive(Copy, Eq, Debug)] |
| pub enum LogLevel { |
| /// The "error" level. |
| /// |
| /// Designates very serious errors. |
| Error = 1, // This way these line up with the discriminants for LogLevelFilter below |
| /// 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 Clone for LogLevel { |
| #[inline] |
| fn clone(&self) -> LogLevel { |
| *self |
| } |
| } |
| |
| impl PartialEq for LogLevel { |
| #[inline] |
| fn eq(&self, other: &LogLevel) -> bool { |
| *self as usize == *other as usize |
| } |
| } |
| |
| impl PartialEq<LogLevelFilter> for LogLevel { |
| #[inline] |
| fn eq(&self, other: &LogLevelFilter) -> bool { |
| *self as usize == *other as usize |
| } |
| } |
| |
| impl PartialOrd for LogLevel { |
| #[inline] |
| fn partial_cmp(&self, other: &LogLevel) -> Option<cmp::Ordering> { |
| Some(self.cmp(other)) |
| } |
| } |
| |
| impl PartialOrd<LogLevelFilter> for LogLevel { |
| #[inline] |
| fn partial_cmp(&self, other: &LogLevelFilter) -> Option<cmp::Ordering> { |
| Some((*self as usize).cmp(&(*other as usize))) |
| } |
| } |
| |
| impl Ord for LogLevel { |
| #[inline] |
| fn cmp(&self, other: &LogLevel) -> cmp::Ordering { |
| (*self as usize).cmp(&(*other as usize)) |
| } |
| } |
| |
| fn ok_or<T, E>(t: Option<T>, e: E) -> Result<T, E> { |
| match t { |
| Some(t) => Ok(t), |
| None => Err(e), |
| } |
| } |
| |
| // Reimplemented here because std::ascii is not available in libcore |
| fn eq_ignore_ascii_case(a: &str, b: &str) -> bool { |
| fn to_ascii_uppercase(c: u8) -> u8 { |
| if c >= b'a' && c <= b'z' { |
| c - b'a' + b'A' |
| } else { |
| c |
| } |
| } |
| |
| if a.len() == b.len() { |
| a.bytes() |
| .zip(b.bytes()) |
| .all(|(a, b)| to_ascii_uppercase(a) == to_ascii_uppercase(b)) |
| } else { |
| false |
| } |
| } |
| |
| impl FromStr for LogLevel { |
| type Err = (); |
| fn from_str(level: &str) -> Result<LogLevel, ()> { |
| ok_or(LOG_LEVEL_NAMES.iter() |
| .position(|&name| eq_ignore_ascii_case(name, level)) |
| .into_iter() |
| .filter(|&idx| idx != 0) |
| .map(|idx| LogLevel::from_usize(idx).unwrap()) |
| .next(), ()) |
| } |
| } |
| |
| impl fmt::Display for LogLevel { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| fmt.pad(LOG_LEVEL_NAMES[*self as usize]) |
| } |
| } |
| |
| impl LogLevel { |
| fn from_usize(u: usize) -> Option<LogLevel> { |
| match u { |
| 1 => Some(LogLevel::Error), |
| 2 => Some(LogLevel::Warn), |
| 3 => Some(LogLevel::Info), |
| 4 => Some(LogLevel::Debug), |
| 5 => Some(LogLevel::Trace), |
| _ => None |
| } |
| } |
| |
| fn from_new(level: log::Level) -> LogLevel { |
| match level { |
| log::Level::Error => LogLevel::Error, |
| log::Level::Warn => LogLevel::Warn, |
| log::Level::Info => LogLevel::Info, |
| log::Level::Debug => LogLevel::Debug, |
| log::Level::Trace => LogLevel::Trace, |
| } |
| } |
| |
| fn to_new(&self) -> log::Level { |
| match *self { |
| LogLevel::Error => log::Level::Error, |
| LogLevel::Warn => log::Level::Warn, |
| LogLevel::Info => log::Level::Info, |
| LogLevel::Debug => log::Level::Debug, |
| LogLevel::Trace => log::Level::Trace, |
| } |
| } |
| |
| /// Returns the most verbose logging level. |
| #[inline] |
| pub fn max() -> LogLevel { |
| LogLevel::Trace |
| } |
| |
| /// Converts the `LogLevel` to the equivalent `LogLevelFilter`. |
| #[inline] |
| pub fn to_log_level_filter(&self) -> LogLevelFilter { |
| LogLevelFilter::from_usize(*self as usize).unwrap() |
| } |
| } |
| |
| /// An enum representing the available verbosity level filters of the logging |
| /// framework. |
| /// |
| /// A `LogLevelFilter` may be compared directly to a `LogLevel`. |
| #[repr(usize)] |
| #[derive(Copy, Eq, Debug)] |
| pub enum LogLevelFilter { |
| /// 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, |
| } |
| |
| // Deriving generates terrible impls of these traits |
| |
| impl Clone for LogLevelFilter { |
| #[inline] |
| fn clone(&self) -> LogLevelFilter { |
| *self |
| } |
| } |
| |
| impl PartialEq for LogLevelFilter { |
| #[inline] |
| fn eq(&self, other: &LogLevelFilter) -> bool { |
| *self as usize == *other as usize |
| } |
| } |
| |
| impl PartialEq<LogLevel> for LogLevelFilter { |
| #[inline] |
| fn eq(&self, other: &LogLevel) -> bool { |
| other.eq(self) |
| } |
| } |
| |
| impl PartialOrd for LogLevelFilter { |
| #[inline] |
| fn partial_cmp(&self, other: &LogLevelFilter) -> Option<cmp::Ordering> { |
| Some(self.cmp(other)) |
| } |
| } |
| |
| impl PartialOrd<LogLevel> for LogLevelFilter { |
| #[inline] |
| fn partial_cmp(&self, other: &LogLevel) -> Option<cmp::Ordering> { |
| other.partial_cmp(self).map(|x| x.reverse()) |
| } |
| } |
| |
| impl Ord for LogLevelFilter { |
| #[inline] |
| fn cmp(&self, other: &LogLevelFilter) -> cmp::Ordering { |
| (*self as usize).cmp(&(*other as usize)) |
| } |
| } |
| |
| impl FromStr for LogLevelFilter { |
| type Err = (); |
| fn from_str(level: &str) -> Result<LogLevelFilter, ()> { |
| ok_or(LOG_LEVEL_NAMES.iter() |
| .position(|&name| eq_ignore_ascii_case(name, level)) |
| .map(|p| LogLevelFilter::from_usize(p).unwrap()), ()) |
| } |
| } |
| |
| impl fmt::Display for LogLevelFilter { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| write!(fmt, "{}", LOG_LEVEL_NAMES[*self as usize]) |
| } |
| } |
| |
| impl LogLevelFilter { |
| fn from_usize(u: usize) -> Option<LogLevelFilter> { |
| match u { |
| 0 => Some(LogLevelFilter::Off), |
| 1 => Some(LogLevelFilter::Error), |
| 2 => Some(LogLevelFilter::Warn), |
| 3 => Some(LogLevelFilter::Info), |
| 4 => Some(LogLevelFilter::Debug), |
| 5 => Some(LogLevelFilter::Trace), |
| _ => None |
| } |
| } |
| |
| fn from_new(filter: log::LevelFilter) -> LogLevelFilter { |
| match filter { |
| log::LevelFilter::Off => LogLevelFilter::Off, |
| log::LevelFilter::Error => LogLevelFilter::Error, |
| log::LevelFilter::Warn => LogLevelFilter::Warn, |
| log::LevelFilter::Info => LogLevelFilter::Info, |
| log::LevelFilter::Debug => LogLevelFilter::Debug, |
| log::LevelFilter::Trace => LogLevelFilter::Trace, |
| } |
| } |
| |
| fn to_new(&self) -> log::LevelFilter { |
| match *self { |
| LogLevelFilter::Off => log::LevelFilter::Off, |
| LogLevelFilter::Error => log::LevelFilter::Error, |
| LogLevelFilter::Warn => log::LevelFilter::Warn, |
| LogLevelFilter::Info => log::LevelFilter::Info, |
| LogLevelFilter::Debug => log::LevelFilter::Debug, |
| LogLevelFilter::Trace => log::LevelFilter::Trace, |
| } |
| } |
| |
| /// Returns the most verbose logging level filter. |
| #[inline] |
| pub fn max() -> LogLevelFilter { |
| LogLevelFilter::Trace |
| } |
| |
| /// Converts `self` to the equivalent `LogLevel`. |
| /// |
| /// Returns `None` if `self` is `LogLevelFilter::Off`. |
| #[inline] |
| pub fn to_log_level(&self) -> Option<LogLevel> { |
| LogLevel::from_usize(*self as usize) |
| } |
| } |
| |
| /// The "payload" of a log message. |
| pub struct LogRecord<'a> { |
| metadata: LogMetadata<'a>, |
| location: &'a LogLocation, |
| args: fmt::Arguments<'a>, |
| } |
| |
| impl<'a> LogRecord<'a> { |
| /// The message body. |
| pub fn args(&self) -> &fmt::Arguments<'a> { |
| &self.args |
| } |
| |
| /// Metadata about the log directive. |
| pub fn metadata(&self) -> &LogMetadata { |
| &self.metadata |
| } |
| |
| /// The location of the log directive. |
| pub fn location(&self) -> &LogLocation { |
| self.location |
| } |
| |
| /// The verbosity level of the message. |
| pub fn level(&self) -> LogLevel { |
| self.metadata.level() |
| } |
| |
| /// The name of the target of the directive. |
| pub fn target(&self) -> &str { |
| self.metadata.target() |
| } |
| } |
| |
| /// Metadata about a log message. |
| pub struct LogMetadata<'a> { |
| level: LogLevel, |
| target: &'a str, |
| } |
| |
| impl<'a> LogMetadata<'a> { |
| /// The verbosity level of the message. |
| pub fn level(&self) -> LogLevel { |
| self.level |
| } |
| |
| /// The name of the target of the directive. |
| pub fn target(&self) -> &str { |
| self.target |
| } |
| } |
| |
| /// 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. |
| fn enabled(&self, metadata: &LogMetadata) -> bool; |
| |
| /// Logs the `LogRecord`. |
| /// |
| /// Note that `enabled` is *not* necessarily called before this method. |
| /// Implementations of `log` should perform all necessary filtering |
| /// internally. |
| fn log(&self, record: &LogRecord); |
| } |
| |
| // Just used as a dummy initial value for LOGGER |
| struct NopLogger; |
| |
| impl Log for NopLogger { |
| fn enabled(&self, _: &LogMetadata) -> bool { false } |
| |
| fn log(&self, _: &LogRecord) {} |
| } |
| |
| /// The location of a log message. |
| /// |
| /// # Warning |
| /// |
| /// The fields of this struct are public so that they may be initialized by the |
| /// `log!` macro. They are subject to change at any time and should never be |
| /// accessed directly. |
| #[derive(Copy, Clone, Debug)] |
| pub struct LogLocation { |
| #[doc(hidden)] |
| pub __module_path: &'static str, |
| #[doc(hidden)] |
| pub __file: &'static str, |
| #[doc(hidden)] |
| pub __line: u32, |
| } |
| |
| impl LogLocation { |
| /// The module path of the message. |
| pub fn module_path(&self) -> &str { |
| self.__module_path |
| } |
| |
| /// The source file containing the message. |
| pub fn file(&self) -> &str { |
| self.__file |
| } |
| |
| /// The line containing the message. |
| pub fn line(&self) -> u32 { |
| self.__line |
| } |
| } |
| |
| /// A token providing read and write access to the global maximum log level |
| /// filter. |
| /// |
| /// The maximum log level is used as an optimization to avoid evaluating log |
| /// messages that will be ignored by the logger. Any message with a level |
| /// higher than the maximum log level filter will be ignored. A logger should |
| /// make sure to keep the maximum log level filter in sync with its current |
| /// configuration. |
| pub struct MaxLogLevelFilter(()); |
| |
| impl fmt::Debug for MaxLogLevelFilter { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| write!(fmt, "MaxLogLevelFilter") |
| } |
| } |
| |
| impl MaxLogLevelFilter { |
| /// Gets the current maximum log level filter. |
| pub fn get(&self) -> LogLevelFilter { |
| max_log_level() |
| } |
| |
| /// Sets the maximum log level. |
| pub fn set(&self, level: LogLevelFilter) { |
| log::set_max_level(level.to_new()) |
| } |
| } |
| |
| /// 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 `MaxLogLevel` token passed to loggers. |
| #[inline(always)] |
| pub fn max_log_level() -> LogLevelFilter { |
| LogLevelFilter::from_new(log::max_level()) |
| } |
| |
| /// Sets the global logger. |
| /// |
| /// The `make_logger` closure is passed a `MaxLogLevel` object, which the |
| /// logger should use to keep the global maximum log level in sync with the |
| /// highest log level that the logger will not ignore. |
| /// |
| /// 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 calls |
| /// `set_logger` internally. |
| /// |
| /// Requires the `use_std` feature (enabled by default). |
| #[cfg(feature = "use_std")] |
| pub fn set_logger<M>(make_logger: M) -> Result<(), SetLoggerError> |
| where M: FnOnce(MaxLogLevelFilter) -> Box<Log> { |
| unsafe { set_logger_raw(|max_level| mem::transmute(make_logger(max_level))) } |
| } |
| |
| /// Sets the global logger from a raw pointer. |
| /// |
| /// This function is similar to `set_logger` except that it is usable in |
| /// `no_std` code. |
| /// |
| /// The `make_logger` closure is passed a `MaxLogLevel` object, which the |
| /// logger should use to keep the global maximum log level in sync with the |
| /// highest log level that the logger will not ignore. |
| /// |
| /// This function may only be called once in the lifetime of a program. Any log |
| /// events that occur before the call to `set_logger_raw` completes will be |
| /// ignored. |
| /// |
| /// This function does not typically need to be called manually. Logger |
| /// implementations should provide an initialization method that calls |
| /// `set_logger_raw` internally. |
| /// |
| /// # Safety |
| /// |
| /// The pointer returned by `make_logger` must remain valid for the entire |
| /// duration of the program or until `shutdown_logger_raw` is called. In |
| /// addition, `shutdown_logger` *must not* be called after this function. |
| pub unsafe fn set_logger_raw<M>(make_logger: M) -> Result<(), SetLoggerError> |
| where M: FnOnce(MaxLogLevelFilter) -> *const Log { |
| static ADAPTOR: LoggerAdaptor = LoggerAdaptor; |
| match log::set_logger(&ADAPTOR) { |
| Ok(()) => { |
| LOGGER = make_logger(MaxLogLevelFilter(())); |
| STATE.store(INITIALIZED, Ordering::SeqCst); |
| Ok(()) |
| } |
| Err(_) => Err(SetLoggerError(())), |
| } |
| } |
| |
| /// Shuts down the global logger. |
| /// |
| /// This function may only be called once in the lifetime of a program, and may |
| /// not be called before `set_logger`. Once the global logger has been shut |
| /// down, it can no longer be re-initialized by `set_logger`. Any log events |
| /// that occur after the call to `shutdown_logger` completes will be ignored. |
| /// |
| /// The logger that was originally created by the call to to `set_logger` is |
| /// returned on success. At that point it is guaranteed that no other threads |
| /// are concurrently accessing the logger object. |
| #[cfg(feature = "use_std")] |
| pub fn shutdown_logger() -> Result<Box<Log>, ShutdownLoggerError> { |
| shutdown_logger_raw().map(|l| unsafe { mem::transmute(l) }) |
| } |
| |
| /// Shuts down the global logger. |
| /// |
| /// This function is similar to `shutdown_logger` except that it is usable in |
| /// `no_std` code. |
| /// |
| /// This function may only be called once in the lifetime of a program, and may |
| /// not be called before `set_logger_raw`. Once the global logger has been shut |
| /// down, it can no longer be re-initialized by `set_logger_raw`. Any log |
| /// events that occur after the call to `shutdown_logger_raw` completes will be |
| /// ignored. |
| /// |
| /// The pointer that was originally passed to `set_logger_raw` is returned on |
| /// success. At that point it is guaranteed that no other threads are |
| /// concurrently accessing the logger object. |
| pub fn shutdown_logger_raw() -> Result<*const Log, ShutdownLoggerError> { |
| // Set to INITIALIZING to prevent re-initialization after |
| if STATE.compare_and_swap(INITIALIZED, INITIALIZING, |
| Ordering::SeqCst) != INITIALIZED { |
| return Err(ShutdownLoggerError(())); |
| } |
| |
| while REFCOUNT.load(Ordering::SeqCst) != 0 { |
| // FIXME add a sleep here when it doesn't involve timers |
| } |
| |
| unsafe { |
| let logger = LOGGER; |
| LOGGER = &NopLogger; |
| Ok(logger) |
| } |
| } |
| |
| /// The type returned by `set_logger` if `set_logger` has already been called. |
| #[allow(missing_copy_implementations)] |
| #[derive(Debug)] |
| pub struct SetLoggerError(()); |
| |
| impl fmt::Display for SetLoggerError { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| write!(fmt, "attempted to set a logger after the logging system \ |
| was already initialized") |
| } |
| } |
| |
| // The Error trait is not available in libcore |
| #[cfg(feature = "use_std")] |
| impl error::Error for SetLoggerError { |
| fn description(&self) -> &str { "set_logger() called multiple times" } |
| } |
| |
| /// The type returned by `shutdown_logger_raw` if `shutdown_logger_raw` has |
| /// already been called or if `set_logger_raw` has not been called yet. |
| #[allow(missing_copy_implementations)] |
| #[derive(Debug)] |
| pub struct ShutdownLoggerError(()); |
| |
| impl fmt::Display for ShutdownLoggerError { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| write!(fmt, "attempted to shut down the logger without an active logger") |
| } |
| } |
| |
| // The Error trait is not available in libcore |
| #[cfg(feature = "use_std")] |
| impl error::Error for ShutdownLoggerError { |
| fn description(&self) -> &str { "shutdown_logger() called without an active logger" } |
| } |
| |
| /// Deprecated |
| /// |
| /// Use https://crates.io/crates/log-panics instead. |
| #[cfg(all(feature = "nightly", feature = "use_std"))] |
| pub fn log_panics() { |
| std::panic::set_hook(Box::new(panic::log)); |
| } |
| |
| // inner module so that the reporting module is log::panic instead of log |
| #[cfg(all(feature = "nightly", feature = "use_std"))] |
| mod panic { |
| use std::panic::PanicInfo; |
| use std::thread; |
| |
| pub fn log(info: &PanicInfo) { |
| let thread = thread::current(); |
| let thread = thread.name().unwrap_or("<unnamed>"); |
| |
| let msg = match info.payload().downcast_ref::<&'static str>() { |
| Some(s) => *s, |
| None => match info.payload().downcast_ref::<String>() { |
| Some(s) => &s[..], |
| None => "Box<Any>", |
| } |
| }; |
| |
| match info.location() { |
| Some(location) => { |
| error!("thread '{}' panicked at '{}': {}:{}", |
| thread, |
| msg, |
| location.file(), |
| location.line()) |
| } |
| None => error!("thread '{}' panicked at '{}'", thread, msg), |
| } |
| } |
| } |
| |
| struct LoggerGuard(&'static Log); |
| |
| impl Drop for LoggerGuard { |
| fn drop(&mut self) { |
| REFCOUNT.fetch_sub(1, Ordering::SeqCst); |
| } |
| } |
| |
| impl Deref for LoggerGuard { |
| type Target = Log; |
| |
| fn deref(&self) -> &(Log + 'static) { |
| self.0 |
| } |
| } |
| |
| fn logger() -> Option<LoggerGuard> { |
| REFCOUNT.fetch_add(1, Ordering::SeqCst); |
| if STATE.load(Ordering::SeqCst) != INITIALIZED { |
| REFCOUNT.fetch_sub(1, Ordering::SeqCst); |
| None |
| } else { |
| Some(LoggerGuard(unsafe { &*LOGGER })) |
| } |
| } |
| |
| struct LoggerAdaptor; |
| |
| impl log::Log for LoggerAdaptor { |
| fn log(&self, record: &log::Record) { |
| if let Some(logger) = logger() { |
| let record = LogRecord { |
| metadata: LogMetadata { |
| level: LogLevel::from_new(record.level()), |
| target: record.target(), |
| }, |
| // file and module path aren't static in 0.4 so we can't forward them. |
| location: &LogLocation { |
| __file: "<unknown>", |
| __line: record.line().unwrap_or(0), |
| __module_path: "<unknown>", |
| }, |
| args: *record.args(), |
| }; |
| logger.log(&record); |
| } |
| } |
| |
| fn enabled(&self, metadata: &log::Metadata) -> bool { |
| match logger() { |
| Some(logger) => { |
| let metadata = LogMetadata { |
| level: LogLevel::from_new(metadata.level()), |
| target: metadata.target(), |
| }; |
| logger.enabled(&metadata) |
| } |
| None => false |
| } |
| } |
| |
| fn flush(&self) {} |
| } |
| |
| // WARNING |
| // This is not considered part of the crate's public API. It is subject to |
| // change at any time. |
| #[doc(hidden)] |
| pub fn __enabled(level: LogLevel, target: &str) -> bool { |
| match logger() { |
| Some(logger) => { |
| let metadata = LogMetadata { |
| level: level, |
| target: target, |
| }; |
| logger.enabled(&metadata) |
| } |
| None => { |
| log::Log::enabled( |
| log::logger(), |
| &log::Metadata::builder() |
| .level(level.to_new()) |
| .target(target) |
| .build() |
| ) |
| } |
| } |
| } |
| |
| // WARNING |
| // This is not considered part of the crate's public API. It is subject to |
| // change at any time. |
| #[doc(hidden)] |
| pub fn __log(level: LogLevel, target: &str, loc: &LogLocation, |
| args: fmt::Arguments) { |
| match logger() { |
| Some(logger) => { |
| let record = LogRecord { |
| metadata: LogMetadata { |
| level: level, |
| target: target, |
| }, |
| location: loc, |
| args: args, |
| }; |
| logger.log(&record); |
| } |
| None => { |
| log::Log::log( |
| log::logger(), |
| &log::Record::builder() |
| .level(level.to_new()) |
| .target(target) |
| .file(Some(loc.__file)) |
| .line(Some(loc.__line)) |
| .module_path(Some(loc.__module_path)) |
| .args(args) |
| .build() |
| ) |
| } |
| } |
| } |
| |
| // WARNING |
| // This is not considered part of the crate's public API. It is subject to |
| // change at any time. |
| #[inline(always)] |
| #[doc(hidden)] |
| pub fn __static_max_level() -> LogLevelFilter { |
| LogLevelFilter::from_new(log::STATIC_MAX_LEVEL) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| extern crate std; |
| use tests::std::string::ToString; |
| use super::{LogLevel, LogLevelFilter}; |
| |
| #[test] |
| fn test_loglevelfilter_from_str() { |
| let tests = [ |
| ("off", Ok(LogLevelFilter::Off)), |
| ("error", Ok(LogLevelFilter::Error)), |
| ("warn", Ok(LogLevelFilter::Warn)), |
| ("info", Ok(LogLevelFilter::Info)), |
| ("debug", Ok(LogLevelFilter::Debug)), |
| ("trace", Ok(LogLevelFilter::Trace)), |
| ("OFF", Ok(LogLevelFilter::Off)), |
| ("ERROR", Ok(LogLevelFilter::Error)), |
| ("WARN", Ok(LogLevelFilter::Warn)), |
| ("INFO", Ok(LogLevelFilter::Info)), |
| ("DEBUG", Ok(LogLevelFilter::Debug)), |
| ("TRACE", Ok(LogLevelFilter::Trace)), |
| ("asdf", Err(())), |
| ]; |
| for &(s, ref expected) in &tests { |
| assert_eq!(expected, &s.parse()); |
| } |
| } |
| |
| #[test] |
| fn test_loglevel_from_str() { |
| let tests = [ |
| ("OFF", Err(())), |
| ("error", Ok(LogLevel::Error)), |
| ("warn", Ok(LogLevel::Warn)), |
| ("info", Ok(LogLevel::Info)), |
| ("debug", Ok(LogLevel::Debug)), |
| ("trace", Ok(LogLevel::Trace)), |
| ("ERROR", Ok(LogLevel::Error)), |
| ("WARN", Ok(LogLevel::Warn)), |
| ("INFO", Ok(LogLevel::Info)), |
| ("DEBUG", Ok(LogLevel::Debug)), |
| ("TRACE", Ok(LogLevel::Trace)), |
| ("asdf", Err(())), |
| ]; |
| for &(s, ref expected) in &tests { |
| assert_eq!(expected, &s.parse()); |
| } |
| } |
| |
| #[test] |
| fn test_loglevel_show() { |
| assert_eq!("INFO", LogLevel::Info.to_string()); |
| assert_eq!("ERROR", LogLevel::Error.to_string()); |
| } |
| |
| #[test] |
| fn test_loglevelfilter_show() { |
| assert_eq!("OFF", LogLevelFilter::Off.to_string()); |
| assert_eq!("ERROR", LogLevelFilter::Error.to_string()); |
| } |
| |
| #[test] |
| fn test_cross_cmp() { |
| assert!(LogLevel::Debug > LogLevelFilter::Error); |
| assert!(LogLevelFilter::Warn < LogLevel::Trace); |
| assert!(LogLevelFilter::Off < LogLevel::Error); |
| } |
| |
| #[test] |
| fn test_cross_eq() { |
| assert!(LogLevel::Error == LogLevelFilter::Error); |
| assert!(LogLevelFilter::Off != LogLevel::Error); |
| assert!(LogLevel::Trace == LogLevelFilter::Trace); |
| } |
| |
| #[test] |
| fn test_to_log_level() { |
| assert_eq!(Some(LogLevel::Error), LogLevelFilter::Error.to_log_level()); |
| assert_eq!(None, LogLevelFilter::Off.to_log_level()); |
| assert_eq!(Some(LogLevel::Debug), LogLevelFilter::Debug.to_log_level()); |
| } |
| |
| #[test] |
| fn test_to_log_level_filter() { |
| assert_eq!(LogLevelFilter::Error, LogLevel::Error.to_log_level_filter()); |
| assert_eq!(LogLevelFilter::Trace, LogLevel::Trace.to_log_level_filter()); |
| } |
| |
| #[test] |
| #[cfg(feature = "use_std")] |
| fn test_error_trait() { |
| use std::error::Error; |
| use super::SetLoggerError; |
| let e = SetLoggerError(()); |
| assert_eq!(e.description(), "set_logger() called multiple times"); |
| } |
| } |