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}