blob: 1abdf31572d2907f95a03abc2488af1eda65b77a [file] [log] [blame]
// 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");
}
}