Skip to main content

midenc_log/
logger.rs

1use std::{borrow::Cow, cell::RefCell, env, io};
2
3use log::{LevelFilter, Log, Metadata, Record, SetLoggerError};
4
5use crate::{
6    fmt,
7    fmt::{FormatFn, Formatter},
8    writer::{self, Writer},
9};
10
11/// The default name for the environment variable to read filters from.
12pub const DEFAULT_FILTER_ENV: &str = "MIDENC_TRACE";
13
14/// The default name for the environment variable to read kv filters from.
15pub const DEFAULT_KV_FILTER_ENV: &str = "MIDENC_TRACE_FILTER";
16
17/// The default name for the environment variable to read style preferences from.
18pub const DEFAULT_WRITE_STYLE_ENV: &str = "MIDENC_TRACE_STYLE";
19
20/// `Builder` acts as builder for initializing a `Logger`.
21///
22/// It can be used to customize the log format, change the environment variable used
23/// to provide the logging directives and also set the default log level filter.
24///
25/// # Examples
26///
27/// ```
28/// # use std::io::Write;
29/// use midenc_log::Builder;
30/// use log::{LevelFilter, error, info};
31///
32/// let mut builder = Builder::from_default_env();
33///
34/// builder
35///     .format(|buf, record| writeln!(buf, "{} - {}", record.level(), record.args()))
36///     .filter(None, LevelFilter::Info)
37///     .init();
38///
39/// error!("error message");
40/// info!("info message");
41/// ```
42#[derive(Default)]
43pub struct Builder {
44    filter: crate::filter::Builder,
45    writer: writer::Builder,
46    format: fmt::Builder,
47    built: bool,
48}
49
50impl Builder {
51    /// Initializes the log builder with defaults.
52    ///
53    /// **NOTE:** This method won't read from any environment variables.
54    /// Use the [`filter`] and [`write_style`] methods to configure the builder
55    /// or use [`from_env`] or [`from_default_env`] instead.
56    ///
57    /// # Examples
58    ///
59    /// Create a new builder and configure filters and style:
60    ///
61    /// ```
62    /// use log::LevelFilter;
63    /// use midenc_log::{Builder, WriteStyle};
64    ///
65    /// let mut builder = Builder::new();
66    ///
67    /// builder
68    ///     .filter(None, LevelFilter::Info)
69    ///     .write_style(WriteStyle::Always)
70    ///     .init();
71    /// ```
72    ///
73    /// [`filter`]: #method.filter
74    /// [`write_style`]: #method.write_style
75    /// [`from_env`]: #method.from_env
76    /// [`from_default_env`]: #method.from_default_env
77    pub fn new() -> Builder {
78        Default::default()
79    }
80
81    /// Initializes the log builder from the environment.
82    ///
83    /// The variables used to read configuration from can be tweaked before
84    /// passing in.
85    ///
86    /// # Examples
87    ///
88    /// Initialise a logger reading the log filter from an environment variable
89    /// called `MY_LOG`:
90    ///
91    /// ```
92    /// use midenc_log::Builder;
93    ///
94    /// let mut builder = Builder::from_env("MY_LOG");
95    /// builder.init();
96    /// ```
97    ///
98    /// Initialise a logger using the `MY_LOG` variable for filtering and
99    /// `MY_LOG_STYLE` for whether or not to write styles:
100    ///
101    /// ```
102    /// use midenc_log::{Builder, Env};
103    ///
104    /// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE");
105    ///
106    /// let mut builder = Builder::from_env(env);
107    /// builder.init();
108    /// ```
109    pub fn from_env<'a, E>(env: E) -> Self
110    where
111        E: Into<Env<'a>>,
112    {
113        let mut builder = Builder::new();
114        builder.parse_env(env);
115        builder
116    }
117
118    /// Applies the configuration from the environment.
119    ///
120    /// This function allows a builder to be configured with default parameters,
121    /// to be then overridden by the environment.
122    ///
123    /// # Examples
124    ///
125    /// Initialise a logger with filter level `Off`, then override the log
126    /// filter from an environment variable called `MY_LOG`:
127    ///
128    /// ```
129    /// use log::LevelFilter;
130    /// use midenc_log::Builder;
131    ///
132    /// let mut builder = Builder::new();
133    ///
134    /// builder.filter_level(LevelFilter::Off);
135    /// builder.parse_env("MY_LOG");
136    /// builder.init();
137    /// ```
138    ///
139    /// Initialise a logger with filter level `Off`, then use the `MY_LOG`
140    /// variable to override filtering and `MY_LOG_STYLE` to override  whether
141    /// or not to write styles:
142    ///
143    /// ```
144    /// use log::LevelFilter;
145    /// use midenc_log::{Builder, Env};
146    ///
147    /// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE");
148    ///
149    /// let mut builder = Builder::new();
150    /// builder.filter_level(LevelFilter::Off);
151    /// builder.parse_env(env);
152    /// builder.init();
153    /// ```
154    pub fn parse_env<'a, E>(&mut self, env: E) -> &mut Self
155    where
156        E: Into<Env<'a>>,
157    {
158        let env = env.into();
159
160        if let Some(s) = env.get_filter() {
161            self.parse_filters(&s);
162        }
163
164        if let Some(s) = env.get_kv_filter() {
165            self.parse_kv_filters(&s);
166        }
167
168        if let Some(s) = env.get_write_style() {
169            self.parse_write_style(&s);
170        }
171
172        self
173    }
174
175    /// Initializes the log builder from the environment using default variable names.
176    ///
177    /// This method is a convenient way to call `from_env(Env::default())` without
178    /// having to use the `Env` type explicitly. The builder will use the
179    /// [default environment variables].
180    ///
181    /// # Examples
182    ///
183    /// Initialise a logger using the default environment variables:
184    ///
185    /// ```
186    /// use midenc_log::Builder;
187    ///
188    /// let mut builder = Builder::from_default_env();
189    /// builder.init();
190    /// ```
191    ///
192    /// [default environment variables]: struct.Env.html#default-environment-variables
193    pub fn from_default_env() -> Self {
194        Self::from_env(Env::default())
195    }
196
197    /// Applies the configuration from the environment using default variable names.
198    ///
199    /// This method is a convenient way to call `parse_env(Env::default())` without
200    /// having to use the `Env` type explicitly. The builder will use the
201    /// [default environment variables].
202    ///
203    /// # Examples
204    ///
205    /// Initialise a logger with filter level `Off`, then configure it using the
206    /// default environment variables:
207    ///
208    /// ```
209    /// use log::LevelFilter;
210    /// use midenc_log::Builder;
211    ///
212    /// let mut builder = Builder::new();
213    /// builder.filter_level(LevelFilter::Off);
214    /// builder.parse_default_env();
215    /// builder.init();
216    /// ```
217    ///
218    /// [default environment variables]: struct.Env.html#default-environment-variables
219    pub fn parse_default_env(&mut self) -> &mut Self {
220        self.parse_env(Env::default())
221    }
222
223    /// Sets the format function for formatting the log output.
224    ///
225    /// This function is called on each record logged and should format the
226    /// log record and output it to the given [`Formatter`].
227    ///
228    /// The format function is expected to output the string directly to the
229    /// `Formatter` so that implementations can use the [`std::fmt`] macros
230    /// to format and output without intermediate heap allocations. The default
231    /// `midenc_log` formatter takes advantage of this.
232    ///
233    /// When the `color` feature is enabled, styling via ANSI escape codes is supported and the
234    /// output will automatically respect [`Builder::write_style`].
235    ///
236    /// # Examples
237    ///
238    /// Use a custom format to write only the log message:
239    ///
240    /// ```
241    /// use std::io::Write;
242    /// use midenc_log::Builder;
243    ///
244    /// let mut builder = Builder::new();
245    ///
246    /// builder.format(|buf, record| writeln!(buf, "{}", record.args()));
247    /// ```
248    ///
249    /// [`Formatter`]: fmt/struct.Formatter.html
250    /// [`String`]: https://doc.rust-lang.org/stable/std/string/struct.String.html
251    /// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html
252    pub fn format<F>(&mut self, format: F) -> &mut Self
253    where
254        F: Fn(&mut Formatter, &Record<'_>) -> io::Result<()> + Sync + Send + 'static,
255    {
256        self.format.custom_format = Some(Box::new(format));
257        self
258    }
259
260    /// Use the default format.
261    ///
262    /// This method will clear any custom format set on the builder.
263    pub fn default_format(&mut self) -> &mut Self {
264        self.format = Default::default();
265        self
266    }
267
268    /// Whether or not to write the level in the default format.
269    pub fn format_level(&mut self, write: bool) -> &mut Self {
270        self.format.default_format.level(write);
271        self
272    }
273
274    /// Whether or not to write the source file path in the default format.
275    pub fn format_file(&mut self, write: bool) -> &mut Self {
276        self.format.default_format.file(write);
277        self
278    }
279
280    /// Whether or not to write the source line number path in the default format.
281    ///
282    /// Only has effect if `format_file` is also enabled
283    pub fn format_line_number(&mut self, write: bool) -> &mut Self {
284        self.format.default_format.line_number(write);
285        self
286    }
287
288    /// Whether or not to write the source path and line number
289    ///
290    /// Equivalent to calling both `format_file` and `format_line_number`
291    /// with `true`
292    pub fn format_source_path(&mut self, write: bool) -> &mut Self {
293        self.format_file(write).format_line_number(write);
294        self
295    }
296
297    /// Whether or not to write the module path in the default format.
298    pub fn format_module_path(&mut self, write: bool) -> &mut Self {
299        self.format.default_format.module_path(write);
300        self
301    }
302
303    /// Whether or not to write the target in the default format.
304    pub fn format_target(&mut self, write: bool) -> &mut Self {
305        self.format.default_format.target(write);
306        self
307    }
308
309    /// Configures the amount of spaces to use to indent multiline log records.
310    /// A value of `None` disables any kind of indentation.
311    pub fn format_indent(&mut self, indent: Option<usize>) -> &mut Self {
312        self.format.default_format.indent(indent);
313        self
314    }
315
316    /// Configures if timestamp should be included and in what precision.
317    pub fn format_timestamp(&mut self, timestamp: Option<fmt::TimestampPrecision>) -> &mut Self {
318        self.format.default_format.timestamp(timestamp);
319        self
320    }
321
322    /// Configures the timestamp to use second precision.
323    pub fn format_timestamp_secs(&mut self) -> &mut Self {
324        self.format_timestamp(Some(fmt::TimestampPrecision::Seconds))
325    }
326
327    /// Configures the timestamp to use millisecond precision.
328    pub fn format_timestamp_millis(&mut self) -> &mut Self {
329        self.format_timestamp(Some(fmt::TimestampPrecision::Millis))
330    }
331
332    /// Configures the timestamp to use microsecond precision.
333    pub fn format_timestamp_micros(&mut self) -> &mut Self {
334        self.format_timestamp(Some(fmt::TimestampPrecision::Micros))
335    }
336
337    /// Configures the timestamp to use nanosecond precision.
338    pub fn format_timestamp_nanos(&mut self) -> &mut Self {
339        self.format_timestamp(Some(fmt::TimestampPrecision::Nanos))
340    }
341
342    /// Configures the end of line suffix.
343    pub fn format_suffix(&mut self, suffix: &'static str) -> &mut Self {
344        self.format.default_format.suffix(suffix);
345        self
346    }
347
348    /// Set the format for structured key/value pairs in the log record
349    ///
350    /// With the default format, this function is called for each record and should format
351    /// the structured key-value pairs as returned by [`log::Record::key_values`].
352    ///
353    /// The format function is expected to output the string directly to the `Formatter` so that
354    /// implementations can use the [`std::fmt`] macros, similar to the main format function.
355    ///
356    /// The default format uses a space to separate each key-value pair, with an "=" between
357    /// the key and value.
358    #[cfg(feature = "kv")]
359    pub fn format_key_values<F>(&mut self, format: F) -> &mut Self
360    where
361        F: Fn(&mut Formatter, &dyn log::kv::Source) -> io::Result<()> + Sync + Send + 'static,
362    {
363        self.format.default_format.key_values(format);
364        self
365    }
366
367    /// Adds a directive to the filter for a specific module.
368    ///
369    /// # Examples
370    ///
371    /// Only include messages for info and above for logs in `path::to::module`:
372    ///
373    /// ```
374    /// use midenc_log::Builder;
375    /// use log::LevelFilter;
376    ///
377    /// let mut builder = Builder::new();
378    ///
379    /// builder.filter_module("path::to::module", LevelFilter::Info);
380    /// ```
381    pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
382        self.filter.filter_module(module, level);
383        self
384    }
385
386    /// Adds a directive to the filter for all modules.
387    ///
388    /// # Examples
389    ///
390    /// Only include messages for info and above for logs globally:
391    ///
392    /// ```
393    /// use midenc_log::Builder;
394    /// use log::LevelFilter;
395    ///
396    /// let mut builder = Builder::new();
397    ///
398    /// builder.filter_level(LevelFilter::Info);
399    /// ```
400    pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
401        self.filter.filter_level(level);
402        self
403    }
404
405    /// Parses the directives string in the same form as the `MIDENC_TRACE`
406    /// environment variable.
407    ///
408    /// See the module documentation for more details.
409    pub fn parse_filters(&mut self, filters: &str) -> &mut Self {
410        self.filter.parse(filters);
411        self
412    }
413
414    /// Parses a kv filter string from the `MIDENC_TRACE_FILTER` environment variable.
415    ///
416    /// See the module documentation for more details.
417    pub fn parse_kv_filters(&mut self, filters: &str) -> &mut Self {
418        for filter in filters.split(',').map(|f| f.trim()) {
419            let (filter, negated) =
420                filter.strip_prefix('-').map(|f| (f, true)).unwrap_or((filter, false));
421            let (key, value) = filter
422                .split_once(':')
423                .expect("invalid kv filter spec: expected format is `key:value[,key2:value2,..]`");
424            self.filter.filter_key_value(key, value, negated);
425        }
426        self
427    }
428
429    /// Sets the target for the log output.
430    ///
431    /// Env logger can log to either stdout, stderr or a custom pipe. The default is stderr.
432    ///
433    /// The custom pipe can be used to send the log messages to a custom sink (for example a file).
434    /// Do note that direct writes to a file can become a bottleneck due to IO operation times.
435    ///
436    /// # Examples
437    ///
438    /// Write log message to `stdout`:
439    ///
440    /// ```
441    /// use midenc_log::{Builder, Target};
442    ///
443    /// let mut builder = Builder::new();
444    ///
445    /// builder.target(Target::Stdout);
446    /// ```
447    pub fn target(&mut self, target: fmt::Target) -> &mut Self {
448        self.writer.target(target);
449        self
450    }
451
452    /// Sets whether or not styles will be written.
453    ///
454    /// This can be useful in environments that don't support control characters
455    /// for setting colors.
456    ///
457    /// # Examples
458    ///
459    /// Never attempt to write styles:
460    ///
461    /// ```
462    /// use midenc_log::{Builder, WriteStyle};
463    ///
464    /// let mut builder = Builder::new();
465    ///
466    /// builder.write_style(WriteStyle::Never);
467    /// ```
468    pub fn write_style(&mut self, write_style: fmt::WriteStyle) -> &mut Self {
469        self.writer.write_style(write_style);
470        self
471    }
472
473    /// Parses whether or not to write styles in the same form as the `MIDENC_LOG_STYLE`
474    /// environment variable.
475    ///
476    /// See the module documentation for more details.
477    pub fn parse_write_style(&mut self, write_style: &str) -> &mut Self {
478        self.writer.parse_write_style(write_style);
479        self
480    }
481
482    /// Sets whether or not the logger will be used in unit tests.
483    ///
484    /// If `is_test` is `true` then the logger will allow the testing framework to
485    /// capture log records rather than printing them to the terminal directly.
486    pub fn is_test(&mut self, is_test: bool) -> &mut Self {
487        self.writer.is_test(is_test);
488        self
489    }
490
491    /// Initializes the global logger with the built env logger.
492    ///
493    /// This should be called early in the execution of a Rust program. Any log
494    /// events that occur before initialization will be ignored.
495    ///
496    /// # Errors
497    ///
498    /// This function will fail if it is called more than once, or if another
499    /// library has already initialized a global logger.
500    pub fn try_init(&mut self) -> Result<(), SetLoggerError> {
501        let logger = self.build();
502
503        let max_level = logger.filter();
504        let r = log::set_boxed_logger(Box::new(logger));
505
506        if r.is_ok() {
507            log::set_max_level(max_level);
508        }
509
510        r
511    }
512
513    /// Initializes the global logger with the built env logger.
514    ///
515    /// This should be called early in the execution of a Rust program. Any log
516    /// events that occur before initialization will be ignored.
517    ///
518    /// # Panics
519    ///
520    /// This function will panic if it is called more than once, or if another
521    /// library has already initialized a global logger.
522    pub fn init(&mut self) {
523        self.try_init()
524            .expect("Builder::init should not be called after logger initialized");
525    }
526
527    /// Build an env logger.
528    ///
529    /// The returned logger implements the `Log` trait and can be installed manually
530    /// or nested within another logger.
531    pub fn build(&mut self) -> Logger {
532        assert!(!self.built, "attempt to re-use consumed builder");
533        self.built = true;
534
535        Logger {
536            writer: self.writer.build(),
537            filter: self.filter.build(),
538            format: self.format.build(),
539        }
540    }
541}
542
543impl std::fmt::Debug for Builder {
544    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
545        if self.built {
546            f.debug_struct("Logger").field("built", &true).finish()
547        } else {
548            f.debug_struct("Logger")
549                .field("filter", &self.filter)
550                .field("writer", &self.writer)
551                .finish()
552        }
553    }
554}
555
556/// The env logger.
557///
558/// This struct implements the `Log` trait from the [`log` crate][log-crate-url],
559/// which allows it to act as a logger.
560///
561/// The [`init()`], [`try_init()`], [`Builder::init()`] and [`Builder::try_init()`]
562/// methods will each construct a `Logger` and immediately initialize it as the
563/// default global logger.
564///
565/// If you'd instead need access to the constructed `Logger`, you can use
566/// the associated [`Builder`] and install it with the
567/// [`log` crate][log-crate-url] directly.
568///
569/// [log-crate-url]: https://docs.rs/log
570/// [`init()`]: fn.init.html
571/// [`try_init()`]: fn.try_init.html
572/// [`Builder::init()`]: struct.Builder.html#method.init
573/// [`Builder::try_init()`]: struct.Builder.html#method.try_init
574/// [`Builder`]: struct.Builder.html
575pub struct Logger {
576    writer: Writer,
577    filter: crate::filter::Filter,
578    format: FormatFn,
579}
580
581impl Logger {
582    /// Creates the logger from the environment.
583    ///
584    /// The variables used to read configuration from can be tweaked before
585    /// passing in.
586    ///
587    /// # Examples
588    ///
589    /// Create a logger reading the log filter from an environment variable
590    /// called `MY_LOG`:
591    ///
592    /// ```
593    /// use midenc_log::Logger;
594    ///
595    /// let logger = Logger::from_env("MY_LOG");
596    /// ```
597    ///
598    /// Create a logger using the `MY_LOG` variable for filtering and
599    /// `MY_LOG_STYLE` for whether or not to write styles:
600    ///
601    /// ```
602    /// use midenc_log::{Logger, Env};
603    ///
604    /// let env = Env::new().filter_or("MY_LOG", "info").write_style_or("MY_LOG_STYLE", "always");
605    ///
606    /// let logger = Logger::from_env(env);
607    /// ```
608    pub fn from_env<'a, E>(env: E) -> Self
609    where
610        E: Into<Env<'a>>,
611    {
612        Builder::from_env(env).build()
613    }
614
615    /// Creates the logger from the environment using default variable names.
616    ///
617    /// This method is a convenient way to call `from_env(Env::default())` without
618    /// having to use the `Env` type explicitly. The logger will use the
619    /// [default environment variables].
620    ///
621    /// # Examples
622    ///
623    /// Creates a logger using the default environment variables:
624    ///
625    /// ```
626    /// use midenc_log::Logger;
627    ///
628    /// let logger = Logger::from_default_env();
629    /// ```
630    ///
631    /// [default environment variables]: struct.Env.html#default-environment-variables
632    pub fn from_default_env() -> Self {
633        Builder::from_default_env().build()
634    }
635
636    /// Returns the maximum `LevelFilter` that this env logger instance is
637    /// configured to output.
638    pub fn filter(&self) -> LevelFilter {
639        self.filter.filter()
640    }
641
642    /// Checks if this record matches the configured filter.
643    pub fn matches(&self, record: &Record<'_>) -> bool {
644        self.filter.matches(record)
645    }
646}
647
648impl Log for Logger {
649    fn enabled(&self, metadata: &Metadata<'_>) -> bool {
650        self.filter.enabled(metadata)
651    }
652
653    fn log(&self, record: &Record<'_>) {
654        if self.matches(record) {
655            // Log records are written to a thread-local buffer before being printed
656            // to the terminal. We clear these buffers afterwards, but they aren't shrunk
657            // so will always at least have capacity for the largest log record formatted
658            // on that thread.
659            //
660            // If multiple `Logger`s are used by the same threads then the thread-local
661            // formatter might have different color support. If this is the case the
662            // formatter and its buffer are discarded and recreated.
663
664            thread_local! {
665                static FORMATTER: RefCell<Option<Formatter>> = const { RefCell::new(None) };
666            }
667
668            let print = |formatter: &mut Formatter, record: &Record<'_>| {
669                let _ = self
670                    .format
671                    .format(formatter, record)
672                    .and_then(|_| formatter.print(&self.writer));
673
674                // Always clear the buffer afterwards
675                formatter.clear();
676            };
677
678            let printed = FORMATTER
679                .try_with(|tl_buf| {
680                    if let Ok(mut tl_buf) = tl_buf.try_borrow_mut() {
681                        // There are no active borrows of the buffer
682                        if let Some(ref mut formatter) = *tl_buf {
683                            // We have a previously set formatter
684
685                            // Check the buffer style. If it's different from the logger's
686                            // style then drop the buffer and recreate it.
687                            if formatter.write_style() != self.writer.write_style() {
688                                *formatter = Formatter::new(&self.writer);
689                            }
690
691                            print(formatter, record);
692                        } else {
693                            // We don't have a previously set formatter
694                            let mut formatter = Formatter::new(&self.writer);
695                            print(&mut formatter, record);
696
697                            *tl_buf = Some(formatter);
698                        }
699                    } else {
700                        // There's already an active borrow of the buffer (due to re-entrancy)
701                        print(&mut Formatter::new(&self.writer), record);
702                    }
703                })
704                .is_ok();
705
706            if !printed {
707                // The thread-local storage was not available (because its
708                // destructor has already run). Create a new single-use
709                // Formatter on the stack for this call.
710                print(&mut Formatter::new(&self.writer), record);
711            }
712        }
713    }
714
715    fn flush(&self) {}
716}
717
718impl std::fmt::Debug for Logger {
719    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
720        f.debug_struct("Logger").field("filter", &self.filter).finish()
721    }
722}
723
724/// Set of environment variables to configure from.
725///
726/// # Default environment variables
727///
728/// By default, the `Env` will read the following environment variables:
729///
730/// - `MIDENC_TRACE`: the level filter
731/// - `MIDENC_TRACE_FILTER`: a key-value filter to apply
732/// - `MIDENC_TRACE_STYLE`: whether or not to print styles with records.
733///
734/// These sources can be configured using the builder methods on `Env`.
735#[derive(Debug)]
736pub struct Env<'a> {
737    filter: Var<'a>,
738    kv_filter: Var<'a>,
739    write_style: Var<'a>,
740}
741
742impl<'a> Env<'a> {
743    /// Get a default set of environment variables.
744    pub fn new() -> Self {
745        Self::default()
746    }
747
748    /// Specify an environment variable to read the filter from.
749    pub fn filter<E>(mut self, filter_env: E) -> Self
750    where
751        E: Into<Cow<'a, str>>,
752    {
753        self.filter = Var::new(filter_env);
754
755        self
756    }
757
758    /// Specify an environment variable to read the filter from.
759    ///
760    /// If the variable is not set, the default value will be used.
761    pub fn filter_or<E, V>(mut self, filter_env: E, default: V) -> Self
762    where
763        E: Into<Cow<'a, str>>,
764        V: Into<Cow<'a, str>>,
765    {
766        self.filter = Var::new_with_default(filter_env, default);
767
768        self
769    }
770
771    /// Use the default environment variable to read the filter from.
772    ///
773    /// If the variable is not set, the default value will be used.
774    pub fn default_filter_or<V>(mut self, default: V) -> Self
775    where
776        V: Into<Cow<'a, str>>,
777    {
778        self.filter = Var::new_with_default(DEFAULT_FILTER_ENV, default);
779
780        self
781    }
782
783    fn get_filter(&self) -> Option<String> {
784        self.filter.get()
785    }
786
787    fn get_kv_filter(&self) -> Option<String> {
788        self.kv_filter.get()
789    }
790
791    /// Specify an environment variable to read the style from.
792    pub fn write_style<E>(mut self, write_style_env: E) -> Self
793    where
794        E: Into<Cow<'a, str>>,
795    {
796        self.write_style = Var::new(write_style_env);
797
798        self
799    }
800
801    /// Specify an environment variable to read the style from.
802    ///
803    /// If the variable is not set, the default value will be used.
804    pub fn write_style_or<E, V>(mut self, write_style_env: E, default: V) -> Self
805    where
806        E: Into<Cow<'a, str>>,
807        V: Into<Cow<'a, str>>,
808    {
809        self.write_style = Var::new_with_default(write_style_env, default);
810
811        self
812    }
813
814    /// Use the default environment variable to read the style from.
815    ///
816    /// If the variable is not set, the default value will be used.
817    pub fn default_write_style_or<V>(mut self, default: V) -> Self
818    where
819        V: Into<Cow<'a, str>>,
820    {
821        self.write_style = Var::new_with_default(DEFAULT_WRITE_STYLE_ENV, default);
822
823        self
824    }
825
826    fn get_write_style(&self) -> Option<String> {
827        self.write_style.get()
828    }
829}
830
831impl<'a, T> From<T> for Env<'a>
832where
833    T: Into<Cow<'a, str>>,
834{
835    fn from(filter_env: T) -> Self {
836        Env::default().filter(filter_env.into())
837    }
838}
839
840impl Default for Env<'_> {
841    fn default() -> Self {
842        Env {
843            filter: Var::new(DEFAULT_FILTER_ENV),
844            kv_filter: Var::new(DEFAULT_KV_FILTER_ENV),
845            write_style: Var::new(DEFAULT_WRITE_STYLE_ENV),
846        }
847    }
848}
849
850#[derive(Debug)]
851struct Var<'a> {
852    name: Cow<'a, str>,
853    default: Option<Cow<'a, str>>,
854}
855
856impl<'a> Var<'a> {
857    fn new<E>(name: E) -> Self
858    where
859        E: Into<Cow<'a, str>>,
860    {
861        Var {
862            name: name.into(),
863            default: None,
864        }
865    }
866
867    fn new_with_default<E, V>(name: E, default: V) -> Self
868    where
869        E: Into<Cow<'a, str>>,
870        V: Into<Cow<'a, str>>,
871    {
872        Var {
873            name: name.into(),
874            default: Some(default.into()),
875        }
876    }
877
878    fn get(&self) -> Option<String> {
879        env::var(&*self.name)
880            .ok()
881            .or_else(|| self.default.clone().map(|v| v.into_owned()))
882    }
883}
884
885/// Attempts to initialize the global logger with an env logger.
886///
887/// This should be called early in the execution of a Rust program. Any log
888/// events that occur before initialization will be ignored.
889///
890/// # Errors
891///
892/// This function will fail if it is called more than once, or if another
893/// library has already initialized a global logger.
894pub fn try_init() -> Result<(), SetLoggerError> {
895    try_init_from_env(Env::default())
896}
897
898/// Initializes the global logger with an env logger.
899///
900/// This should be called early in the execution of a Rust program. Any log
901/// events that occur before initialization will be ignored.
902///
903/// # Panics
904///
905/// This function will panic if it is called more than once, or if another
906/// library has already initialized a global logger.
907pub fn init() {
908    try_init().expect("midenc_log::init should not be called after logger initialized");
909}
910
911/// Attempts to initialize the global logger with an env logger from the given
912/// environment variables.
913///
914/// This should be called early in the execution of a Rust program. Any log
915/// events that occur before initialization will be ignored.
916///
917/// # Examples
918///
919/// Initialise a logger using the `MY_LOG` environment variable for filters
920/// and `MY_LOG_STYLE` for writing colors:
921///
922/// ```
923/// use midenc_log::{Builder, Env};
924///
925/// # fn run() -> Result<(), Box<dyn ::std::error::Error>> {
926/// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE");
927///
928/// midenc_log::try_init_from_env(env)?;
929///
930/// Ok(())
931/// # }
932/// # run().unwrap();
933/// ```
934///
935/// # Errors
936///
937/// This function will fail if it is called more than once, or if another
938/// library has already initialized a global logger.
939pub fn try_init_from_env<'a, E>(env: E) -> Result<(), SetLoggerError>
940where
941    E: Into<Env<'a>>,
942{
943    let mut builder = Builder::from_env(env);
944
945    builder.try_init()
946}
947
948/// Initializes the global logger with an env logger from the given environment
949/// variables.
950///
951/// This should be called early in the execution of a Rust program. Any log
952/// events that occur before initialization will be ignored.
953///
954/// # Examples
955///
956/// Initialise a logger using the `MY_LOG` environment variable for filters
957/// and `MY_LOG_STYLE` for writing colors:
958///
959/// ```
960/// use midenc_log::{Builder, Env};
961///
962/// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE");
963///
964/// midenc_log::init_from_env(env);
965/// ```
966///
967/// # Panics
968///
969/// This function will panic if it is called more than once, or if another
970/// library has already initialized a global logger.
971pub fn init_from_env<'a, E>(env: E)
972where
973    E: Into<Env<'a>>,
974{
975    try_init_from_env(env)
976        .expect("midenc_log::init_from_env should not be called after logger initialized");
977}
978
979/// Create a new builder with the default environment variables.
980///
981/// The builder can be configured before being initialized.
982/// This is a convenient way of calling [`Builder::from_default_env`].
983///
984/// [`Builder::from_default_env`]: struct.Builder.html#method.from_default_env
985pub fn builder() -> Builder {
986    Builder::from_default_env()
987}
988
989#[cfg(test)]
990mod tests {
991    use super::*;
992
993    #[test]
994    fn env_get_filter_reads_from_var_if_set() {
995        unsafe {
996            env::set_var("env_get_filter_reads_from_var_if_set", "from var");
997        }
998
999        let env = Env::new().filter_or("env_get_filter_reads_from_var_if_set", "from default");
1000
1001        assert_eq!(Some("from var".to_owned()), env.get_filter());
1002    }
1003
1004    #[test]
1005    fn env_get_filter_reads_from_default_if_var_not_set() {
1006        unsafe {
1007            env::remove_var("env_get_filter_reads_from_default_if_var_not_set");
1008        }
1009
1010        let env = Env::new()
1011            .filter_or("env_get_filter_reads_from_default_if_var_not_set", "from default");
1012
1013        assert_eq!(Some("from default".to_owned()), env.get_filter());
1014    }
1015
1016    #[test]
1017    fn env_get_write_style_reads_from_var_if_set() {
1018        unsafe {
1019            env::set_var("env_get_write_style_reads_from_var_if_set", "from var");
1020        }
1021
1022        let env =
1023            Env::new().write_style_or("env_get_write_style_reads_from_var_if_set", "from default");
1024
1025        assert_eq!(Some("from var".to_owned()), env.get_write_style());
1026    }
1027
1028    #[test]
1029    fn env_get_write_style_reads_from_default_if_var_not_set() {
1030        unsafe {
1031            env::remove_var("env_get_write_style_reads_from_default_if_var_not_set");
1032        }
1033
1034        let env = Env::new().write_style_or(
1035            "env_get_write_style_reads_from_default_if_var_not_set",
1036            "from default",
1037        );
1038
1039        assert_eq!(Some("from default".to_owned()), env.get_write_style());
1040    }
1041
1042    #[test]
1043    fn builder_parse_env_overrides_existing_filters() {
1044        unsafe {
1045            env::set_var("builder_parse_default_env_overrides_existing_filters", "debug");
1046        }
1047        let env = Env::new().filter("builder_parse_default_env_overrides_existing_filters");
1048
1049        let mut builder = Builder::new();
1050        builder.filter_level(LevelFilter::Trace);
1051        // Overrides global level to debug
1052        builder.parse_env(env);
1053
1054        assert_eq!(builder.filter.build().filter(), LevelFilter::Debug);
1055    }
1056}