From dc11b85c071dc32267d445bcd367d0fd9d4d3500 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 30 Sep 2017 21:02:37 -0700 Subject: [PATCH] Restructure initialization setup try_set_logger_raw is now set_logger and try_set_logger is now set_boxed_logger. The use_std feature is now disabled by default. The old set_logger has been removed. When we did the crate evaluation for log, we wanted to add more variants of set_logger that e.g. panicked by default, but I'm no longer convinced that's a good idea. There are going to be very few instances of actually calling these methods explicitly, since each logger implementation should be providing their own init method that calls them. Having a huge constellation of functions that all do basically the same thing just makes things really confusing. We also don't want to encourage logger implementations to only provide an init function that panics because a common way of working with logging in tests is to try to init the system in each test and ignore the result. --- .travis.yml | 4 +- Cargo.toml | 1 - appveyor.yml | 1 + src/lib.rs | 168 ++++++++----------------------- tests/filters.rs | 8 +- tests/max_level_features/main.rs | 8 +- 6 files changed, 53 insertions(+), 137 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6bc0aa9..78aa737 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,10 @@ before_script: script: - cargo build --verbose - cargo build --verbose --features serde - - cargo build --verbose --no-default-features + - cargo build --verbose --features use_std - cargo test --verbose - cargo test --verbose --features serde - - cargo test --verbose --no-default-features + - cargo test --verbose --features use_std - cargo run --verbose --manifest-path tests/max_level_features/Cargo.toml - cargo run --verbose --manifest-path tests/max_level_features/Cargo.toml --release after_success: diff --git a/Cargo.toml b/Cargo.toml index ce52d94..10474b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,6 @@ release_max_level_debug = [] release_max_level_trace = [] use_std = [] -default = ["use_std"] [badges] travis-ci = { repository = "rust-lang-nursery/log" } diff --git a/appveyor.yml b/appveyor.yml index b968102..ae92798 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,3 +16,4 @@ build: false test_script: - cargo test --verbose - cargo test --verbose --features serde + - cargo test --verbose --features use_std diff --git a/src/lib.rs b/src/lib.rs index e5d42b2..d3bc80a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,7 +150,7 @@ //! //! ```rust //! # extern crate log; -//! # use log::{Level, LevelFilter, SetLoggerError, Metadata}; +//! # use log::{Level, Metadata}; //! # struct SimpleLogger; //! # impl log::Log for SimpleLogger { //! # fn enabled(&self, _: &Metadata) -> bool { false } @@ -158,22 +158,25 @@ //! # fn flush(&self) {} //! # } //! # fn main() {} -//! # #[cfg(feature = "use_std")] -//! pub fn init() { -//! let filter = LevelFilter::Info; -//! let logger = Box::new(SimpleLogger); -//! log::set_logger(logger, filter); +//! use log::{SetLoggerError, LevelFilter}; +//! +//! static LOGGER: SimpleLogger = SimpleLogger; +//! +//! pub fn init() -> Result<(), SetLoggerError> { +//! log::set_logger(|max_level| { +//! max_level.set(LevelFilter::Info); +//! &LOGGER +//! }) //! } //! ``` //! -//! # Use with `no_std` +//! # Use with `use_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 [`try_set_logger_raw`] -//! function to initialize a logger and the [`shutdown_logger_raw`] function to -//! shut down the global logger before exiting: +//! `set_logger` requires you to provide a `&'static Log`, which can be hard if +//! your logger depends on some runtime configuration. The `set_boxed_logger` +//! function is available with the `use_std` Cargo feature. It is identical to +//! `set_logger` except that it requires you to provide a `Box` rather than +//! a `&'static Log`: //! //! ```rust //! # extern crate log; @@ -185,14 +188,12 @@ //! # fn flush(&self) {} //! # } //! # fn main() {} -//! pub fn try_init() -> Result<(), SetLoggerError> { -//! unsafe { -//! log::try_set_logger_raw(|max_level| { -//! static LOGGER: SimpleLogger = SimpleLogger; -//! max_level.set(LevelFilter::Info); -//! &LOGGER -//! }) -//! } +//! # #[cfg(feature = "use_std")] +//! pub fn init() -> Result<(), SetLoggerError> { +//! log::set_boxed_logger(|max_level| { +//! max_level.set(LevelFilter::Info); +//! Box::new(SimpleLogger) +//! }) //! } //! ``` //! @@ -206,7 +207,7 @@ //! //! ```toml //! [dependencies.log] -//! version = "^0.3.7" +//! version = "0.4" //! features = ["max_level_debug", "release_max_level_warn"] //! ``` //! @@ -972,123 +973,42 @@ pub fn max_level() -> LevelFilter { unsafe { mem::transmute(MAX_LOG_LEVEL_FILTER.load(Ordering::Relaxed)) } } -/// Sets the global logger. +/// Sets the global logger to a `Box`. /// -/// The `make_logger` closure is passed a `MaxLevel` 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 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. /// -/// This function may only be called once in the lifetime of a program. Any log -/// events that occur before the call to `try_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 -/// `try_set_logger` internally. -/// -/// Requires the `use_std` feature (enabled by default). +/// Requires the `use_std` feature. /// /// # Errors /// -/// This function fails to set the global logger if it has already -/// been called before. +/// An error is returned if a logger has already been set. /// -/// # Example -/// -/// Implements a custom logger `ConsoleLogger` which prints to stdout. -/// In order to use the logging macros, `ConsoleLogger` implements -/// the [`Log`] trait and has to be installed via `set_logger`. -/// -/// ```rust -/// # #[macro_use] -/// # extern crate log; -/// # -/// use log::{Record, Level, Metadata, LevelFilter, SetLoggerError}; -/// -/// struct ConsoleLogger; -/// -/// impl log::Log for ConsoleLogger { -/// fn enabled(&self, metadata: &Metadata) -> bool { -/// metadata.level() <= Level::Info -/// } -/// -/// fn log(&self, record: &Record) { -/// if self.enabled(record.metadata()) { -/// println!("Rust says: {} - {}", record.level(), record.args()); -/// } -/// } -/// fn flush(&self) {} -/// } -/// -/// fn try_init() -> Result<(), SetLoggerError> { -/// log::try_set_logger(|max_log_level| { -/// max_log_level.set(LevelFilter::Info); -/// Box::new(ConsoleLogger) -/// }); -/// -/// info!("hello log"); -/// warn!("warning"); -/// error!("oops"); -/// Ok(()) -/// } -/// # -/// # fn main(){} -/// ``` -/// -/// [`Log`]: trait.Log.html +/// [`set_logger`]: fn.set_logger.html #[cfg(feature = "use_std")] -pub fn try_set_logger(make_logger: M) -> Result<(), SetLoggerError> +pub fn set_boxed_logger(make_logger: M) -> Result<(), SetLoggerError> where M: FnOnce(MaxLevelFilter) -> Box { - unsafe { try_set_logger_raw(|max_level| &*Box::into_raw(make_logger(max_level))) } + unsafe { set_logger(|max_level| &*Box::into_raw(make_logger(max_level))) } } -/// Sets the global logger. -/// -/// 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 will panic on future initialization attempts. -/// -/// 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). -/// -/// # Panics -/// -/// The function will panic if it is called more than once. -#[cfg(feature = "use_std")] -pub fn set_logger(logger: Box, filter: LevelFilter) { - match try_set_logger(|max| { max.set(filter); logger }) { - Ok(()) => {} - Err(_) => panic!("global logger is already initialized"), - } -} - -/// Sets the global logger from a raw pointer. -/// -/// This function is similar to [`set_logger`] except that it is usable in -/// `no_std` code. +/// Sets the global logger to a `&'static Log`. /// /// The `make_logger` closure is passed a `MaxLevel` 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 `try_set_logger_raw` completes will be -/// ignored. +/// 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 -/// `try_set_logger_raw` internally. +/// implementations should provide an initialization method that installs the +/// logger internally. /// /// # Errors /// -/// This function fails to set the global logger if [`set_logger`] -/// has already been called. +/// An error is returned if a logger has already been set. /// /// # Examples /// @@ -1098,10 +1018,10 @@ pub fn set_logger(logger: Box, filter: LevelFilter) { /// # /// use log::{Record, Level, Metadata, LevelFilter}; /// -/// struct MyLogger; -/// /// static MY_LOGGER: MyLogger = MyLogger; /// +/// struct MyLogger; +/// /// impl log::Log for MyLogger { /// fn enabled(&self, metadata: &Metadata) -> bool { /// metadata.level() <= Level::Info @@ -1116,21 +1036,17 @@ pub fn set_logger(logger: Box, filter: LevelFilter) { /// } /// /// # fn main(){ -/// log::try_set_logger_raw(|max_log_level| { +/// log::set_logger(|max_log_level| { /// max_log_level.set(LevelFilter::Info); /// &MY_LOGGER -/// }); +/// }).unwrap(); /// /// info!("hello log"); /// warn!("warning"); /// error!("oops"); /// # } /// ``` -/// -/// [`set_logger`]: fn.set_logger.html -/// [`shutdown_logger`]: fn.shutdown_logger.html -/// [`shutdown_logger_raw`]: fn.shutdown_logger_raw.html -pub fn try_set_logger_raw(make_logger: M) -> Result<(), SetLoggerError> +pub fn set_logger(make_logger: M) -> Result<(), SetLoggerError> where M: FnOnce(MaxLevelFilter) -> &'static Log { unsafe { diff --git a/tests/filters.rs b/tests/filters.rs index 4b4d090..d28fa00 100644 --- a/tests/filters.rs +++ b/tests/filters.rs @@ -6,12 +6,12 @@ use log::{Level, LevelFilter, Log, Record, Metadata}; use log::MaxLevelFilter; #[cfg(feature = "use_std")] -use log::try_set_logger; +use log::set_boxed_logger; #[cfg(not(feature = "use_std"))] -fn try_set_logger(make_logger: M) -> Result<(), log::SetLoggerError> +fn set_boxed_logger(make_logger: M) -> Result<(), log::SetLoggerError> where M: FnOnce(MaxLevelFilter) -> Box { - unsafe { log::try_set_logger_raw(|x| std::mem::transmute(make_logger(x))) } + log::set_logger(|x| unsafe { &*Box::into_raw(make_logger(x)) }) } struct State { @@ -34,7 +34,7 @@ impl Log for Logger { fn main() { let mut a = None; - try_set_logger(|max| { + set_boxed_logger(|max| { let me = Arc::new(State { last_log: Mutex::new(None), filter: max, diff --git a/tests/max_level_features/main.rs b/tests/max_level_features/main.rs index 7e1186c..e2c43f9 100644 --- a/tests/max_level_features/main.rs +++ b/tests/max_level_features/main.rs @@ -6,12 +6,12 @@ use log::{Level, LevelFilter, Log, Record, Metadata}; use log::MaxLevelFilter; #[cfg(feature = "use_std")] -use log::try_set_logger; +use log::set_boxed_logger; #[cfg(not(feature = "use_std"))] -fn try_set_logger(make_logger: M) -> Result<(), log::SetLoggerError> +fn set_boxed_logger(make_logger: M) -> Result<(), log::SetLoggerError> where M: FnOnce(MaxLevelFilter) -> Box { unsafe { - log::try_set_logger_raw(|x| std::mem::transmute(make_logger(x))) + log::set_logger(|x| &*Box::into_raw(make_logger(x))) } } @@ -36,7 +36,7 @@ impl Log for Logger { fn main() { let mut a = None; - try_set_logger(|max| { + set_boxed_logger(|max| { let me = Arc::new(State { last_log: Mutex::new(None), filter: max,