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); } }