From 6bad99b84f18678fe7517c615d4e1c8bd607517b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 10 Nov 2023 09:31:14 -0600 Subject: [PATCH] refactor: Move impls next to types --- src/filter/mod.rs | 152 +-- .../extern_impl.rs => humantime.rs} | 4 - src/fmt/humantime/mod.rs | 11 - src/fmt/humantime/shim_impl.rs | 5 - src/fmt/mod.rs | 34 +- src/lib.rs | 1021 +---------------- src/logger.rs | 1016 ++++++++++++++++ 7 files changed, 1112 insertions(+), 1131 deletions(-) rename src/fmt/{humantime/extern_impl.rs => humantime.rs} (98%) delete mode 100644 src/fmt/humantime/mod.rs delete mode 100644 src/fmt/humantime/shim_impl.rs create mode 100644 src/logger.rs diff --git a/src/filter/mod.rs b/src/filter/mod.rs index 81bb017a..1e647236 100644 --- a/src/filter/mod.rs +++ b/src/filter/mod.rs @@ -71,18 +71,6 @@ mod inner; #[path = "string.rs"] mod inner; -/// A log filter. -/// -/// This struct can be used to determine whether or not a log record -/// should be written to the output. -/// Use the [`Builder`] type to parse and construct a `Filter`. -/// -/// [`Builder`]: struct.Builder.html -pub struct Filter { - directives: Vec, - filter: Option, -} - /// A builder for a log filter. /// /// It can be used to parse a set of directives from a string before building @@ -112,61 +100,6 @@ pub struct Builder { built: bool, } -#[derive(Debug)] -struct Directive { - name: Option, - level: LevelFilter, -} - -impl Filter { - /// Returns the maximum `LevelFilter` that this filter instance is - /// configured to output. - /// - /// # Example - /// - /// ```rust - /// use log::LevelFilter; - /// use env_logger::filter::Builder; - /// - /// let mut builder = Builder::new(); - /// builder.filter(Some("module1"), LevelFilter::Info); - /// builder.filter(Some("module2"), LevelFilter::Error); - /// - /// let filter = builder.build(); - /// assert_eq!(filter.filter(), LevelFilter::Info); - /// ``` - pub fn filter(&self) -> LevelFilter { - self.directives - .iter() - .map(|d| d.level) - .max() - .unwrap_or(LevelFilter::Off) - } - - /// Checks if this record matches the configured filter. - pub fn matches(&self, record: &Record) -> bool { - if !self.enabled(record.metadata()) { - return false; - } - - if let Some(filter) = self.filter.as_ref() { - if !filter.is_match(&record.args().to_string()) { - return false; - } - } - - true - } - - /// Determines if a log message with the specified metadata would be logged. - pub fn enabled(&self, metadata: &Metadata) -> bool { - let level = metadata.level(); - let target = metadata.target(); - - enabled(&self.directives, level, target) - } -} - impl Builder { /// Initializes the filter builder with defaults. pub fn new() -> Builder { @@ -276,15 +209,6 @@ impl Default for Builder { } } -impl fmt::Debug for Filter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Filter") - .field("filter", &self.filter) - .field("directives", &self.directives) - .finish() - } -} - impl fmt::Debug for Builder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.built { @@ -298,6 +222,82 @@ impl fmt::Debug for Builder { } } +#[derive(Debug)] +struct Directive { + name: Option, + level: LevelFilter, +} + +/// A log filter. +/// +/// This struct can be used to determine whether or not a log record +/// should be written to the output. +/// Use the [`Builder`] type to parse and construct a `Filter`. +/// +/// [`Builder`]: struct.Builder.html +pub struct Filter { + directives: Vec, + filter: Option, +} + +impl Filter { + /// Returns the maximum `LevelFilter` that this filter instance is + /// configured to output. + /// + /// # Example + /// + /// ```rust + /// use log::LevelFilter; + /// use env_logger::filter::Builder; + /// + /// let mut builder = Builder::new(); + /// builder.filter(Some("module1"), LevelFilter::Info); + /// builder.filter(Some("module2"), LevelFilter::Error); + /// + /// let filter = builder.build(); + /// assert_eq!(filter.filter(), LevelFilter::Info); + /// ``` + pub fn filter(&self) -> LevelFilter { + self.directives + .iter() + .map(|d| d.level) + .max() + .unwrap_or(LevelFilter::Off) + } + + /// Checks if this record matches the configured filter. + pub fn matches(&self, record: &Record) -> bool { + if !self.enabled(record.metadata()) { + return false; + } + + if let Some(filter) = self.filter.as_ref() { + if !filter.is_match(&record.args().to_string()) { + return false; + } + } + + true + } + + /// Determines if a log message with the specified metadata would be logged. + pub fn enabled(&self, metadata: &Metadata) -> bool { + let level = metadata.level(); + let target = metadata.target(); + + enabled(&self.directives, level, target) + } +} + +impl fmt::Debug for Filter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Filter") + .field("filter", &self.filter) + .field("directives", &self.directives) + .finish() + } +} + /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo") /// and return a vector with log directives. fn parse_spec(spec: &str) -> (Vec, Option) { diff --git a/src/fmt/humantime/extern_impl.rs b/src/fmt/humantime.rs similarity index 98% rename from src/fmt/humantime/extern_impl.rs rename to src/fmt/humantime.rs index bdf165c4..582a6318 100644 --- a/src/fmt/humantime/extern_impl.rs +++ b/src/fmt/humantime.rs @@ -7,10 +7,6 @@ use humantime::{ use crate::fmt::{Formatter, TimestampPrecision}; -pub(in crate::fmt) mod glob { - pub use super::*; -} - impl Formatter { /// Get a [`Timestamp`] for the current date and time in UTC. /// diff --git a/src/fmt/humantime/mod.rs b/src/fmt/humantime/mod.rs deleted file mode 100644 index ac23ae24..00000000 --- a/src/fmt/humantime/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -/* -This internal module contains the timestamp implementation. - -Its public API is available when the `humantime` crate is available. -*/ - -#[cfg_attr(feature = "humantime", path = "extern_impl.rs")] -#[cfg_attr(not(feature = "humantime"), path = "shim_impl.rs")] -mod imp; - -pub(in crate::fmt) use self::imp::*; diff --git a/src/fmt/humantime/shim_impl.rs b/src/fmt/humantime/shim_impl.rs deleted file mode 100644 index 906bf9e4..00000000 --- a/src/fmt/humantime/shim_impl.rs +++ /dev/null @@ -1,5 +0,0 @@ -/* -Timestamps aren't available when we don't have a `humantime` dependency. -*/ - -pub(in crate::fmt) mod glob {} diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 86c093f0..ded40278 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -37,10 +37,12 @@ use std::{fmt, io, mem}; use log::Record; +#[cfg(feature = "humantime")] mod humantime; pub(crate) mod writer; -pub use self::humantime::glob::*; +#[cfg(feature = "humantime")] +pub use self::humantime::Timestamp; pub use self::writer::glob::*; use self::writer::{Buffer, Writer}; @@ -149,21 +151,6 @@ pub(crate) struct Builder { built: bool, } -impl Default for Builder { - fn default() -> Self { - Builder { - format_timestamp: Some(Default::default()), - format_module_path: false, - format_target: true, - format_level: true, - format_indent: Some(4), - custom_format: None, - format_suffix: "\n", - built: false, - } - } -} - impl Builder { /// Convert the format into a callable function. /// @@ -202,6 +189,21 @@ impl Builder { } } +impl Default for Builder { + fn default() -> Self { + Builder { + format_timestamp: Some(Default::default()), + format_module_path: false, + format_target: true, + format_level: true, + format_indent: Some(4), + custom_format: None, + format_suffix: "\n", + built: false, + } + } +} + #[cfg(feature = "color")] type SubtleStyle = StyledValue<'static, &'static str>; #[cfg(not(feature = "color"))] diff --git a/src/lib.rs b/src/lib.rs index 4e5d5428..aff50098 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -275,1027 +275,10 @@ #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] #![deny(missing_debug_implementations, missing_docs)] -use std::{borrow::Cow, cell::RefCell, env, io}; - -use log::{LevelFilter, Log, Metadata, Record, SetLoggerError}; +mod logger; pub mod filter; pub mod fmt; pub use self::fmt::glob::*; - -use self::filter::Filter; -use self::fmt::writer::{self, Writer}; -use self::fmt::{FormatFn, Formatter}; - -/// The default name for the environment variable to read filters from. -pub const DEFAULT_FILTER_ENV: &str = "RUST_LOG"; - -/// The default name for the environment variable to read style preferences from. -pub const DEFAULT_WRITE_STYLE_ENV: &str = "RUST_LOG_STYLE"; - -/// Set of environment variables to configure from. -/// -/// # Default environment variables -/// -/// By default, the `Env` will read the following environment variables: -/// -/// - `RUST_LOG`: the level filter -/// - `RUST_LOG_STYLE`: whether or not to print styles with records. -/// -/// These sources can be configured using the builder methods on `Env`. -#[derive(Debug)] -pub struct Env<'a> { - filter: Var<'a>, - write_style: Var<'a>, -} - -#[derive(Debug)] -struct Var<'a> { - name: Cow<'a, str>, - default: Option>, -} - -/// The env logger. -/// -/// This struct implements the `Log` trait from the [`log` crate][log-crate-url], -/// which allows it to act as a logger. -/// -/// The [`init()`], [`try_init()`], [`Builder::init()`] and [`Builder::try_init()`] -/// methods will each construct a `Logger` and immediately initialize it as the -/// default global logger. -/// -/// If you'd instead need access to the constructed `Logger`, you can use -/// the associated [`Builder`] and install it with the -/// [`log` crate][log-crate-url] directly. -/// -/// [log-crate-url]: https://docs.rs/log -/// [`init()`]: fn.init.html -/// [`try_init()`]: fn.try_init.html -/// [`Builder::init()`]: struct.Builder.html#method.init -/// [`Builder::try_init()`]: struct.Builder.html#method.try_init -/// [`Builder`]: struct.Builder.html -pub struct Logger { - writer: Writer, - filter: Filter, - format: FormatFn, -} - -/// `Builder` acts as builder for initializing a `Logger`. -/// -/// It can be used to customize the log format, change the environment variable used -/// to provide the logging directives and also set the default log level filter. -/// -/// # Examples -/// -/// ``` -/// # use std::io::Write; -/// use env_logger::Builder; -/// use log::{LevelFilter, error, info}; -/// -/// let mut builder = Builder::from_default_env(); -/// -/// builder -/// .format(|buf, record| writeln!(buf, "{} - {}", record.level(), record.args())) -/// .filter(None, LevelFilter::Info) -/// .init(); -/// -/// error!("error message"); -/// info!("info message"); -/// ``` -#[derive(Default)] -pub struct Builder { - filter: filter::Builder, - writer: writer::Builder, - format: fmt::Builder, - built: bool, -} - -impl Builder { - /// Initializes the log builder with defaults. - /// - /// **NOTE:** This method won't read from any environment variables. - /// Use the [`filter`] and [`write_style`] methods to configure the builder - /// or use [`from_env`] or [`from_default_env`] instead. - /// - /// # Examples - /// - /// Create a new builder and configure filters and style: - /// - /// ``` - /// use log::LevelFilter; - /// use env_logger::{Builder, WriteStyle}; - /// - /// let mut builder = Builder::new(); - /// - /// builder - /// .filter(None, LevelFilter::Info) - /// .write_style(WriteStyle::Always) - /// .init(); - /// ``` - /// - /// [`filter`]: #method.filter - /// [`write_style`]: #method.write_style - /// [`from_env`]: #method.from_env - /// [`from_default_env`]: #method.from_default_env - pub fn new() -> Builder { - Default::default() - } - - /// Initializes the log builder from the environment. - /// - /// The variables used to read configuration from can be tweaked before - /// passing in. - /// - /// # Examples - /// - /// Initialise a logger reading the log filter from an environment variable - /// called `MY_LOG`: - /// - /// ``` - /// use env_logger::Builder; - /// - /// let mut builder = Builder::from_env("MY_LOG"); - /// builder.init(); - /// ``` - /// - /// Initialise a logger using the `MY_LOG` variable for filtering and - /// `MY_LOG_STYLE` for whether or not to write styles: - /// - /// ``` - /// use env_logger::{Builder, Env}; - /// - /// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); - /// - /// let mut builder = Builder::from_env(env); - /// builder.init(); - /// ``` - pub fn from_env<'a, E>(env: E) -> Self - where - E: Into>, - { - let mut builder = Builder::new(); - builder.parse_env(env); - builder - } - - /// Applies the configuration from the environment. - /// - /// This function allows a builder to be configured with default parameters, - /// to be then overridden by the environment. - /// - /// # Examples - /// - /// Initialise a logger with filter level `Off`, then override the log - /// filter from an environment variable called `MY_LOG`: - /// - /// ``` - /// use log::LevelFilter; - /// use env_logger::Builder; - /// - /// let mut builder = Builder::new(); - /// - /// builder.filter_level(LevelFilter::Off); - /// builder.parse_env("MY_LOG"); - /// builder.init(); - /// ``` - /// - /// Initialise a logger with filter level `Off`, then use the `MY_LOG` - /// variable to override filtering and `MY_LOG_STYLE` to override whether - /// or not to write styles: - /// - /// ``` - /// use log::LevelFilter; - /// use env_logger::{Builder, Env}; - /// - /// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); - /// - /// let mut builder = Builder::new(); - /// builder.filter_level(LevelFilter::Off); - /// builder.parse_env(env); - /// builder.init(); - /// ``` - pub fn parse_env<'a, E>(&mut self, env: E) -> &mut Self - where - E: Into>, - { - let env = env.into(); - - if let Some(s) = env.get_filter() { - self.parse_filters(&s); - } - - if let Some(s) = env.get_write_style() { - self.parse_write_style(&s); - } - - self - } - - /// Initializes the log builder from the environment using default variable names. - /// - /// This method is a convenient way to call `from_env(Env::default())` without - /// having to use the `Env` type explicitly. The builder will use the - /// [default environment variables]. - /// - /// # Examples - /// - /// Initialise a logger using the default environment variables: - /// - /// ``` - /// use env_logger::Builder; - /// - /// let mut builder = Builder::from_default_env(); - /// builder.init(); - /// ``` - /// - /// [default environment variables]: struct.Env.html#default-environment-variables - pub fn from_default_env() -> Self { - Self::from_env(Env::default()) - } - - /// Applies the configuration from the environment using default variable names. - /// - /// This method is a convenient way to call `parse_env(Env::default())` without - /// having to use the `Env` type explicitly. The builder will use the - /// [default environment variables]. - /// - /// # Examples - /// - /// Initialise a logger with filter level `Off`, then configure it using the - /// default environment variables: - /// - /// ``` - /// use log::LevelFilter; - /// use env_logger::Builder; - /// - /// let mut builder = Builder::new(); - /// builder.filter_level(LevelFilter::Off); - /// builder.parse_default_env(); - /// builder.init(); - /// ``` - /// - /// [default environment variables]: struct.Env.html#default-environment-variables - pub fn parse_default_env(&mut self) -> &mut Self { - self.parse_env(Env::default()) - } - - /// Sets the format function for formatting the log output. - /// - /// This function is called on each record logged and should format the - /// log record and output it to the given [`Formatter`]. - /// - /// The format function is expected to output the string directly to the - /// `Formatter` so that implementations can use the [`std::fmt`] macros - /// to format and output without intermediate heap allocations. The default - /// `env_logger` formatter takes advantage of this. - /// - /// # Examples - /// - /// Use a custom format to write only the log message: - /// - /// ``` - /// use std::io::Write; - /// use env_logger::Builder; - /// - /// let mut builder = Builder::new(); - /// - /// builder.format(|buf, record| writeln!(buf, "{}", record.args())); - /// ``` - /// - /// [`Formatter`]: fmt/struct.Formatter.html - /// [`String`]: https://doc.rust-lang.org/stable/std/string/struct.String.html - /// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html - pub fn format(&mut self, format: F) -> &mut Self - where - F: Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send, - { - self.format.custom_format = Some(Box::new(format)); - self - } - - /// Use the default format. - /// - /// This method will clear any custom format set on the builder. - pub fn default_format(&mut self) -> &mut Self { - self.format = Default::default(); - self - } - - /// Whether or not to write the level in the default format. - pub fn format_level(&mut self, write: bool) -> &mut Self { - self.format.format_level = write; - self - } - - /// Whether or not to write the module path in the default format. - pub fn format_module_path(&mut self, write: bool) -> &mut Self { - self.format.format_module_path = write; - self - } - - /// Whether or not to write the target in the default format. - pub fn format_target(&mut self, write: bool) -> &mut Self { - self.format.format_target = write; - self - } - - /// Configures the amount of spaces to use to indent multiline log records. - /// A value of `None` disables any kind of indentation. - pub fn format_indent(&mut self, indent: Option) -> &mut Self { - self.format.format_indent = indent; - self - } - - /// Configures if timestamp should be included and in what precision. - pub fn format_timestamp(&mut self, timestamp: Option) -> &mut Self { - self.format.format_timestamp = timestamp; - self - } - - /// Configures the timestamp to use second precision. - pub fn format_timestamp_secs(&mut self) -> &mut Self { - self.format_timestamp(Some(fmt::TimestampPrecision::Seconds)) - } - - /// Configures the timestamp to use millisecond precision. - pub fn format_timestamp_millis(&mut self) -> &mut Self { - self.format_timestamp(Some(fmt::TimestampPrecision::Millis)) - } - - /// Configures the timestamp to use microsecond precision. - pub fn format_timestamp_micros(&mut self) -> &mut Self { - self.format_timestamp(Some(fmt::TimestampPrecision::Micros)) - } - - /// Configures the timestamp to use nanosecond precision. - pub fn format_timestamp_nanos(&mut self) -> &mut Self { - self.format_timestamp(Some(fmt::TimestampPrecision::Nanos)) - } - - /// Configures the end of line suffix. - pub fn format_suffix(&mut self, suffix: &'static str) -> &mut Self { - self.format.format_suffix = suffix; - self - } - - /// Adds a directive to the filter for a specific module. - /// - /// # Examples - /// - /// Only include messages for info and above for logs in `path::to::module`: - /// - /// ``` - /// use env_logger::Builder; - /// use log::LevelFilter; - /// - /// let mut builder = Builder::new(); - /// - /// builder.filter_module("path::to::module", LevelFilter::Info); - /// ``` - pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self { - self.filter.filter_module(module, level); - self - } - - /// Adds a directive to the filter for all modules. - /// - /// # Examples - /// - /// Only include messages for info and above for logs globally: - /// - /// ``` - /// use env_logger::Builder; - /// use log::LevelFilter; - /// - /// let mut builder = Builder::new(); - /// - /// builder.filter_level(LevelFilter::Info); - /// ``` - pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self { - self.filter.filter_level(level); - self - } - - /// Adds filters to the logger. - /// - /// The given module (if any) will log at most the specified level provided. - /// If no module is provided then the filter will apply to all log messages. - /// - /// # Examples - /// - /// Only include messages for info and above for logs in `path::to::module`: - /// - /// ``` - /// use env_logger::Builder; - /// use log::LevelFilter; - /// - /// let mut builder = Builder::new(); - /// - /// builder.filter(Some("path::to::module"), LevelFilter::Info); - /// ``` - pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self { - self.filter.filter(module, level); - self - } - - /// Parses the directives string in the same form as the `RUST_LOG` - /// environment variable. - /// - /// See the module documentation for more details. - pub fn parse_filters(&mut self, filters: &str) -> &mut Self { - self.filter.parse(filters); - self - } - - /// Sets the target for the log output. - /// - /// Env logger can log to either stdout, stderr or a custom pipe. The default is stderr. - /// - /// The custom pipe can be used to send the log messages to a custom sink (for example a file). - /// Do note that direct writes to a file can become a bottleneck due to IO operation times. - /// - /// # Examples - /// - /// Write log message to `stdout`: - /// - /// ``` - /// use env_logger::{Builder, Target}; - /// - /// let mut builder = Builder::new(); - /// - /// builder.target(Target::Stdout); - /// ``` - pub fn target(&mut self, target: fmt::Target) -> &mut Self { - self.writer.target(target); - self - } - - /// Sets whether or not styles will be written. - /// - /// This can be useful in environments that don't support control characters - /// for setting colors. - /// - /// # Examples - /// - /// Never attempt to write styles: - /// - /// ``` - /// use env_logger::{Builder, WriteStyle}; - /// - /// let mut builder = Builder::new(); - /// - /// builder.write_style(WriteStyle::Never); - /// ``` - pub fn write_style(&mut self, write_style: fmt::WriteStyle) -> &mut Self { - self.writer.write_style(write_style); - self - } - - /// Parses whether or not to write styles in the same form as the `RUST_LOG_STYLE` - /// environment variable. - /// - /// See the module documentation for more details. - pub fn parse_write_style(&mut self, write_style: &str) -> &mut Self { - self.writer.parse_write_style(write_style); - self - } - - /// Sets whether or not the logger will be used in unit tests. - /// - /// If `is_test` is `true` then the logger will allow the testing framework to - /// capture log records rather than printing them to the terminal directly. - pub fn is_test(&mut self, is_test: bool) -> &mut Self { - self.writer.is_test(is_test); - self - } - - /// Initializes the global logger with the built env logger. - /// - /// This should be called early in the execution of a Rust program. Any log - /// events that occur before initialization will be ignored. - /// - /// # Errors - /// - /// This function will fail if it is called more than once, or if another - /// library has already initialized a global logger. - pub fn try_init(&mut self) -> Result<(), SetLoggerError> { - let logger = self.build(); - - let max_level = logger.filter(); - let r = log::set_boxed_logger(Box::new(logger)); - - if r.is_ok() { - log::set_max_level(max_level); - } - - r - } - - /// Initializes the global logger with the built env logger. - /// - /// This should be called early in the execution of a Rust program. Any log - /// events that occur before initialization will be ignored. - /// - /// # Panics - /// - /// This function will panic if it is called more than once, or if another - /// library has already initialized a global logger. - pub fn init(&mut self) { - self.try_init() - .expect("Builder::init should not be called after logger initialized"); - } - - /// Build an env logger. - /// - /// The returned logger implements the `Log` trait and can be installed manually - /// or nested within another logger. - pub fn build(&mut self) -> Logger { - assert!(!self.built, "attempt to re-use consumed builder"); - self.built = true; - - Logger { - writer: self.writer.build(), - filter: self.filter.build(), - format: self.format.build(), - } - } -} - -impl Logger { - /// Creates the logger from the environment. - /// - /// The variables used to read configuration from can be tweaked before - /// passing in. - /// - /// # Examples - /// - /// Create a logger reading the log filter from an environment variable - /// called `MY_LOG`: - /// - /// ``` - /// use env_logger::Logger; - /// - /// let logger = Logger::from_env("MY_LOG"); - /// ``` - /// - /// Create a logger using the `MY_LOG` variable for filtering and - /// `MY_LOG_STYLE` for whether or not to write styles: - /// - /// ``` - /// use env_logger::{Logger, Env}; - /// - /// let env = Env::new().filter_or("MY_LOG", "info").write_style_or("MY_LOG_STYLE", "always"); - /// - /// let logger = Logger::from_env(env); - /// ``` - pub fn from_env<'a, E>(env: E) -> Self - where - E: Into>, - { - Builder::from_env(env).build() - } - - /// Creates the logger from the environment using default variable names. - /// - /// This method is a convenient way to call `from_env(Env::default())` without - /// having to use the `Env` type explicitly. The logger will use the - /// [default environment variables]. - /// - /// # Examples - /// - /// Creates a logger using the default environment variables: - /// - /// ``` - /// use env_logger::Logger; - /// - /// let logger = Logger::from_default_env(); - /// ``` - /// - /// [default environment variables]: struct.Env.html#default-environment-variables - pub fn from_default_env() -> Self { - Builder::from_default_env().build() - } - - /// Returns the maximum `LevelFilter` that this env logger instance is - /// configured to output. - pub fn filter(&self) -> LevelFilter { - self.filter.filter() - } - - /// Checks if this record matches the configured filter. - pub fn matches(&self, record: &Record) -> bool { - self.filter.matches(record) - } -} - -impl Log for Logger { - fn enabled(&self, metadata: &Metadata) -> bool { - self.filter.enabled(metadata) - } - - fn log(&self, record: &Record) { - if self.matches(record) { - // Log records are written to a thread-local buffer before being printed - // to the terminal. We clear these buffers afterwards, but they aren't shrunk - // so will always at least have capacity for the largest log record formatted - // on that thread. - // - // If multiple `Logger`s are used by the same threads then the thread-local - // formatter might have different color support. If this is the case the - // formatter and its buffer are discarded and recreated. - - thread_local! { - static FORMATTER: RefCell> = RefCell::new(None); - } - - let print = |formatter: &mut Formatter, record: &Record| { - let _ = - (self.format)(formatter, record).and_then(|_| formatter.print(&self.writer)); - - // Always clear the buffer afterwards - formatter.clear(); - }; - - let printed = FORMATTER - .try_with(|tl_buf| { - match tl_buf.try_borrow_mut() { - // There are no active borrows of the buffer - Ok(mut tl_buf) => match *tl_buf { - // We have a previously set formatter - Some(ref mut formatter) => { - // Check the buffer style. If it's different from the logger's - // style then drop the buffer and recreate it. - if formatter.write_style() != self.writer.write_style() { - *formatter = Formatter::new(&self.writer); - } - - print(formatter, record); - } - // We don't have a previously set formatter - None => { - let mut formatter = Formatter::new(&self.writer); - print(&mut formatter, record); - - *tl_buf = Some(formatter); - } - }, - // There's already an active borrow of the buffer (due to re-entrancy) - Err(_) => { - print(&mut Formatter::new(&self.writer), record); - } - } - }) - .is_ok(); - - if !printed { - // The thread-local storage was not available (because its - // destructor has already run). Create a new single-use - // Formatter on the stack for this call. - print(&mut Formatter::new(&self.writer), record); - } - } - } - - fn flush(&self) {} -} - -impl<'a> Env<'a> { - /// Get a default set of environment variables. - pub fn new() -> Self { - Self::default() - } - - /// Specify an environment variable to read the filter from. - pub fn filter(mut self, filter_env: E) -> Self - where - E: Into>, - { - self.filter = Var::new(filter_env); - - self - } - - /// Specify an environment variable to read the filter from. - /// - /// If the variable is not set, the default value will be used. - pub fn filter_or(mut self, filter_env: E, default: V) -> Self - where - E: Into>, - V: Into>, - { - self.filter = Var::new_with_default(filter_env, default); - - self - } - - /// Use the default environment variable to read the filter from. - /// - /// If the variable is not set, the default value will be used. - pub fn default_filter_or(mut self, default: V) -> Self - where - V: Into>, - { - self.filter = Var::new_with_default(DEFAULT_FILTER_ENV, default); - - self - } - - fn get_filter(&self) -> Option { - self.filter.get() - } - - /// Specify an environment variable to read the style from. - pub fn write_style(mut self, write_style_env: E) -> Self - where - E: Into>, - { - self.write_style = Var::new(write_style_env); - - self - } - - /// Specify an environment variable to read the style from. - /// - /// If the variable is not set, the default value will be used. - pub fn write_style_or(mut self, write_style_env: E, default: V) -> Self - where - E: Into>, - V: Into>, - { - self.write_style = Var::new_with_default(write_style_env, default); - - self - } - - /// Use the default environment variable to read the style from. - /// - /// If the variable is not set, the default value will be used. - pub fn default_write_style_or(mut self, default: V) -> Self - where - V: Into>, - { - self.write_style = Var::new_with_default(DEFAULT_WRITE_STYLE_ENV, default); - - self - } - - fn get_write_style(&self) -> Option { - self.write_style.get() - } -} - -impl<'a> Var<'a> { - fn new(name: E) -> Self - where - E: Into>, - { - Var { - name: name.into(), - default: None, - } - } - - fn new_with_default(name: E, default: V) -> Self - where - E: Into>, - V: Into>, - { - Var { - name: name.into(), - default: Some(default.into()), - } - } - - fn get(&self) -> Option { - env::var(&*self.name) - .ok() - .or_else(|| self.default.to_owned().map(|v| v.into_owned())) - } -} - -impl<'a, T> From for Env<'a> -where - T: Into>, -{ - fn from(filter_env: T) -> Self { - Env::default().filter(filter_env.into()) - } -} - -impl<'a> Default for Env<'a> { - fn default() -> Self { - Env { - filter: Var::new(DEFAULT_FILTER_ENV), - write_style: Var::new(DEFAULT_WRITE_STYLE_ENV), - } - } -} - -mod std_fmt_impls { - use super::*; - use std::fmt; - - impl fmt::Debug for Logger { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Logger") - .field("filter", &self.filter) - .finish() - } - } - - impl fmt::Debug for Builder { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.built { - f.debug_struct("Logger").field("built", &true).finish() - } else { - f.debug_struct("Logger") - .field("filter", &self.filter) - .field("writer", &self.writer) - .finish() - } - } - } -} - -/// Attempts to initialize the global logger with an env logger. -/// -/// This should be called early in the execution of a Rust program. Any log -/// events that occur before initialization will be ignored. -/// -/// # Errors -/// -/// This function will fail if it is called more than once, or if another -/// library has already initialized a global logger. -pub fn try_init() -> Result<(), SetLoggerError> { - try_init_from_env(Env::default()) -} - -/// Initializes the global logger with an env logger. -/// -/// This should be called early in the execution of a Rust program. Any log -/// events that occur before initialization will be ignored. -/// -/// # Panics -/// -/// This function will panic if it is called more than once, or if another -/// library has already initialized a global logger. -pub fn init() { - try_init().expect("env_logger::init should not be called after logger initialized"); -} - -/// Attempts to initialize the global logger with an env logger from the given -/// environment variables. -/// -/// This should be called early in the execution of a Rust program. Any log -/// events that occur before initialization will be ignored. -/// -/// # Examples -/// -/// Initialise a logger using the `MY_LOG` environment variable for filters -/// and `MY_LOG_STYLE` for writing colors: -/// -/// ``` -/// use env_logger::{Builder, Env}; -/// -/// # fn run() -> Result<(), Box> { -/// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); -/// -/// env_logger::try_init_from_env(env)?; -/// -/// Ok(()) -/// # } -/// # run().unwrap(); -/// ``` -/// -/// # Errors -/// -/// This function will fail if it is called more than once, or if another -/// library has already initialized a global logger. -pub fn try_init_from_env<'a, E>(env: E) -> Result<(), SetLoggerError> -where - E: Into>, -{ - let mut builder = Builder::from_env(env); - - builder.try_init() -} - -/// Initializes the global logger with an env logger from the given environment -/// variables. -/// -/// This should be called early in the execution of a Rust program. Any log -/// events that occur before initialization will be ignored. -/// -/// # Examples -/// -/// Initialise a logger using the `MY_LOG` environment variable for filters -/// and `MY_LOG_STYLE` for writing colors: -/// -/// ``` -/// use env_logger::{Builder, Env}; -/// -/// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); -/// -/// env_logger::init_from_env(env); -/// ``` -/// -/// # Panics -/// -/// This function will panic if it is called more than once, or if another -/// library has already initialized a global logger. -pub fn init_from_env<'a, E>(env: E) -where - E: Into>, -{ - try_init_from_env(env) - .expect("env_logger::init_from_env should not be called after logger initialized"); -} - -/// Create a new builder with the default environment variables. -/// -/// The builder can be configured before being initialized. -/// This is a convenient way of calling [`Builder::from_default_env`]. -/// -/// [`Builder::from_default_env`]: struct.Builder.html#method.from_default_env -pub fn builder() -> Builder { - Builder::from_default_env() -} - -/// Create a builder from the given environment variables. -/// -/// The builder can be configured before being initialized. -#[deprecated( - since = "0.8.0", - note = "Prefer `env_logger::Builder::from_env()` instead." -)] -pub fn from_env<'a, E>(env: E) -> Builder -where - E: Into>, -{ - Builder::from_env(env) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn env_get_filter_reads_from_var_if_set() { - env::set_var("env_get_filter_reads_from_var_if_set", "from var"); - - let env = Env::new().filter_or("env_get_filter_reads_from_var_if_set", "from default"); - - assert_eq!(Some("from var".to_owned()), env.get_filter()); - } - - #[test] - fn env_get_filter_reads_from_default_if_var_not_set() { - env::remove_var("env_get_filter_reads_from_default_if_var_not_set"); - - let env = Env::new().filter_or( - "env_get_filter_reads_from_default_if_var_not_set", - "from default", - ); - - assert_eq!(Some("from default".to_owned()), env.get_filter()); - } - - #[test] - fn env_get_write_style_reads_from_var_if_set() { - env::set_var("env_get_write_style_reads_from_var_if_set", "from var"); - - let env = - Env::new().write_style_or("env_get_write_style_reads_from_var_if_set", "from default"); - - assert_eq!(Some("from var".to_owned()), env.get_write_style()); - } - - #[test] - fn env_get_write_style_reads_from_default_if_var_not_set() { - env::remove_var("env_get_write_style_reads_from_default_if_var_not_set"); - - let env = Env::new().write_style_or( - "env_get_write_style_reads_from_default_if_var_not_set", - "from default", - ); - - assert_eq!(Some("from default".to_owned()), env.get_write_style()); - } - - #[test] - fn builder_parse_env_overrides_existing_filters() { - env::set_var( - "builder_parse_default_env_overrides_existing_filters", - "debug", - ); - let env = Env::new().filter("builder_parse_default_env_overrides_existing_filters"); - - let mut builder = Builder::new(); - builder.filter_level(LevelFilter::Trace); - // Overrides global level to debug - builder.parse_env(env); - - assert_eq!(builder.filter.build().filter(), LevelFilter::Debug); - } -} +pub use self::logger::*; diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 00000000..6c8a00d4 --- /dev/null +++ b/src/logger.rs @@ -0,0 +1,1016 @@ +use std::{borrow::Cow, cell::RefCell, env, io}; + +use log::{LevelFilter, Log, Metadata, Record, SetLoggerError}; + +use crate::filter; +use crate::filter::Filter; +use crate::fmt; +use crate::fmt::writer::{self, Writer}; +use crate::fmt::{FormatFn, Formatter}; + +/// The default name for the environment variable to read filters from. +pub const DEFAULT_FILTER_ENV: &str = "RUST_LOG"; + +/// The default name for the environment variable to read style preferences from. +pub const DEFAULT_WRITE_STYLE_ENV: &str = "RUST_LOG_STYLE"; + +/// `Builder` acts as builder for initializing a `Logger`. +/// +/// It can be used to customize the log format, change the environment variable used +/// to provide the logging directives and also set the default log level filter. +/// +/// # Examples +/// +/// ``` +/// # use std::io::Write; +/// use env_logger::Builder; +/// use log::{LevelFilter, error, info}; +/// +/// let mut builder = Builder::from_default_env(); +/// +/// builder +/// .format(|buf, record| writeln!(buf, "{} - {}", record.level(), record.args())) +/// .filter(None, LevelFilter::Info) +/// .init(); +/// +/// error!("error message"); +/// info!("info message"); +/// ``` +#[derive(Default)] +pub struct Builder { + filter: filter::Builder, + writer: writer::Builder, + format: fmt::Builder, + built: bool, +} + +impl Builder { + /// Initializes the log builder with defaults. + /// + /// **NOTE:** This method won't read from any environment variables. + /// Use the [`filter`] and [`write_style`] methods to configure the builder + /// or use [`from_env`] or [`from_default_env`] instead. + /// + /// # Examples + /// + /// Create a new builder and configure filters and style: + /// + /// ``` + /// use log::LevelFilter; + /// use env_logger::{Builder, WriteStyle}; + /// + /// let mut builder = Builder::new(); + /// + /// builder + /// .filter(None, LevelFilter::Info) + /// .write_style(WriteStyle::Always) + /// .init(); + /// ``` + /// + /// [`filter`]: #method.filter + /// [`write_style`]: #method.write_style + /// [`from_env`]: #method.from_env + /// [`from_default_env`]: #method.from_default_env + pub fn new() -> Builder { + Default::default() + } + + /// Initializes the log builder from the environment. + /// + /// The variables used to read configuration from can be tweaked before + /// passing in. + /// + /// # Examples + /// + /// Initialise a logger reading the log filter from an environment variable + /// called `MY_LOG`: + /// + /// ``` + /// use env_logger::Builder; + /// + /// let mut builder = Builder::from_env("MY_LOG"); + /// builder.init(); + /// ``` + /// + /// Initialise a logger using the `MY_LOG` variable for filtering and + /// `MY_LOG_STYLE` for whether or not to write styles: + /// + /// ``` + /// use env_logger::{Builder, Env}; + /// + /// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); + /// + /// let mut builder = Builder::from_env(env); + /// builder.init(); + /// ``` + pub fn from_env<'a, E>(env: E) -> Self + where + E: Into>, + { + let mut builder = Builder::new(); + builder.parse_env(env); + builder + } + + /// Applies the configuration from the environment. + /// + /// This function allows a builder to be configured with default parameters, + /// to be then overridden by the environment. + /// + /// # Examples + /// + /// Initialise a logger with filter level `Off`, then override the log + /// filter from an environment variable called `MY_LOG`: + /// + /// ``` + /// use log::LevelFilter; + /// use env_logger::Builder; + /// + /// let mut builder = Builder::new(); + /// + /// builder.filter_level(LevelFilter::Off); + /// builder.parse_env("MY_LOG"); + /// builder.init(); + /// ``` + /// + /// Initialise a logger with filter level `Off`, then use the `MY_LOG` + /// variable to override filtering and `MY_LOG_STYLE` to override whether + /// or not to write styles: + /// + /// ``` + /// use log::LevelFilter; + /// use env_logger::{Builder, Env}; + /// + /// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); + /// + /// let mut builder = Builder::new(); + /// builder.filter_level(LevelFilter::Off); + /// builder.parse_env(env); + /// builder.init(); + /// ``` + pub fn parse_env<'a, E>(&mut self, env: E) -> &mut Self + where + E: Into>, + { + let env = env.into(); + + if let Some(s) = env.get_filter() { + self.parse_filters(&s); + } + + if let Some(s) = env.get_write_style() { + self.parse_write_style(&s); + } + + self + } + + /// Initializes the log builder from the environment using default variable names. + /// + /// This method is a convenient way to call `from_env(Env::default())` without + /// having to use the `Env` type explicitly. The builder will use the + /// [default environment variables]. + /// + /// # Examples + /// + /// Initialise a logger using the default environment variables: + /// + /// ``` + /// use env_logger::Builder; + /// + /// let mut builder = Builder::from_default_env(); + /// builder.init(); + /// ``` + /// + /// [default environment variables]: struct.Env.html#default-environment-variables + pub fn from_default_env() -> Self { + Self::from_env(Env::default()) + } + + /// Applies the configuration from the environment using default variable names. + /// + /// This method is a convenient way to call `parse_env(Env::default())` without + /// having to use the `Env` type explicitly. The builder will use the + /// [default environment variables]. + /// + /// # Examples + /// + /// Initialise a logger with filter level `Off`, then configure it using the + /// default environment variables: + /// + /// ``` + /// use log::LevelFilter; + /// use env_logger::Builder; + /// + /// let mut builder = Builder::new(); + /// builder.filter_level(LevelFilter::Off); + /// builder.parse_default_env(); + /// builder.init(); + /// ``` + /// + /// [default environment variables]: struct.Env.html#default-environment-variables + pub fn parse_default_env(&mut self) -> &mut Self { + self.parse_env(Env::default()) + } + + /// Sets the format function for formatting the log output. + /// + /// This function is called on each record logged and should format the + /// log record and output it to the given [`Formatter`]. + /// + /// The format function is expected to output the string directly to the + /// `Formatter` so that implementations can use the [`std::fmt`] macros + /// to format and output without intermediate heap allocations. The default + /// `env_logger` formatter takes advantage of this. + /// + /// # Examples + /// + /// Use a custom format to write only the log message: + /// + /// ``` + /// use std::io::Write; + /// use env_logger::Builder; + /// + /// let mut builder = Builder::new(); + /// + /// builder.format(|buf, record| writeln!(buf, "{}", record.args())); + /// ``` + /// + /// [`Formatter`]: fmt/struct.Formatter.html + /// [`String`]: https://doc.rust-lang.org/stable/std/string/struct.String.html + /// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html + pub fn format(&mut self, format: F) -> &mut Self + where + F: Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send, + { + self.format.custom_format = Some(Box::new(format)); + self + } + + /// Use the default format. + /// + /// This method will clear any custom format set on the builder. + pub fn default_format(&mut self) -> &mut Self { + self.format = Default::default(); + self + } + + /// Whether or not to write the level in the default format. + pub fn format_level(&mut self, write: bool) -> &mut Self { + self.format.format_level = write; + self + } + + /// Whether or not to write the module path in the default format. + pub fn format_module_path(&mut self, write: bool) -> &mut Self { + self.format.format_module_path = write; + self + } + + /// Whether or not to write the target in the default format. + pub fn format_target(&mut self, write: bool) -> &mut Self { + self.format.format_target = write; + self + } + + /// Configures the amount of spaces to use to indent multiline log records. + /// A value of `None` disables any kind of indentation. + pub fn format_indent(&mut self, indent: Option) -> &mut Self { + self.format.format_indent = indent; + self + } + + /// Configures if timestamp should be included and in what precision. + pub fn format_timestamp(&mut self, timestamp: Option) -> &mut Self { + self.format.format_timestamp = timestamp; + self + } + + /// Configures the timestamp to use second precision. + pub fn format_timestamp_secs(&mut self) -> &mut Self { + self.format_timestamp(Some(fmt::TimestampPrecision::Seconds)) + } + + /// Configures the timestamp to use millisecond precision. + pub fn format_timestamp_millis(&mut self) -> &mut Self { + self.format_timestamp(Some(fmt::TimestampPrecision::Millis)) + } + + /// Configures the timestamp to use microsecond precision. + pub fn format_timestamp_micros(&mut self) -> &mut Self { + self.format_timestamp(Some(fmt::TimestampPrecision::Micros)) + } + + /// Configures the timestamp to use nanosecond precision. + pub fn format_timestamp_nanos(&mut self) -> &mut Self { + self.format_timestamp(Some(fmt::TimestampPrecision::Nanos)) + } + + /// Configures the end of line suffix. + pub fn format_suffix(&mut self, suffix: &'static str) -> &mut Self { + self.format.format_suffix = suffix; + self + } + + /// Adds a directive to the filter for a specific module. + /// + /// # Examples + /// + /// Only include messages for info and above for logs in `path::to::module`: + /// + /// ``` + /// use env_logger::Builder; + /// use log::LevelFilter; + /// + /// let mut builder = Builder::new(); + /// + /// builder.filter_module("path::to::module", LevelFilter::Info); + /// ``` + pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self { + self.filter.filter_module(module, level); + self + } + + /// Adds a directive to the filter for all modules. + /// + /// # Examples + /// + /// Only include messages for info and above for logs globally: + /// + /// ``` + /// use env_logger::Builder; + /// use log::LevelFilter; + /// + /// let mut builder = Builder::new(); + /// + /// builder.filter_level(LevelFilter::Info); + /// ``` + pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self { + self.filter.filter_level(level); + self + } + + /// Adds filters to the logger. + /// + /// The given module (if any) will log at most the specified level provided. + /// If no module is provided then the filter will apply to all log messages. + /// + /// # Examples + /// + /// Only include messages for info and above for logs in `path::to::module`: + /// + /// ``` + /// use env_logger::Builder; + /// use log::LevelFilter; + /// + /// let mut builder = Builder::new(); + /// + /// builder.filter(Some("path::to::module"), LevelFilter::Info); + /// ``` + pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self { + self.filter.filter(module, level); + self + } + + /// Parses the directives string in the same form as the `RUST_LOG` + /// environment variable. + /// + /// See the module documentation for more details. + pub fn parse_filters(&mut self, filters: &str) -> &mut Self { + self.filter.parse(filters); + self + } + + /// Sets the target for the log output. + /// + /// Env logger can log to either stdout, stderr or a custom pipe. The default is stderr. + /// + /// The custom pipe can be used to send the log messages to a custom sink (for example a file). + /// Do note that direct writes to a file can become a bottleneck due to IO operation times. + /// + /// # Examples + /// + /// Write log message to `stdout`: + /// + /// ``` + /// use env_logger::{Builder, Target}; + /// + /// let mut builder = Builder::new(); + /// + /// builder.target(Target::Stdout); + /// ``` + pub fn target(&mut self, target: fmt::Target) -> &mut Self { + self.writer.target(target); + self + } + + /// Sets whether or not styles will be written. + /// + /// This can be useful in environments that don't support control characters + /// for setting colors. + /// + /// # Examples + /// + /// Never attempt to write styles: + /// + /// ``` + /// use env_logger::{Builder, WriteStyle}; + /// + /// let mut builder = Builder::new(); + /// + /// builder.write_style(WriteStyle::Never); + /// ``` + pub fn write_style(&mut self, write_style: fmt::WriteStyle) -> &mut Self { + self.writer.write_style(write_style); + self + } + + /// Parses whether or not to write styles in the same form as the `RUST_LOG_STYLE` + /// environment variable. + /// + /// See the module documentation for more details. + pub fn parse_write_style(&mut self, write_style: &str) -> &mut Self { + self.writer.parse_write_style(write_style); + self + } + + /// Sets whether or not the logger will be used in unit tests. + /// + /// If `is_test` is `true` then the logger will allow the testing framework to + /// capture log records rather than printing them to the terminal directly. + pub fn is_test(&mut self, is_test: bool) -> &mut Self { + self.writer.is_test(is_test); + self + } + + /// Initializes the global logger with the built env logger. + /// + /// This should be called early in the execution of a Rust program. Any log + /// events that occur before initialization will be ignored. + /// + /// # Errors + /// + /// This function will fail if it is called more than once, or if another + /// library has already initialized a global logger. + pub fn try_init(&mut self) -> Result<(), SetLoggerError> { + let logger = self.build(); + + let max_level = logger.filter(); + let r = log::set_boxed_logger(Box::new(logger)); + + if r.is_ok() { + log::set_max_level(max_level); + } + + r + } + + /// Initializes the global logger with the built env logger. + /// + /// This should be called early in the execution of a Rust program. Any log + /// events that occur before initialization will be ignored. + /// + /// # Panics + /// + /// This function will panic if it is called more than once, or if another + /// library has already initialized a global logger. + pub fn init(&mut self) { + self.try_init() + .expect("Builder::init should not be called after logger initialized"); + } + + /// Build an env logger. + /// + /// The returned logger implements the `Log` trait and can be installed manually + /// or nested within another logger. + pub fn build(&mut self) -> Logger { + assert!(!self.built, "attempt to re-use consumed builder"); + self.built = true; + + Logger { + writer: self.writer.build(), + filter: self.filter.build(), + format: self.format.build(), + } + } +} + +impl std::fmt::Debug for Builder { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if self.built { + f.debug_struct("Logger").field("built", &true).finish() + } else { + f.debug_struct("Logger") + .field("filter", &self.filter) + .field("writer", &self.writer) + .finish() + } + } +} + +/// The env logger. +/// +/// This struct implements the `Log` trait from the [`log` crate][log-crate-url], +/// which allows it to act as a logger. +/// +/// The [`init()`], [`try_init()`], [`Builder::init()`] and [`Builder::try_init()`] +/// methods will each construct a `Logger` and immediately initialize it as the +/// default global logger. +/// +/// If you'd instead need access to the constructed `Logger`, you can use +/// the associated [`Builder`] and install it with the +/// [`log` crate][log-crate-url] directly. +/// +/// [log-crate-url]: https://docs.rs/log +/// [`init()`]: fn.init.html +/// [`try_init()`]: fn.try_init.html +/// [`Builder::init()`]: struct.Builder.html#method.init +/// [`Builder::try_init()`]: struct.Builder.html#method.try_init +/// [`Builder`]: struct.Builder.html +pub struct Logger { + writer: Writer, + filter: Filter, + format: FormatFn, +} + +impl Logger { + /// Creates the logger from the environment. + /// + /// The variables used to read configuration from can be tweaked before + /// passing in. + /// + /// # Examples + /// + /// Create a logger reading the log filter from an environment variable + /// called `MY_LOG`: + /// + /// ``` + /// use env_logger::Logger; + /// + /// let logger = Logger::from_env("MY_LOG"); + /// ``` + /// + /// Create a logger using the `MY_LOG` variable for filtering and + /// `MY_LOG_STYLE` for whether or not to write styles: + /// + /// ``` + /// use env_logger::{Logger, Env}; + /// + /// let env = Env::new().filter_or("MY_LOG", "info").write_style_or("MY_LOG_STYLE", "always"); + /// + /// let logger = Logger::from_env(env); + /// ``` + pub fn from_env<'a, E>(env: E) -> Self + where + E: Into>, + { + Builder::from_env(env).build() + } + + /// Creates the logger from the environment using default variable names. + /// + /// This method is a convenient way to call `from_env(Env::default())` without + /// having to use the `Env` type explicitly. The logger will use the + /// [default environment variables]. + /// + /// # Examples + /// + /// Creates a logger using the default environment variables: + /// + /// ``` + /// use env_logger::Logger; + /// + /// let logger = Logger::from_default_env(); + /// ``` + /// + /// [default environment variables]: struct.Env.html#default-environment-variables + pub fn from_default_env() -> Self { + Builder::from_default_env().build() + } + + /// Returns the maximum `LevelFilter` that this env logger instance is + /// configured to output. + pub fn filter(&self) -> LevelFilter { + self.filter.filter() + } + + /// Checks if this record matches the configured filter. + pub fn matches(&self, record: &Record) -> bool { + self.filter.matches(record) + } +} + +impl Log for Logger { + fn enabled(&self, metadata: &Metadata) -> bool { + self.filter.enabled(metadata) + } + + fn log(&self, record: &Record) { + if self.matches(record) { + // Log records are written to a thread-local buffer before being printed + // to the terminal. We clear these buffers afterwards, but they aren't shrunk + // so will always at least have capacity for the largest log record formatted + // on that thread. + // + // If multiple `Logger`s are used by the same threads then the thread-local + // formatter might have different color support. If this is the case the + // formatter and its buffer are discarded and recreated. + + thread_local! { + static FORMATTER: RefCell> = RefCell::new(None); + } + + let print = |formatter: &mut Formatter, record: &Record| { + let _ = + (self.format)(formatter, record).and_then(|_| formatter.print(&self.writer)); + + // Always clear the buffer afterwards + formatter.clear(); + }; + + let printed = FORMATTER + .try_with(|tl_buf| { + match tl_buf.try_borrow_mut() { + // There are no active borrows of the buffer + Ok(mut tl_buf) => match *tl_buf { + // We have a previously set formatter + Some(ref mut formatter) => { + // Check the buffer style. If it's different from the logger's + // style then drop the buffer and recreate it. + if formatter.write_style() != self.writer.write_style() { + *formatter = Formatter::new(&self.writer); + } + + print(formatter, record); + } + // We don't have a previously set formatter + None => { + let mut formatter = Formatter::new(&self.writer); + print(&mut formatter, record); + + *tl_buf = Some(formatter); + } + }, + // There's already an active borrow of the buffer (due to re-entrancy) + Err(_) => { + print(&mut Formatter::new(&self.writer), record); + } + } + }) + .is_ok(); + + if !printed { + // The thread-local storage was not available (because its + // destructor has already run). Create a new single-use + // Formatter on the stack for this call. + print(&mut Formatter::new(&self.writer), record); + } + } + } + + fn flush(&self) {} +} + +impl std::fmt::Debug for Logger { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("Logger") + .field("filter", &self.filter) + .finish() + } +} + +/// Set of environment variables to configure from. +/// +/// # Default environment variables +/// +/// By default, the `Env` will read the following environment variables: +/// +/// - `RUST_LOG`: the level filter +/// - `RUST_LOG_STYLE`: whether or not to print styles with records. +/// +/// These sources can be configured using the builder methods on `Env`. +#[derive(Debug)] +pub struct Env<'a> { + filter: Var<'a>, + write_style: Var<'a>, +} + +impl<'a> Env<'a> { + /// Get a default set of environment variables. + pub fn new() -> Self { + Self::default() + } + + /// Specify an environment variable to read the filter from. + pub fn filter(mut self, filter_env: E) -> Self + where + E: Into>, + { + self.filter = Var::new(filter_env); + + self + } + + /// Specify an environment variable to read the filter from. + /// + /// If the variable is not set, the default value will be used. + pub fn filter_or(mut self, filter_env: E, default: V) -> Self + where + E: Into>, + V: Into>, + { + self.filter = Var::new_with_default(filter_env, default); + + self + } + + /// Use the default environment variable to read the filter from. + /// + /// If the variable is not set, the default value will be used. + pub fn default_filter_or(mut self, default: V) -> Self + where + V: Into>, + { + self.filter = Var::new_with_default(DEFAULT_FILTER_ENV, default); + + self + } + + fn get_filter(&self) -> Option { + self.filter.get() + } + + /// Specify an environment variable to read the style from. + pub fn write_style(mut self, write_style_env: E) -> Self + where + E: Into>, + { + self.write_style = Var::new(write_style_env); + + self + } + + /// Specify an environment variable to read the style from. + /// + /// If the variable is not set, the default value will be used. + pub fn write_style_or(mut self, write_style_env: E, default: V) -> Self + where + E: Into>, + V: Into>, + { + self.write_style = Var::new_with_default(write_style_env, default); + + self + } + + /// Use the default environment variable to read the style from. + /// + /// If the variable is not set, the default value will be used. + pub fn default_write_style_or(mut self, default: V) -> Self + where + V: Into>, + { + self.write_style = Var::new_with_default(DEFAULT_WRITE_STYLE_ENV, default); + + self + } + + fn get_write_style(&self) -> Option { + self.write_style.get() + } +} + +impl<'a, T> From for Env<'a> +where + T: Into>, +{ + fn from(filter_env: T) -> Self { + Env::default().filter(filter_env.into()) + } +} + +impl<'a> Default for Env<'a> { + fn default() -> Self { + Env { + filter: Var::new(DEFAULT_FILTER_ENV), + write_style: Var::new(DEFAULT_WRITE_STYLE_ENV), + } + } +} + +#[derive(Debug)] +struct Var<'a> { + name: Cow<'a, str>, + default: Option>, +} + +impl<'a> Var<'a> { + fn new(name: E) -> Self + where + E: Into>, + { + Var { + name: name.into(), + default: None, + } + } + + fn new_with_default(name: E, default: V) -> Self + where + E: Into>, + V: Into>, + { + Var { + name: name.into(), + default: Some(default.into()), + } + } + + fn get(&self) -> Option { + env::var(&*self.name) + .ok() + .or_else(|| self.default.to_owned().map(|v| v.into_owned())) + } +} + +/// Attempts to initialize the global logger with an env logger. +/// +/// This should be called early in the execution of a Rust program. Any log +/// events that occur before initialization will be ignored. +/// +/// # Errors +/// +/// This function will fail if it is called more than once, or if another +/// library has already initialized a global logger. +pub fn try_init() -> Result<(), SetLoggerError> { + try_init_from_env(Env::default()) +} + +/// Initializes the global logger with an env logger. +/// +/// This should be called early in the execution of a Rust program. Any log +/// events that occur before initialization will be ignored. +/// +/// # Panics +/// +/// This function will panic if it is called more than once, or if another +/// library has already initialized a global logger. +pub fn init() { + try_init().expect("env_logger::init should not be called after logger initialized"); +} + +/// Attempts to initialize the global logger with an env logger from the given +/// environment variables. +/// +/// This should be called early in the execution of a Rust program. Any log +/// events that occur before initialization will be ignored. +/// +/// # Examples +/// +/// Initialise a logger using the `MY_LOG` environment variable for filters +/// and `MY_LOG_STYLE` for writing colors: +/// +/// ``` +/// use env_logger::{Builder, Env}; +/// +/// # fn run() -> Result<(), Box> { +/// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); +/// +/// env_logger::try_init_from_env(env)?; +/// +/// Ok(()) +/// # } +/// # run().unwrap(); +/// ``` +/// +/// # Errors +/// +/// This function will fail if it is called more than once, or if another +/// library has already initialized a global logger. +pub fn try_init_from_env<'a, E>(env: E) -> Result<(), SetLoggerError> +where + E: Into>, +{ + let mut builder = Builder::from_env(env); + + builder.try_init() +} + +/// Initializes the global logger with an env logger from the given environment +/// variables. +/// +/// This should be called early in the execution of a Rust program. Any log +/// events that occur before initialization will be ignored. +/// +/// # Examples +/// +/// Initialise a logger using the `MY_LOG` environment variable for filters +/// and `MY_LOG_STYLE` for writing colors: +/// +/// ``` +/// use env_logger::{Builder, Env}; +/// +/// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); +/// +/// env_logger::init_from_env(env); +/// ``` +/// +/// # Panics +/// +/// This function will panic if it is called more than once, or if another +/// library has already initialized a global logger. +pub fn init_from_env<'a, E>(env: E) +where + E: Into>, +{ + try_init_from_env(env) + .expect("env_logger::init_from_env should not be called after logger initialized"); +} + +/// Create a new builder with the default environment variables. +/// +/// The builder can be configured before being initialized. +/// This is a convenient way of calling [`Builder::from_default_env`]. +/// +/// [`Builder::from_default_env`]: struct.Builder.html#method.from_default_env +pub fn builder() -> Builder { + Builder::from_default_env() +} + +/// Create a builder from the given environment variables. +/// +/// The builder can be configured before being initialized. +#[deprecated( + since = "0.8.0", + note = "Prefer `env_logger::Builder::from_env()` instead." +)] +pub fn from_env<'a, E>(env: E) -> Builder +where + E: Into>, +{ + Builder::from_env(env) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn env_get_filter_reads_from_var_if_set() { + env::set_var("env_get_filter_reads_from_var_if_set", "from var"); + + let env = Env::new().filter_or("env_get_filter_reads_from_var_if_set", "from default"); + + assert_eq!(Some("from var".to_owned()), env.get_filter()); + } + + #[test] + fn env_get_filter_reads_from_default_if_var_not_set() { + env::remove_var("env_get_filter_reads_from_default_if_var_not_set"); + + let env = Env::new().filter_or( + "env_get_filter_reads_from_default_if_var_not_set", + "from default", + ); + + assert_eq!(Some("from default".to_owned()), env.get_filter()); + } + + #[test] + fn env_get_write_style_reads_from_var_if_set() { + env::set_var("env_get_write_style_reads_from_var_if_set", "from var"); + + let env = + Env::new().write_style_or("env_get_write_style_reads_from_var_if_set", "from default"); + + assert_eq!(Some("from var".to_owned()), env.get_write_style()); + } + + #[test] + fn env_get_write_style_reads_from_default_if_var_not_set() { + env::remove_var("env_get_write_style_reads_from_default_if_var_not_set"); + + let env = Env::new().write_style_or( + "env_get_write_style_reads_from_default_if_var_not_set", + "from default", + ); + + assert_eq!(Some("from default".to_owned()), env.get_write_style()); + } + + #[test] + fn builder_parse_env_overrides_existing_filters() { + env::set_var( + "builder_parse_default_env_overrides_existing_filters", + "debug", + ); + let env = Env::new().filter("builder_parse_default_env_overrides_existing_filters"); + + let mut builder = Builder::new(); + builder.filter_level(LevelFilter::Trace); + // Overrides global level to debug + builder.parse_env(env); + + assert_eq!(builder.filter.build().filter(), LevelFilter::Debug); + } +}