spdlog/
lib.rs

1//! Fast, highly configurable Rust logging crate.
2//!
3//! It is inspired by the C++ logging library [spdlog], and we share most of the
4//! same concepts. So if you are already familiar with C++ `spdlog`, you should
5//! be able to get started with this crate quite easily. Of course, there are
6//! some differences, you can see [Significant differences from C++
7//! spdlog](#significant-differences-from-c-spdlog) below.
8//!
9//! # Table of contents
10//!
11//! - [Getting started](#getting-started)
12//! - [Compile-time filters](#compile-time-filters)
13//! - [Crate feature flags](#crate-feature-flags)
14//! - [Supported Rust versions](#supported-rust-versions)
15//! - Overview of features
16//!   - [Configured via environment variable](init_env_level)
17//!   - [Compile-time and runtime pattern formatter]
18//!   - [Asynchronous support]
19//!   - [Compatible with log crate](LogCrateProxy)
20//!
21//! [Compile-time and runtime pattern formatter]: formatter/index.html#compile-time-and-runtime-pattern-formatter
22//! [Asynchronous support]: crate::sink::AsyncPoolSink
23//!
24//! # Getting started
25//!
26//! Add this to `Cargo.toml`:
27//! ```toml
28//! [dependencies]
29//! spdlog-rs = "0.4"
30//! ```
31//!
32//! `spdlog-rs` is highly configurable, and also works out-of-the-box for
33//! lightweight projects. By default, logs will be output to `stdout` and
34//! `stderr`.
35//!
36//! ```
37//! use spdlog::prelude::*;
38//!
39//! // Non-severe logs (trace, debug) are ignored by default.
40//! // If you wish to enable all logs, call
41//! spdlog::default_logger().set_level_filter(spdlog::LevelFilter::All);
42//!
43//! info!("hello, world!");
44//! error!("oops!");
45//! debug!("3 + 2 = {}", 5);
46//! ```
47//!
48//! Output:
49//!
50//! <pre>
51//! [2022-11-02 09:23:12.263] [<font color="#0DBC79">info</font>] hello, world!
52//! [2022-11-02 09:23:12.263] [<font color="#F35E5E">error</font>] oops!
53//! [2022-11-02 09:23:12.263] [<font color="#11A8CD">debug</font>] 3 + 2 = 5
54//! </pre>
55//!
56//! The basic use is through these logging macros: [`trace!`], [`debug!`],
57//! [`info!`], [`warn!`], [`error!`], [`critical!`], where `critical!`
58//! represents the most severe logs and `trace!` the most verbose. Each of these
59//! macros accept format strings similarly to [`println!`]. All log macros
60//! and common types are already under [`prelude`] module.
61//!
62//! ## Sink
63//!
64//! Many real programs want more than just displaying logs to the terminal.
65//!
66//! [`Sink`]s are the objects that actually write logs to their targets. If you
67//! want logs to be written to files as well, [`FileSink`] is what you need.
68//!
69//! ```
70//! # use std::sync::Arc;
71//! use spdlog::{prelude::*, sink::FileSink};
72//!
73//! # fn main() -> spdlog::Result<()> {
74//! let path = "path/to/somewhere.log";
75//!
76//! # let path = concat!(env!("OUT_DIR"), "/doctest-out/crate-1.txt");
77//! let new_logger = spdlog::default_logger().fork_with(|new| {
78//!     let file_sink = Arc::new(FileSink::builder().path(path).build()?);
79//!     new.sinks_mut().push(file_sink);
80//!     Ok(())
81//! })?;
82//! # let backup = spdlog::default_logger();
83//! spdlog::set_default_logger(new_logger);
84//!
85//! info!("from now on, logs will be written to both stdout/stderr and the file");
86//! # spdlog::set_default_logger(backup);
87//! # Ok(()) }
88//! ```
89//!
90//! Take a look at [`sink`] module for more interesting sinks, such as
91//! [`RotatingFileSink`] that automatically rotates files by time point or file
92//! size, and [`AsyncPoolSink`] that outputs logs asynchronously.
93//!
94//! ## Logger
95//!
96//! A complex program may consist of many separated components.
97//!
98//! [`Logger`] manages, controls and manipulates multiple sinks within it. In
99//! addition to having the global [`default_logger`], more loggers are allowed
100//! to be configured, stored and used independently.
101//!
102//! Logging macros provide an optional parameter `logger`. If it is specified,
103//! logs will be processed by the specified logger instead of the global default
104//! logger.
105//!
106//! And benefiting from the fact that a logger uses `Arc` to store sinks, a sink
107//! can be set and used by more than one logger, and you can combine them as you
108//! like.
109//!
110//! ```
111//! use spdlog::prelude::*;
112//! # use spdlog::Result;
113//!
114//! struct AppDatabase {
115//!     logger: Logger,
116//!     // Database info...
117//! }
118//!
119//! impl AppDatabase {
120//!     fn new() -> Result<Self> {
121//!         let logger = Logger::builder()
122//!             .name("database")
123//!             // .sink( ... )
124//!             // .sink( ... )
125//!             // .level_filter( ... )
126//!             // ...
127//!             .build()?;
128//!         Ok(Self { logger, /* Database info... */ })
129//!     }
130//!
131//!     fn query<T>(&self) -> T {
132//!         let data = /* Query from the database */
133//!         # 114514;
134//!         trace!(logger: self.logger, "queried data {}", data);
135//!         data
136//!         # ; unreachable!()
137//!     }
138//! }
139//!
140//! struct AppNetwork { /* ... */ }
141//! struct AppAuth { /* ... */ }
142//! struct AppBlahBlah { /* ... */ }
143//! ```
144//!
145//! ## Learn more
146//!
147//! Directory [./examples] contains more advanced usage examples. You can learn
148//! them along with their documentation.
149//!
150//! If you have any trouble while using this crate, please don't hesitate to
151//! [open a discussion] for help. For feature requests or bug reports, please
152//! [open an issue].
153//!
154//! # Compile-time filters
155//!
156//! Log levels can be statically disabled at compile time via Cargo features.
157//! Log invocations at disabled levels will be skipped and will not even be
158//! present in the resulting binary. This level is configured separately for
159//! release and debug builds. The features are:
160//!
161//!  - `level-off`
162//!  - `level-critical`
163//!  - `level-error`
164//!  - `level-warn`
165//!  - `level-info`
166//!  - `level-debug`
167//!  - `level-trace`
168//!  - `release-level-off`
169//!  - `release-level-critical`
170//!  - `release-level-error`
171//!  - `release-level-warn`
172//!  - `release-level-info`
173//!  - `release-level-debug`
174//!  - `release-level-trace`
175//!
176//! These features control the value of the `STATIC_LEVEL_FILTER` constant. The
177//! logging macros check this value before logging a message. By default, no
178//! levels are disabled.
179//!
180//! For example, a crate can disable trace level logs in debug builds and trace,
181//! debug, and info level logs in release builds with
182//! `features = ["level-debug", "release-level-warn"]`.
183//!
184//! # Crate feature flags
185//!
186//! The following crate feature flags are available in addition to the filters.
187//! They are configured in your `Cargo.toml`.
188//!
189//!  - `source-location` allows recording the source location of each log. When
190//!    it is enabled, the source location will be present in [`Record`] and
191//!    visible to [`Formatter`], and formatting patterns related to source
192//!    location will be available. If you do not want the source location
193//!    information to appear in your binary file, you may prefer not to enable
194//!    it.
195//!
196//!  - `flexible-string` improves the performance of formatting records, however
197//!    it contains unsafe code. For more details, see the documentation of
198//!    [`StringBuf`].
199//!
200//!  - `log` enables the compatibility with [log crate].
201//!
202//!  - `native` enables platform-specific components, such as
203//!    [`sink::WinDebugSink`] for Windows, [`sink::JournaldSink`] for Linux,
204//!    etc. Note If the component requires additional system dependencies, then
205//!    more granular features need to be enabled as well.
206//!
207//!  - `runtime-pattern` enables the ability to build patterns with runtime
208//!    template string. See [`RuntimePattern`] for more details.
209//!
210//!  - `serde_json` enables [`formatter::JsonFormatter`].
211//!
212//! # Supported Rust versions
213//!
214//! <!--
215//! When updating this, also update:
216//! - .github/workflows/ci.yml
217//! - Cargo.toml
218//! - README.md
219//! -->
220//!
221//! The current minimum supported Rust version is 1.60.
222//!
223//! `spdlog-rs` is built against the latest Rust stable release, it is not
224//! guaranteed to build on Rust versions earlier than the minimum supported
225//! version.
226//!
227//! `spdlog-rs` follows the compiler support policy that the latest stable
228//! version and the 3 most recent minor versions before that are always
229//! supported. For example, if the current latest Rust stable version is 1.61,
230//! the minimum supported version will not be increased past 1.58. Increasing
231//! the minimum supported version is not considered a semver breaking change as
232//! long as it complies with this policy.
233//!
234//! # Significant differences from C++ spdlog
235//!
236//! The significant differences between `spdlog-rs` and C++ `spdlog`[^1]:
237//!  - `spdlog-rs` does not have `registry`[^2]. You don't need to register for
238//!    loggers.
239//!
240//!  - `spdlog-rs` does not have `backtrace`[^2].
241//!
242//!  - In `spdlog-rs`, [`LevelFilter`] is a more flexible and readable enum with
243//!    logical conditions.
244//!
245//!  - In `spdlog-rs`, there is no "_st" sinks, all sinks are "_mt".
246//!
247//!  - `daily_file_sink` and `hourly_file_sink` in C++ `spdlog` are merged into
248//!    [`RotatingFileSink`] in `spdlog-rs`. They correspond to rotation policies
249//!    [`RotationPolicy::Daily`] and [`RotationPolicy::Hourly`].
250//!
251//!  - `async_logger` in C++ `spdlog` is [`AsyncPoolSink`] in `spdlog-rs`. This
252//!    allows it to be used with synchronous sinks.
253//!
254//!  - Some sinks in C++ `spdlog` are not yet implemented in `spdlog-rs`. (Yes,
255//!    PRs are welcome)
256//!
257//!  - ...
258//!
259//! [^1]: At the time of writing this section, the latest version of C++ `spdlog` is v1.9.2.
260//!
261//! [^2]: C++ `spdlog` is also planned to remove it in v2.x.
262//!
263//! [spdlog]: http://github.com/gabime/spdlog
264//! [`FileSink`]: crate::sink::FileSink
265//! [`RotatingFileSink`]: crate::sink::RotatingFileSink
266//! [`AsyncPoolSink`]: crate::sink::AsyncPoolSink
267//! [./examples]: http://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples
268//! [open a discussion]: http://github.com/SpriteOvO/spdlog-rs/discussions/new
269//! [open an issue]: http://github.com/SpriteOvO/spdlog-rs/issues/new/choose
270//! [log crate]: http://crates.io/crates/log
271//! [`Formatter`]: crate::formatter::Formatter
272//! [`RuntimePattern`]: crate::formatter::RuntimePattern
273//! [`RotationPolicy::Daily`]: crate::sink::RotationPolicy::Daily
274//! [`RotationPolicy::Hourly`]: crate::sink::RotationPolicy::Hourly
275
276#![allow(unexpected_cfgs)]
277// Credits: http://blog.wnut.pw/2020/03/24/documentation-and-unstable-rustdoc-features/
278#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
279#![warn(missing_docs)]
280
281mod env_level;
282pub mod error;
283pub mod formatter;
284mod level;
285#[cfg(feature = "log")]
286mod log_crate_proxy;
287mod log_macros;
288mod logger;
289mod periodic_worker;
290pub mod re_export;
291mod record;
292pub mod sink;
293mod source_location;
294mod string_buf;
295mod sync;
296pub mod terminal_style;
297#[cfg(test)]
298mod test_utils;
299#[cfg(feature = "multi-thread")]
300mod thread_pool;
301mod utils;
302
303pub use error::{Error, ErrorHandler, Result};
304pub use level::*;
305#[cfg(feature = "log")]
306pub use log_crate_proxy::*;
307pub use logger::*;
308pub use record::*;
309pub use source_location::*;
310pub use string_buf::StringBuf;
311#[cfg(feature = "multi-thread")]
312pub use thread_pool::*;
313
314/// Contains all log macros and common types.
315pub mod prelude {
316    pub use super::{
317        critical, debug, error, info, log, trace, warn, Level, LevelFilter, Logger, LoggerBuilder,
318    };
319}
320
321use std::{
322    borrow::Cow,
323    env::{self, VarError},
324    ffi::OsStr,
325    fmt,
326    io::{self, Write},
327    panic,
328    result::Result as StdResult,
329};
330
331use cfg_if::cfg_if;
332use error::EnvLevelError;
333use sink::{Sink, StdStreamSink};
334use sync::*;
335
336/// The statically resolved log level filter.
337///
338/// See the crate level documentation for information on how to configure this.
339///
340/// This value is checked by the log macros, but not by [`Logger`]s and
341/// [`Sink`]s. Code that manually calls functions on these should test the level
342/// against this value.
343///
344/// [`Logger`]: crate::logger::Logger
345/// [`Sink`]: crate::sink::Sink
346pub const STATIC_LEVEL_FILTER: LevelFilter = STATIC_LEVEL_FILTER_INNER;
347
348cfg_if! {
349    if #[cfg(all(not(debug_assertions), feature = "release-level-off"))] {
350        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::Off;
351    } else if #[cfg(all(not(debug_assertions), feature = "release-level-critical"))] {
352        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Critical);
353    } else if #[cfg(all(not(debug_assertions), feature = "release-level-error"))] {
354        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Error);
355    } else if #[cfg(all(not(debug_assertions), feature = "release-level-warn"))] {
356        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Warn);
357    } else if #[cfg(all(not(debug_assertions), feature = "release-level-info"))] {
358        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Info);
359    } else if #[cfg(all(not(debug_assertions), feature = "release-level-debug"))] {
360        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Debug);
361    } else if #[cfg(all(not(debug_assertions), feature = "release-level-trace"))] {
362        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Trace);
363    } else if #[cfg(feature = "level-off")] {
364        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::Off;
365    } else if #[cfg(feature = "level-critical")] {
366        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Critical);
367    } else if #[cfg(feature = "level-error")] {
368        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Error);
369    } else if #[cfg(feature = "level-warn")] {
370        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Warn);
371    } else if #[cfg(feature = "level-info")] {
372        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Info);
373    } else if #[cfg(feature = "level-debug")] {
374        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Debug);
375    } else {
376        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Trace);
377    }
378}
379
380#[cfg(not(windows))]
381#[doc(hidden)]
382pub const __EOL: &str = "\n";
383#[cfg(windows)]
384#[doc(hidden)]
385pub const __EOL: &str = "\r\n";
386
387static DEFAULT_LOGGER: OnceCell<ArcSwap<Logger>> = OnceCell::new();
388
389#[must_use]
390fn default_logger_ref() -> &'static ArcSwap<Logger> {
391    DEFAULT_LOGGER.get_or_init(|| {
392        let stdout = StdStreamSink::builder()
393            .stdout()
394            .level_filter(LevelFilter::MoreVerbose(Level::Warn))
395            .build()
396            .unwrap();
397
398        let stderr = StdStreamSink::builder()
399            .stderr()
400            .level_filter(LevelFilter::MoreSevereEqual(Level::Warn))
401            .build()
402            .unwrap();
403
404        let sinks: [Arc<dyn Sink>; 2] = [Arc::new(stdout), Arc::new(stderr)];
405
406        let res = ArcSwap::from_pointee(Logger::builder().sinks(sinks).build_default().unwrap());
407
408        flush_default_logger_at_exit();
409        res
410    })
411}
412
413/// Returns the global default logger.
414///
415/// This default logger will be used by logging macros, if the `logger`
416/// parameter is not specified when logging macros are called.
417///
418/// If the default logger has not been replaced, the default:
419///
420///  - contains a sink [`StdStreamSink`], writing logs on [`Level::Info`] and
421///    more verbose levels to `stdout`.
422///
423///  - contains a sink [`StdStreamSink`], writing logs on [`Level::Warn`] level
424///    and more severe levels to `stderr`.
425///
426///  - level filter ignores logs on [`Level::Debug`] and more verbose levels.
427///
428///    However, if you want to enable logging for all levels:
429///    ```
430///    use spdlog::prelude::*;
431///
432///    spdlog::default_logger().set_level_filter(LevelFilter::All);
433///    ```
434///
435/// Users can replace the default logger with [`set_default_logger`] or
436/// [`swap_default_logger`].
437///
438/// # Examples
439///
440/// ```
441/// # use std::sync::Arc;
442/// use spdlog::prelude::*;
443///
444/// let default_logger = spdlog::default_logger();
445///
446/// default_logger.set_level_filter(LevelFilter::All);
447///
448/// info!("this log will be written to `stdout`");
449/// debug!("this log will be written to `stdout`");
450/// trace!("this log will be written to `stdout`");
451///
452/// warn!("this log will be written to `stderr`");
453/// error!("this log will be written to `stderr`");
454/// critical!("this log will be written to `stderr`");
455/// ```
456#[must_use]
457pub fn default_logger() -> Arc<Logger> {
458    default_logger_ref().load().clone()
459}
460
461/// Sets the given logger as the new global default logger, and returns the old
462/// one.
463///
464/// # Examples
465///
466/// ```
467/// # use std::sync::Arc;
468/// use spdlog::prelude::*;
469///
470/// let new_logger: Arc<Logger> = /* ... */
471/// # spdlog::default_logger();
472/// let old_logger = spdlog::swap_default_logger(new_logger);
473///
474/// info!("this log will be handled by `new_logger`");
475/// info!(logger: old_logger, "this log will be handled by `old_logger`");
476/// ```
477pub fn swap_default_logger(logger: Arc<Logger>) -> Arc<Logger> {
478    default_logger_ref().swap(logger)
479}
480
481/// Sets the given logger as the new global default logger.
482///
483/// # Examples
484///
485/// ```
486/// # use std::sync::Arc;
487/// use spdlog::prelude::*;
488///
489/// # let new_logger = spdlog::default_logger();
490/// spdlog::set_default_logger(new_logger);
491///
492/// info!("this log will be handled by `new_logger`");
493/// ```
494pub fn set_default_logger(logger: Arc<Logger>) {
495    swap_default_logger(logger);
496}
497
498/// Initializes environment variable level filters from environment variable
499/// `SPDLOG_RS_LEVEL`.
500///
501/// Returns whether the level in the environment variable was applied if there
502/// are no errors.
503///
504/// The default level filter of loggers built after calling this function will
505/// be configured based on the value of environment variable `SPDLOG_RS_LEVEL`.
506///
507/// If you want to read from a custom environment variable, see
508/// [`init_env_level_from`].
509///
510/// Users should call this function early, the level filter of loggers built
511/// before calling this function will not be configured by environment variable.
512///
513/// ## Formats of the environment variable value
514///
515/// The levels contained in the environment variable mean
516/// `LevelFilter::MoreSevereEqual(level)`.
517///
518/// ---
519///
520/// - Specifies the level filter for ***the default logger***.
521///
522///   Possible inputs: `off`, `trace`, `warn`, `all`, etc.
523///
524/// ---
525///
526/// - Specifies the level filter for ***unnamed loggers***.
527///
528///   Possible inputs: `=off`, `=info`, `=error`, `=all`, etc.
529///
530/// ---
531///
532/// - Specifies the level filter for ***loggers with the specified name***.
533///
534///   Possible inputs: `logger-name=info`, `network=warn`, `core=info`,
535///   `gui=critical`, etc.
536///
537/// ---
538///
539/// - Specifies the level filter for ***all loggers except the default logger***
540///   (respect the above rules first if they are matched).
541///
542///   Possible inputs: `*=error`, `*=off`, `*=critical`, etc.
543///
544/// ---
545///
546/// The levels are not case-sensitive, and these rules are combinable, separated
547/// by commas.
548///
549/// For example, these are legal:
550///
551/// ---
552///
553/// - `ALL,*=ALL`
554///
555///   Specifies the level filter for all loggers as `LevelFilter::All`.
556///
557/// ---
558///
559/// - `off,*=ERROR`
560///
561///   Specifies the level filter for the default logger as `LevelFilter::Off`,
562///   the rest of loggers as `LevelFilter::MoreSevereEqual(Level::Error)`.
563///
564/// ---
565///
566/// - `gui=warn,network=trace`
567///
568///   Specifies the level filter for loggers with name "gui" as
569///   `LevelFilter::MoreSevereEqual(Level::Warn)`, loggers with name "network"
570///   as `LevelFilter::MoreSevereEqual(Level::Trace)`.
571///
572/// ---
573///
574/// However, the same rule cannot be specified more than once.
575///
576/// # Examples
577///
578/// - Environment variable `SPDLOG_RS_LEVEL` is not present:
579///
580///   ```
581///   use spdlog::prelude::*;
582///
583///   # fn main() -> Result<(), Box<dyn std::error::Error>> {
584///   assert_eq!(spdlog::init_env_level()?, false);
585///
586///   assert_eq!(
587///       spdlog::default_logger().level_filter(),
588///       LevelFilter::MoreSevereEqual(Level::Info) // default level filter
589///   );
590///   assert_eq!(
591///       Logger::builder().build()?.level_filter(), // unnamed logger
592///       LevelFilter::MoreSevereEqual(Level::Info) // default level filter
593///   );
594///   assert_eq!(
595///       Logger::builder().name("gui").build()?.level_filter(),
596///       LevelFilter::MoreSevereEqual(Level::Info) // default level filter
597///   );
598///   assert_eq!(
599///       Logger::builder().name("network").build()?.level_filter(),
600///       LevelFilter::MoreSevereEqual(Level::Info) // default level filter
601///   );
602///   # Ok(()) }
603///   ```
604///
605/// ---
606///
607/// - `SPDLOG_RS_LEVEL="TRACE,network=Warn,*=error"`:
608///
609///   ```
610///   use spdlog::prelude::*;
611///
612///   # fn main() -> Result<(), Box<dyn std::error::Error>> {
613///   # std::env::set_var("SPDLOG_RS_LEVEL", "TRACE,network=Warn,*=error");
614///   assert_eq!(spdlog::init_env_level()?, true);
615///
616///   assert_eq!(
617///       spdlog::default_logger().level_filter(),
618///       LevelFilter::MoreSevereEqual(Level::Trace)
619///   );
620///   assert_eq!(
621///       Logger::builder().build()?.level_filter(), // unnamed logger
622///       LevelFilter::MoreSevereEqual(Level::Error)
623///   );
624///   assert_eq!(
625///       Logger::builder().name("gui").build()?.level_filter(),
626///       LevelFilter::MoreSevereEqual(Level::Error)
627///   );
628///   assert_eq!(
629///       Logger::builder().name("network").build()?.level_filter(),
630///       LevelFilter::MoreSevereEqual(Level::Warn)
631///   );
632///   # Ok(()) }
633///   ```
634///
635/// ---
636///
637/// - `SPDLOG_RS_LEVEL="network=Warn,network=Warn"` will fail, as the same rule
638///   is specified multiple times.
639///
640///   ```
641///   # std::env::set_var("SPDLOG_RS_LEVEL", "network=Warn,network=Warn");
642///   assert!(matches!(
643///       spdlog::init_env_level(),
644///       Err(spdlog::error::EnvLevelError::ParseEnvVar(_))
645///   ));
646///   ```
647pub fn init_env_level() -> StdResult<bool, EnvLevelError> {
648    init_env_level_from("SPDLOG_RS_LEVEL")
649}
650
651/// Initializes environment variable level filters from a specified environment
652/// variable.
653///
654/// For more information, see [`init_env_level`].
655///
656/// # Examples
657///
658/// - `MY_APP_LOG_LEVEL="TRACE,network=Warn,*=error"`:
659///
660///   ```
661///   use spdlog::prelude::*;
662///
663///   # fn main() -> Result<(), Box<dyn std::error::Error>> {
664///   # std::env::set_var("MY_APP_LOG_LEVEL", "TRACE,network=Warn,*=error");
665///   assert_eq!(spdlog::init_env_level_from("MY_APP_LOG_LEVEL")?, true);
666///
667///   assert_eq!(
668///       spdlog::default_logger().level_filter(),
669///       LevelFilter::MoreSevereEqual(Level::Trace)
670///   );
671///   assert_eq!(
672///       Logger::builder().build()?.level_filter(), // unnamed logger
673///       LevelFilter::MoreSevereEqual(Level::Error)
674///   );
675///   assert_eq!(
676///       Logger::builder().name("gui").build()?.level_filter(),
677///       LevelFilter::MoreSevereEqual(Level::Error)
678///   );
679///   assert_eq!(
680///       Logger::builder().name("network").build()?.level_filter(),
681///       LevelFilter::MoreSevereEqual(Level::Warn)
682///   );
683///   # Ok(()) }
684///   ```
685///
686/// For more examples, see [`init_env_level`].
687pub fn init_env_level_from<K: AsRef<OsStr>>(env_key: K) -> StdResult<bool, EnvLevelError> {
688    let var = match env::var(env_key.as_ref()) {
689        Err(VarError::NotPresent) => return Ok(false),
690        Err(err) => return Err(EnvLevelError::FetchEnvVar(err)),
691        Ok(var) => var,
692    };
693    env_level::from_str(&var)?;
694    Ok(true)
695}
696
697/// Initializes the log crate proxy.
698///
699/// This function calls [`log::set_logger`] to set up a [`LogCrateProxy`] and
700/// all logs from log crate will be forwarded to `spdlog-rs`'s logger.
701///
702/// Users should call this function only once, and then configure the proxy by
703/// calling [`log_crate_proxy()`].
704///
705/// Note that the `log` crate uses a different log level filter and by default
706/// it rejects all log messages. To log messages via the `log` crate, you have
707/// to call [`log::set_max_level`] manually before logging. For more
708/// information, please read the upstream documentation of
709/// [`log::set_max_level`].
710#[cfg(feature = "log")]
711pub fn init_log_crate_proxy() -> StdResult<(), re_export::log::SetLoggerError> {
712    log::set_logger(log_crate_proxy())
713}
714
715/// Returns the global instance of log crate proxy.
716#[cfg(feature = "log")]
717#[must_use]
718pub fn log_crate_proxy() -> &'static LogCrateProxy {
719    static PROXY: Lazy<LogCrateProxy> = Lazy::new(LogCrateProxy::new);
720    &PROXY
721}
722
723static IS_TEARING_DOWN: AtomicBool = AtomicBool::new(false);
724
725fn flush_default_logger_at_exit() {
726    // Rust never calls `drop` for static variables.
727    //
728    // Setting up an exit handler gives us a chance to flush the default logger
729    // once at the program exit, thus we don't lose the last logs.
730
731    extern "C" fn handler() {
732        IS_TEARING_DOWN.store(true, Ordering::SeqCst);
733        if let Some(default_logger) = DEFAULT_LOGGER.get() {
734            default_logger.load().flush()
735        }
736    }
737
738    #[must_use]
739    fn try_atexit() -> bool {
740        use std::os::raw::c_int;
741
742        extern "C" {
743            fn atexit(cb: extern "C" fn()) -> c_int;
744        }
745
746        (unsafe { atexit(handler) }) == 0
747    }
748
749    fn hook_panic() {
750        let previous_hook = panic::take_hook();
751
752        panic::set_hook(Box::new(move |info| {
753            handler();
754            previous_hook(info);
755        }));
756    }
757
758    if !try_atexit() {
759        hook_panic() // at least
760    }
761}
762
763fn default_error_handler(from: impl AsRef<str>, error: Error) {
764    if let Error::Multiple(errs) = error {
765        errs.into_iter()
766            .for_each(|err| default_error_handler(from.as_ref(), err));
767        return;
768    }
769
770    let date = chrono::Local::now()
771        .format("%Y-%m-%d %H:%M:%S.%3f")
772        .to_string();
773
774    // http://github.com/SpriteOvO/spdlog-rs/discussions/87
775    //
776    // Don't use `eprintln!` here, as it may fail to write and then panic.
777    let _ = writeln!(
778        io::stderr(),
779        "[*** SPDLOG-RS UNHANDLED ERROR ***] [{}] [{}] {}",
780        date,
781        from.as_ref(),
782        error
783    );
784}
785
786// Used at log macros
787#[doc(hidden)]
788pub fn __log(
789    logger: &Logger,
790    level: Level,
791    srcloc: Option<SourceLocation>,
792    fmt_args: fmt::Arguments,
793) {
794    // Use `Cow` to avoid allocation as much as we can
795    let payload: Cow<str> = fmt_args
796        .as_str()
797        .map(Cow::Borrowed) // No format arguments, so it is a `&'static str`
798        .unwrap_or_else(|| Cow::Owned(fmt_args.to_string()));
799    let record = Record::new(level, payload, srcloc, logger.name());
800    logger.log(&record);
801}
802
803#[cfg(test)]
804mod tests {
805    use test_utils::*;
806
807    use super::*;
808
809    #[test]
810    fn test_default_logger() {
811        let test_sink = Arc::new(TestSink::new());
812
813        let test_logger = Arc::new(build_test_logger(|b| b.sink(test_sink.clone())));
814        let empty_logger = Arc::new(Logger::builder().build().unwrap());
815
816        set_default_logger(empty_logger.clone());
817        info!("hello");
818        error!("world");
819
820        set_default_logger(test_logger);
821        warn!("hello");
822        error!("rust");
823
824        set_default_logger(empty_logger);
825        info!("hello");
826        error!("spdlog");
827
828        assert_eq!(test_sink.log_count(), 2);
829        assert_eq!(
830            test_sink.payloads(),
831            vec!["hello".to_string(), "rust".to_string()]
832        );
833    }
834}