1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! A lightweight logging facade.
13 //! A logging facade provides a single logging API that abstracts over the
14 //! actual logging implementation. Libraries can use the logging API provided
15 //! by this crate, and the consumer of those libraries can choose the logging
16 //! framework that is most suitable for its use case.
18 //! If no logging implementation is selected, the facade falls back to a "noop"
19 //! implementation that ignores all log messages. The overhead in this case
20 //! is very small - just an integer load, comparison and jump.
22 //! A log request consists of a target, a level, and a body. A target is a
23 //! string which defaults to the module path of the location of the log
24 //! request, though that default may be overridden. Logger implementations
25 //! typically use the target to filter requests based on some user
32 //! Libraries should link only to the `log` crate, and use the provided
33 //! macros to log whatever information will be useful to downstream consumers.
38 //! # #![allow(unstable)]
42 //! # #[derive(Debug)] pub struct Yak(String);
43 //! # impl Yak { fn shave(&self, _: u32) {} }
44 //! # fn find_a_razor() -> Result<u32, u32> { Ok(1) }
45 //! pub fn shave_the_yak(yak: &Yak) {
46 //! info!(target: "yak_events", "Commencing yak shaving for {:?}", yak);
49 //! match find_a_razor() {
51 //! info!("Razor located: {}", razor);
56 //! warn!("Unable to locate a razor: {}, retrying", err);
66 //! Executables should choose a logging framework and initialize it early in the
67 //! runtime of the program. Logging frameworks will typically include a
68 //! function to do this. Any log messages generated before the framework is
69 //! initialized will be ignored.
71 //! The executable itself may use the `log` crate to log as well.
75 //! The logging system may only be initialized once.
82 //! extern crate my_logger;
85 //! my_logger::init();
87 //! info!("starting up");
93 //! # Logger implementations
95 //! Loggers implement the `Log` trait. Here's a very basic example that simply
96 //! logs all messages at the `Error`, `Warn` or `Info` levels to stdout:
101 //! use log::{LogRecord, LogLevel, LogMetadata};
103 //! struct SimpleLogger;
105 //! impl log::Log for SimpleLogger {
106 //! fn enabled(&self, metadata: &LogMetadata) -> bool {
107 //! metadata.level() <= LogLevel::Info
110 //! fn log(&self, record: &LogRecord) {
111 //! if self.enabled(record.metadata()) {
112 //! println!("{} - {}", record.level(), record.args());
120 //! Loggers are installed by calling the `set_logger` function. It takes a
121 //! closure which is provided a `MaxLogLevel` token and returns a `Log` trait
122 //! object. The `MaxLogLevel` token controls the global maximum log level. The
123 //! logging facade uses this as an optimization to improve performance of log
124 //! messages at levels that are disabled. In the case of our example logger,
125 //! we'll want to set the maximum log level to `Info`, since we ignore any
126 //! `Debug` or `Trace` level log messages. A logging framework should provide a
127 //! function that wraps a call to `set_logger`, handling initialization of the
131 //! # extern crate log;
132 //! # use log::{LogLevel, LogLevelFilter, SetLoggerError, LogMetadata};
133 //! # struct SimpleLogger;
134 //! # impl log::Log for SimpleLogger {
135 //! # fn enabled(&self, _: &LogMetadata) -> bool { false }
136 //! # fn log(&self, _: &log::LogRecord) {}
139 //! # #[cfg(feature = "use_std")]
140 //! pub fn init() -> Result<(), SetLoggerError> {
141 //! log::set_logger(|max_log_level| {
142 //! max_log_level.set(LogLevelFilter::Info);
143 //! Box::new(SimpleLogger)
148 //! # Use with `no_std`
150 //! To use the `log` crate without depending on `libstd`, you need to specify
151 //! `default-features = false` when specifying the dependency in `Cargo.toml`.
152 //! This makes no difference to libraries using `log` since the logging API
153 //! remains the same. However executables will need to use the `set_logger_raw`
154 //! function to initialize a logger and the `shutdown_logger_raw` function to
155 //! shut down the global logger before exiting:
158 //! # extern crate log;
159 //! # use log::{LogLevel, LogLevelFilter, SetLoggerError, ShutdownLoggerError,
161 //! # struct SimpleLogger;
162 //! # impl log::Log for SimpleLogger {
163 //! # fn enabled(&self, _: &LogMetadata) -> bool { false }
164 //! # fn log(&self, _: &log::LogRecord) {}
166 //! # impl SimpleLogger {
167 //! # fn flush(&self) {}
170 //! pub fn init() -> Result<(), SetLoggerError> {
172 //! log::set_logger_raw(|max_log_level| {
173 //! static LOGGER: SimpleLogger = SimpleLogger;
174 //! max_log_level.set(LogLevelFilter::Info);
179 //! pub fn shutdown() -> Result<(), ShutdownLoggerError> {
180 //! log::shutdown_logger_raw().map(|logger| {
181 //! let logger = unsafe { &*(logger as *const SimpleLogger) };
187 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
188 html_favicon_url
= "https://www.rust-lang.org/favicon.ico",
189 html_root_url
= "https://doc.rust-lang.org/log/")]
190 #![warn(missing_docs)]
191 #![cfg_attr(feature = "nightly", feature(panic_handler))]
193 #![cfg_attr(not(feature = "use_std"), no_std)]
195 #[cfg(not(feature = "use_std"))]
196 extern crate core
as std
;
199 #[cfg(feature = "use_std")]
204 use std
::str::FromStr
;
205 use std
::sync
::atomic
::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}
;
209 // The setup here is a bit weird to make shutdown_logger_raw work.
211 // There are four different states that we care about: the logger's
212 // uninitialized, the logger's initializing (set_logger's been called but
213 // LOGGER hasn't actually been set yet), the logger's active, or the logger is
214 // shut down after calling shutdown_logger_raw.
216 // The LOGGER static holds a pointer to the global logger. It is protected by
217 // the STATE static which determines whether LOGGER has been initialized yet.
219 // The shutdown_logger_raw routine needs to make sure that no threads are
220 // actively logging before it returns. The number of actively logging threads is
221 // tracked in the REFCOUNT static. The routine first sets STATE back to
222 // INITIALIZING. All logging calls past that point will immediately return
223 // without accessing the logger. At that point, the at_exit routine just waits
224 // for the refcount to reach 0 before deallocating the logger. Note that the
225 // refcount does not necessarily monotonically decrease at this point, as new
226 // log calls still increment and decrement it, but the interval in between is
227 // small enough that the wait is really just for the active log calls to finish.
229 static mut LOGGER
: *const Log
= &NopLogger
;
230 static STATE
: AtomicUsize
= ATOMIC_USIZE_INIT
;
231 static REFCOUNT
: AtomicUsize
= ATOMIC_USIZE_INIT
;
233 const UNINITIALIZED
: usize = 0;
234 const INITIALIZING
: usize = 1;
235 const INITIALIZED
: usize = 2;
237 static MAX_LOG_LEVEL_FILTER
: AtomicUsize
= ATOMIC_USIZE_INIT
;
239 static LOG_LEVEL_NAMES
: [&'
static str; 6] = ["OFF", "ERROR", "WARN", "INFO",
242 /// An enum representing the available verbosity levels of the logging framework
244 /// A `LogLevel` may be compared directly to a `LogLevelFilter`.
246 #[derive(Copy, Eq, Debug)]
248 /// The "error" level.
250 /// Designates very serious errors.
251 Error
= 1, // This way these line up with the discriminants for LogLevelFilter below
252 /// The "warn" level.
254 /// Designates hazardous situations.
256 /// The "info" level.
258 /// Designates useful information.
260 /// The "debug" level.
262 /// Designates lower priority information.
264 /// The "trace" level.
266 /// Designates very low priority, often extremely verbose, information.
270 impl Clone
for LogLevel
{
272 fn clone(&self) -> LogLevel
{
277 impl PartialEq
for LogLevel
{
279 fn eq(&self, other
: &LogLevel
) -> bool
{
280 *self as usize == *other
as usize
284 impl PartialEq
<LogLevelFilter
> for LogLevel
{
286 fn eq(&self, other
: &LogLevelFilter
) -> bool
{
287 *self as usize == *other
as usize
291 impl PartialOrd
for LogLevel
{
293 fn partial_cmp(&self, other
: &LogLevel
) -> Option
<cmp
::Ordering
> {
294 Some(self.cmp(other
))
298 impl PartialOrd
<LogLevelFilter
> for LogLevel
{
300 fn partial_cmp(&self, other
: &LogLevelFilter
) -> Option
<cmp
::Ordering
> {
301 Some((*self as usize).cmp(&(*other
as usize)))
305 impl Ord
for LogLevel
{
307 fn cmp(&self, other
: &LogLevel
) -> cmp
::Ordering
{
308 (*self as usize).cmp(&(*other
as usize))
312 fn ok_or
<T
, E
>(t
: Option
<T
>, e
: E
) -> Result
<T
, E
> {
319 // Reimplemented here because std::ascii is not available in libcore
320 fn eq_ignore_ascii_case(a
: &str, b
: &str) -> bool
{
321 fn to_ascii_uppercase(c
: u8) -> u8 {
322 if c
>= b'a'
&& c
<= b'z'
{
329 if a
.len() == b
.len() {
332 .all(|(a
, b
)| to_ascii_uppercase(a
) == to_ascii_uppercase(b
))
338 impl FromStr
for LogLevel
{
340 fn from_str(level
: &str) -> Result
<LogLevel
, ()> {
341 ok_or(LOG_LEVEL_NAMES
.iter()
342 .position(|&name
| eq_ignore_ascii_case(name
, level
))
344 .filter(|&idx
| idx
!= 0)
345 .map(|idx
| LogLevel
::from_usize(idx
).unwrap())
350 impl fmt
::Display
for LogLevel
{
351 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
352 fmt
.pad(LOG_LEVEL_NAMES
[*self as usize])
357 fn from_usize(u
: usize) -> Option
<LogLevel
> {
359 1 => Some(LogLevel
::Error
),
360 2 => Some(LogLevel
::Warn
),
361 3 => Some(LogLevel
::Info
),
362 4 => Some(LogLevel
::Debug
),
363 5 => Some(LogLevel
::Trace
),
368 /// Returns the most verbose logging level.
370 pub fn max() -> LogLevel
{
374 /// Converts the `LogLevel` to the equivalent `LogLevelFilter`.
376 pub fn to_log_level_filter(&self) -> LogLevelFilter
{
377 LogLevelFilter
::from_usize(*self as usize).unwrap()
381 /// An enum representing the available verbosity level filters of the logging
384 /// A `LogLevelFilter` may be compared directly to a `LogLevel`.
386 #[derive(Copy, Eq, Debug)]
387 pub enum LogLevelFilter
{
388 /// A level lower than all log levels.
390 /// Corresponds to the `Error` log level.
392 /// Corresponds to the `Warn` log level.
394 /// Corresponds to the `Info` log level.
396 /// Corresponds to the `Debug` log level.
398 /// Corresponds to the `Trace` log level.
402 // Deriving generates terrible impls of these traits
404 impl Clone
for LogLevelFilter
{
406 fn clone(&self) -> LogLevelFilter
{
411 impl PartialEq
for LogLevelFilter
{
413 fn eq(&self, other
: &LogLevelFilter
) -> bool
{
414 *self as usize == *other
as usize
418 impl PartialEq
<LogLevel
> for LogLevelFilter
{
420 fn eq(&self, other
: &LogLevel
) -> bool
{
425 impl PartialOrd
for LogLevelFilter
{
427 fn partial_cmp(&self, other
: &LogLevelFilter
) -> Option
<cmp
::Ordering
> {
428 Some(self.cmp(other
))
432 impl PartialOrd
<LogLevel
> for LogLevelFilter
{
434 fn partial_cmp(&self, other
: &LogLevel
) -> Option
<cmp
::Ordering
> {
435 other
.partial_cmp(self).map(|x
| x
.reverse())
439 impl Ord
for LogLevelFilter
{
441 fn cmp(&self, other
: &LogLevelFilter
) -> cmp
::Ordering
{
442 (*self as usize).cmp(&(*other
as usize))
446 impl FromStr
for LogLevelFilter
{
448 fn from_str(level
: &str) -> Result
<LogLevelFilter
, ()> {
449 ok_or(LOG_LEVEL_NAMES
.iter()
450 .position(|&name
| eq_ignore_ascii_case(name
, level
))
451 .map(|p
| LogLevelFilter
::from_usize(p
).unwrap()), ())
455 impl fmt
::Display
for LogLevelFilter
{
456 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
457 write
!(fmt
, "{}", LOG_LEVEL_NAMES
[*self as usize])
461 impl LogLevelFilter
{
462 fn from_usize(u
: usize) -> Option
<LogLevelFilter
> {
464 0 => Some(LogLevelFilter
::Off
),
465 1 => Some(LogLevelFilter
::Error
),
466 2 => Some(LogLevelFilter
::Warn
),
467 3 => Some(LogLevelFilter
::Info
),
468 4 => Some(LogLevelFilter
::Debug
),
469 5 => Some(LogLevelFilter
::Trace
),
473 /// Returns the most verbose logging level filter.
475 pub fn max() -> LogLevelFilter
{
476 LogLevelFilter
::Trace
479 /// Converts `self` to the equivalent `LogLevel`.
481 /// Returns `None` if `self` is `LogLevelFilter::Off`.
483 pub fn to_log_level(&self) -> Option
<LogLevel
> {
484 LogLevel
::from_usize(*self as usize)
488 /// The "payload" of a log message.
489 pub struct LogRecord
<'a
> {
490 metadata
: LogMetadata
<'a
>,
491 location
: &'a LogLocation
,
492 args
: fmt
::Arguments
<'a
>,
495 impl<'a
> LogRecord
<'a
> {
496 /// The message body.
497 pub fn args(&self) -> &fmt
::Arguments
<'a
> {
501 /// Metadata about the log directive.
502 pub fn metadata(&self) -> &LogMetadata
{
506 /// The location of the log directive.
507 pub fn location(&self) -> &LogLocation
{
511 /// The verbosity level of the message.
512 pub fn level(&self) -> LogLevel
{
513 self.metadata
.level()
516 /// The name of the target of the directive.
517 pub fn target(&self) -> &str {
518 self.metadata
.target()
522 /// Metadata about a log message.
523 pub struct LogMetadata
<'a
> {
528 impl<'a
> LogMetadata
<'a
> {
529 /// The verbosity level of the message.
530 pub fn level(&self) -> LogLevel
{
534 /// The name of the target of the directive.
535 pub fn target(&self) -> &str {
540 /// A trait encapsulating the operations required of a logger
541 pub trait Log
: Sync
+Send
{
542 /// Determines if a log message with the specified metadata would be
545 /// This is used by the `log_enabled!` macro to allow callers to avoid
546 /// expensive computation of log message arguments if the message would be
547 /// discarded anyway.
548 fn enabled(&self, metadata
: &LogMetadata
) -> bool
;
550 /// Logs the `LogRecord`.
552 /// Note that `enabled` is *not* necessarily called before this method.
553 /// Implementations of `log` should perform all necessary filtering
555 fn log(&self, record
: &LogRecord
);
558 // Just used as a dummy initial value for LOGGER
561 impl Log
for NopLogger
{
562 fn enabled(&self, _
: &LogMetadata
) -> bool { false }
564 fn log(&self, _
: &LogRecord
) {}
567 /// The location of a log message.
571 /// The fields of this struct are public so that they may be initialized by the
572 /// `log!` macro. They are subject to change at any time and should never be
573 /// accessed directly.
574 #[derive(Copy, Clone, Debug)]
575 pub struct LogLocation
{
577 pub __module_path
: &'
static str,
579 pub __file
: &'
static str,
585 /// The module path of the message.
586 pub fn module_path(&self) -> &str {
590 /// The source file containing the message.
591 pub fn file(&self) -> &str {
595 /// The line containing the message.
596 pub fn line(&self) -> u32 {
601 /// A token providing read and write access to the global maximum log level
604 /// The maximum log level is used as an optimization to avoid evaluating log
605 /// messages that will be ignored by the logger. Any message with a level
606 /// higher than the maximum log level filter will be ignored. A logger should
607 /// make sure to keep the maximum log level filter in sync with its current
609 #[allow(missing_copy_implementations)]
610 pub struct MaxLogLevelFilter(());
612 impl fmt
::Debug
for MaxLogLevelFilter
{
613 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
614 write
!(fmt
, "MaxLogLevelFilter")
618 impl MaxLogLevelFilter
{
619 /// Gets the current maximum log level filter.
620 pub fn get(&self) -> LogLevelFilter
{
624 /// Sets the maximum log level.
625 pub fn set(&self, level
: LogLevelFilter
) {
626 MAX_LOG_LEVEL_FILTER
.store(level
as usize, Ordering
::SeqCst
)
630 /// Returns the current maximum log level.
632 /// The `log!`, `error!`, `warn!`, `info!`, `debug!`, and `trace!` macros check
633 /// this value and discard any message logged at a higher level. The maximum
634 /// log level is set by the `MaxLogLevel` token passed to loggers.
636 pub fn max_log_level() -> LogLevelFilter
{
637 unsafe { mem::transmute(MAX_LOG_LEVEL_FILTER.load(Ordering::Relaxed)) }
640 /// Sets the global logger.
642 /// The `make_logger` closure is passed a `MaxLogLevel` object, which the
643 /// logger should use to keep the global maximum log level in sync with the
644 /// highest log level that the logger will not ignore.
646 /// This function may only be called once in the lifetime of a program. Any log
647 /// events that occur before the call to `set_logger` completes will be
650 /// This function does not typically need to be called manually. Logger
651 /// implementations should provide an initialization method that calls
652 /// `set_logger` internally.
654 /// Requires the `use_std` feature (enabled by default).
655 #[cfg(feature = "use_std")]
656 pub fn set_logger
<M
>(make_logger
: M
) -> Result
<(), SetLoggerError
>
657 where M
: FnOnce(MaxLogLevelFilter
) -> Box
<Log
> {
658 unsafe { set_logger_raw(|max_level| mem::transmute(make_logger(max_level))) }
661 /// Sets the global logger from a raw pointer.
663 /// This function is similar to `set_logger` except that it is usable in
666 /// The `make_logger` closure is passed a `MaxLogLevel` object, which the
667 /// logger should use to keep the global maximum log level in sync with the
668 /// highest log level that the logger will not ignore.
670 /// This function may only be called once in the lifetime of a program. Any log
671 /// events that occur before the call to `set_logger_raw` completes will be
674 /// This function does not typically need to be called manually. Logger
675 /// implementations should provide an initialization method that calls
676 /// `set_logger_raw` internally.
680 /// The pointer returned by `make_logger` must remain valid for the entire
681 /// duration of the program or until `shutdown_logger_raw` is called. In
682 /// addition, `shutdown_logger` *must not* be called after this function.
683 pub unsafe fn set_logger_raw
<M
>(make_logger
: M
) -> Result
<(), SetLoggerError
>
684 where M
: FnOnce(MaxLogLevelFilter
) -> *const Log
{
685 if STATE
.compare_and_swap(UNINITIALIZED
, INITIALIZING
,
686 Ordering
::SeqCst
) != UNINITIALIZED
{
687 return Err(SetLoggerError(()));
690 LOGGER
= make_logger(MaxLogLevelFilter(()));
691 STATE
.store(INITIALIZED
, Ordering
::SeqCst
);
695 /// Shuts down the global logger.
697 /// This function may only be called once in the lifetime of a program, and may
698 /// not be called before `set_logger`. Once the global logger has been shut
699 /// down, it can no longer be re-initialized by `set_logger`. Any log events
700 /// that occur after the call to `shutdown_logger` completes will be ignored.
702 /// The logger that was originally created by the call to to `set_logger` is
703 /// returned on success. At that point it is guaranteed that no other threads
704 /// are concurrently accessing the logger object.
705 #[cfg(feature = "use_std")]
706 pub fn shutdown_logger() -> Result
<Box
<Log
>, ShutdownLoggerError
> {
707 shutdown_logger_raw().map(|l
| unsafe { mem::transmute(l) }
)
710 /// Shuts down the global logger.
712 /// This function is similar to `shutdown_logger` except that it is usable in
715 /// This function may only be called once in the lifetime of a program, and may
716 /// not be called before `set_logger_raw`. Once the global logger has been shut
717 /// down, it can no longer be re-initialized by `set_logger_raw`. Any log
718 /// events that occur after the call to `shutdown_logger_raw` completes will be
721 /// The pointer that was originally passed to `set_logger_raw` is returned on
722 /// success. At that point it is guaranteed that no other threads are
723 /// concurrently accessing the logger object.
724 pub fn shutdown_logger_raw() -> Result
<*const Log
, ShutdownLoggerError
> {
725 // Set the global log level to stop other thread from logging
726 MAX_LOG_LEVEL_FILTER
.store(0, Ordering
::SeqCst
);
728 // Set to INITIALIZING to prevent re-initialization after
729 if STATE
.compare_and_swap(INITIALIZED
, INITIALIZING
,
730 Ordering
::SeqCst
) != INITIALIZED
{
731 return Err(ShutdownLoggerError(()));
734 while REFCOUNT
.load(Ordering
::SeqCst
) != 0 {
735 // FIXME add a sleep here when it doesn't involve timers
745 /// The type returned by `set_logger` if `set_logger` has already been called.
746 #[allow(missing_copy_implementations)]
748 pub struct SetLoggerError(());
750 impl fmt
::Display
for SetLoggerError
{
751 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
752 write
!(fmt
, "attempted to set a logger after the logging system \
753 was already initialized")
757 // The Error trait is not available in libcore
758 #[cfg(feature = "use_std")]
759 impl error
::Error
for SetLoggerError
{
760 fn description(&self) -> &str { "set_logger() called multiple times" }
763 /// The type returned by `shutdown_logger_raw` if `shutdown_logger_raw` has
764 /// already been called or if `set_logger_raw` has not been called yet.
765 #[allow(missing_copy_implementations)]
767 pub struct ShutdownLoggerError(());
769 impl fmt
::Display
for ShutdownLoggerError
{
770 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
771 write
!(fmt
, "attempted to shut down the logger without an active logger")
775 // The Error trait is not available in libcore
776 #[cfg(feature = "use_std")]
777 impl error
::Error
for ShutdownLoggerError
{
778 fn description(&self) -> &str { "shutdown_logger() called without an active logger" }
781 /// Registers a panic hook which logs at the error level.
783 /// The format is the same as the default panic hook . The reporting module is
786 /// Requires the `use_std` (enabled by default) and `nightly` features.
787 #[cfg(all(feature = "nightly", feature = "use_std"))]
788 pub fn log_panics() {
789 std
::panic
::set_hook(Box
::new(panic
::log
));
792 // inner module so that the reporting module is log::panic instead of log
793 #[cfg(all(feature = "nightly", feature = "use_std"))]
795 use std
::panic
::PanicInfo
;
798 pub fn log(info
: &PanicInfo
) {
799 let thread
= thread
::current();
800 let thread
= thread
.name().unwrap_or("<unnamed>");
802 let msg
= match info
.payload().downcast_ref
::<&'
static str>() {
804 None
=> match info
.payload().downcast_ref
::<String
>() {
810 match info
.location() {
812 error
!("thread '{}' panicked at '{}': {}:{}",
818 None
=> error
!("thread '{}' panicked at '{}'", thread
, msg
),
823 struct LoggerGuard(&'
static Log
);
825 impl Drop
for LoggerGuard
{
827 REFCOUNT
.fetch_sub(1, Ordering
::SeqCst
);
831 impl Deref
for LoggerGuard
{
834 fn deref(&self) -> &(Log
+ '
static) {
839 fn logger() -> Option
<LoggerGuard
> {
840 REFCOUNT
.fetch_add(1, Ordering
::SeqCst
);
841 if STATE
.load(Ordering
::SeqCst
) != INITIALIZED
{
842 REFCOUNT
.fetch_sub(1, Ordering
::SeqCst
);
845 Some(LoggerGuard(unsafe { &*LOGGER }
))
850 // This is not considered part of the crate's public API. It is subject to
851 // change at any time.
853 pub fn __enabled(level
: LogLevel
, target
: &str) -> bool
{
854 if let Some(logger
) = logger() {
855 logger
.enabled(&LogMetadata { level: level, target: target }
)
862 // This is not considered part of the crate's public API. It is subject to
863 // change at any time.
865 pub fn __log(level
: LogLevel
, target
: &str, loc
: &LogLocation
,
866 args
: fmt
::Arguments
) {
867 if let Some(logger
) = logger() {
868 let record
= LogRecord
{
869 metadata
: LogMetadata
{
881 // This is not considered part of the crate's public API. It is subject to
882 // change at any time.
885 pub fn __static_max_level() -> LogLevelFilter
{
886 if !cfg
!(debug_assertions
) {
887 // This is a release build. Check `release_max_level_*` first.
888 if cfg
!(feature
= "release_max_level_off") {
889 return LogLevelFilter
::Off
890 } else if cfg
!(feature
= "release_max_level_error") {
891 return LogLevelFilter
::Error
892 } else if cfg
!(feature
= "release_max_level_warn") {
893 return LogLevelFilter
::Warn
894 } else if cfg
!(feature
= "release_max_level_info") {
895 return LogLevelFilter
::Info
896 } else if cfg
!(feature
= "release_max_level_debug") {
897 return LogLevelFilter
::Debug
898 } else if cfg
!(feature
= "release_max_level_trace") {
899 return LogLevelFilter
::Trace
902 if cfg
!(feature
= "max_level_off") {
904 } else if cfg
!(feature
= "max_level_error") {
905 LogLevelFilter
::Error
906 } else if cfg
!(feature
= "max_level_warn") {
908 } else if cfg
!(feature
= "max_level_info") {
910 } else if cfg
!(feature
= "max_level_debug") {
911 LogLevelFilter
::Debug
913 LogLevelFilter
::Trace
920 use tests
::std
::string
::ToString
;
921 use super::{LogLevel, LogLevelFilter}
;
924 fn test_loglevelfilter_from_str() {
926 ("off", Ok(LogLevelFilter
::Off
)),
927 ("error", Ok(LogLevelFilter
::Error
)),
928 ("warn", Ok(LogLevelFilter
::Warn
)),
929 ("info", Ok(LogLevelFilter
::Info
)),
930 ("debug", Ok(LogLevelFilter
::Debug
)),
931 ("trace", Ok(LogLevelFilter
::Trace
)),
932 ("OFF", Ok(LogLevelFilter
::Off
)),
933 ("ERROR", Ok(LogLevelFilter
::Error
)),
934 ("WARN", Ok(LogLevelFilter
::Warn
)),
935 ("INFO", Ok(LogLevelFilter
::Info
)),
936 ("DEBUG", Ok(LogLevelFilter
::Debug
)),
937 ("TRACE", Ok(LogLevelFilter
::Trace
)),
940 for &(s
, ref expected
) in &tests
{
941 assert_eq
!(expected
, &s
.parse());
946 fn test_loglevel_from_str() {
949 ("error", Ok(LogLevel
::Error
)),
950 ("warn", Ok(LogLevel
::Warn
)),
951 ("info", Ok(LogLevel
::Info
)),
952 ("debug", Ok(LogLevel
::Debug
)),
953 ("trace", Ok(LogLevel
::Trace
)),
954 ("ERROR", Ok(LogLevel
::Error
)),
955 ("WARN", Ok(LogLevel
::Warn
)),
956 ("INFO", Ok(LogLevel
::Info
)),
957 ("DEBUG", Ok(LogLevel
::Debug
)),
958 ("TRACE", Ok(LogLevel
::Trace
)),
961 for &(s
, ref expected
) in &tests
{
962 assert_eq
!(expected
, &s
.parse());
967 fn test_loglevel_show() {
968 assert_eq
!("INFO", LogLevel
::Info
.to_string());
969 assert_eq
!("ERROR", LogLevel
::Error
.to_string());
973 fn test_loglevelfilter_show() {
974 assert_eq
!("OFF", LogLevelFilter
::Off
.to_string());
975 assert_eq
!("ERROR", LogLevelFilter
::Error
.to_string());
979 fn test_cross_cmp() {
980 assert
!(LogLevel
::Debug
> LogLevelFilter
::Error
);
981 assert
!(LogLevelFilter
::Warn
< LogLevel
::Trace
);
982 assert
!(LogLevelFilter
::Off
< LogLevel
::Error
);
987 assert
!(LogLevel
::Error
== LogLevelFilter
::Error
);
988 assert
!(LogLevelFilter
::Off
!= LogLevel
::Error
);
989 assert
!(LogLevel
::Trace
== LogLevelFilter
::Trace
);
993 fn test_to_log_level() {
994 assert_eq
!(Some(LogLevel
::Error
), LogLevelFilter
::Error
.to_log_level());
995 assert_eq
!(None
, LogLevelFilter
::Off
.to_log_level());
996 assert_eq
!(Some(LogLevel
::Debug
), LogLevelFilter
::Debug
.to_log_level());
1000 fn test_to_log_level_filter() {
1001 assert_eq
!(LogLevelFilter
::Error
, LogLevel
::Error
.to_log_level_filter());
1002 assert_eq
!(LogLevelFilter
::Trace
, LogLevel
::Trace
.to_log_level_filter());
1006 #[cfg(feature = "use_std")]
1007 fn test_error_trait() {
1008 use std
::error
::Error
;
1009 use super::SetLoggerError
;
1010 let e
= SetLoggerError(());
1011 assert_eq
!(e
.description(), "set_logger() called multiple times");