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]: https://github.com/gabime/spdlog
264//! [`FileSink`]: crate::sink::FileSink
265//! [`RotatingFileSink`]: crate::sink::RotatingFileSink
266//! [`AsyncPoolSink`]: crate::sink::AsyncPoolSink
267//! [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples
268//! [open a discussion]: https://github.com/SpriteOvO/spdlog-rs/discussions/new
269//! [open an issue]: https://github.com/SpriteOvO/spdlog-rs/issues/new/choose
270//! [log crate]: https://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: https://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, panic,
326    result::Result as StdResult,
327};
328
329use cfg_if::cfg_if;
330use error::EnvLevelError;
331use sink::{Sink, StdStreamSink};
332use sync::*;
333
334/// The statically resolved log level filter.
335///
336/// See the crate level documentation for information on how to configure this.
337///
338/// This value is checked by the log macros, but not by [`Logger`]s and
339/// [`Sink`]s. Code that manually calls functions on these should test the level
340/// against this value.
341///
342/// [`Logger`]: crate::logger::Logger
343/// [`Sink`]: crate::sink::Sink
344pub const STATIC_LEVEL_FILTER: LevelFilter = STATIC_LEVEL_FILTER_INNER;
345
346cfg_if! {
347    if #[cfg(all(not(debug_assertions), feature = "release-level-off"))] {
348        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::Off;
349    } else if #[cfg(all(not(debug_assertions), feature = "release-level-critical"))] {
350        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Critical);
351    } else if #[cfg(all(not(debug_assertions), feature = "release-level-error"))] {
352        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Error);
353    } else if #[cfg(all(not(debug_assertions), feature = "release-level-warn"))] {
354        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Warn);
355    } else if #[cfg(all(not(debug_assertions), feature = "release-level-info"))] {
356        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Info);
357    } else if #[cfg(all(not(debug_assertions), feature = "release-level-debug"))] {
358        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Debug);
359    } else if #[cfg(all(not(debug_assertions), feature = "release-level-trace"))] {
360        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Trace);
361    } else if #[cfg(feature = "level-off")] {
362        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::Off;
363    } else if #[cfg(feature = "level-critical")] {
364        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Critical);
365    } else if #[cfg(feature = "level-error")] {
366        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Error);
367    } else if #[cfg(feature = "level-warn")] {
368        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Warn);
369    } else if #[cfg(feature = "level-info")] {
370        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Info);
371    } else if #[cfg(feature = "level-debug")] {
372        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Debug);
373    } else {
374        const STATIC_LEVEL_FILTER_INNER: LevelFilter = LevelFilter::MoreSevereEqual(Level::Trace);
375    }
376}
377
378#[cfg(not(windows))]
379#[doc(hidden)]
380pub const __EOL: &str = "\n";
381#[cfg(windows)]
382#[doc(hidden)]
383pub const __EOL: &str = "\r\n";
384
385static DEFAULT_LOGGER: OnceCell<ArcSwap<Logger>> = OnceCell::new();
386
387#[must_use]
388fn default_logger_ref() -> &'static ArcSwap<Logger> {
389    DEFAULT_LOGGER.get_or_init(|| {
390        let stdout = StdStreamSink::builder()
391            .stdout()
392            .level_filter(LevelFilter::MoreVerbose(Level::Warn))
393            .build()
394            .unwrap();
395
396        let stderr = StdStreamSink::builder()
397            .stderr()
398            .level_filter(LevelFilter::MoreSevereEqual(Level::Warn))
399            .build()
400            .unwrap();
401
402        let sinks: [Arc<dyn Sink>; 2] = [Arc::new(stdout), Arc::new(stderr)];
403
404        let res = ArcSwap::from_pointee(Logger::builder().sinks(sinks).build_default().unwrap());
405
406        flush_default_logger_at_exit();
407        res
408    })
409}
410
411/// Returns the global default logger.
412///
413/// This default logger will be used by logging macros, if the `logger`
414/// parameter is not specified when logging macros are called.
415///
416/// If the default logger has not been replaced, the default:
417///
418///  - contains a sink [`StdStreamSink`], writing logs on [`Level::Info`] and
419///    more verbose levels to `stdout`.
420///
421///  - contains a sink [`StdStreamSink`], writing logs on [`Level::Warn`] level
422///    and more severe levels to `stderr`.
423///
424///  - level filter ignores logs on [`Level::Debug`] and more verbose levels.
425///
426///    However, if you want to enable logging for all levels:
427///    ```
428///    use spdlog::prelude::*;
429///
430///    spdlog::default_logger().set_level_filter(LevelFilter::All);
431///    ```
432///
433/// Users can replace the default logger with [`set_default_logger`] or
434/// [`swap_default_logger`].
435///
436/// # Examples
437///
438/// ```
439/// # use std::sync::Arc;
440/// use spdlog::prelude::*;
441///
442/// let default_logger = spdlog::default_logger();
443///
444/// default_logger.set_level_filter(LevelFilter::All);
445///
446/// info!("this log will be written to `stdout`");
447/// debug!("this log will be written to `stdout`");
448/// trace!("this log will be written to `stdout`");
449///
450/// warn!("this log will be written to `stderr`");
451/// error!("this log will be written to `stderr`");
452/// critical!("this log will be written to `stderr`");
453/// ```
454#[must_use]
455pub fn default_logger() -> Arc<Logger> {
456    default_logger_ref().load().clone()
457}
458
459/// Sets the given logger as the new global default logger, and returns the old
460/// one.
461///
462/// # Examples
463///
464/// ```
465/// # use std::sync::Arc;
466/// use spdlog::prelude::*;
467///
468/// let new_logger: Arc<Logger> = /* ... */
469/// # spdlog::default_logger();
470/// let old_logger = spdlog::swap_default_logger(new_logger);
471///
472/// info!("this log will be handled by `new_logger`");
473/// info!(logger: old_logger, "this log will be handled by `old_logger`");
474/// ```
475pub fn swap_default_logger(logger: Arc<Logger>) -> Arc<Logger> {
476    default_logger_ref().swap(logger)
477}
478
479/// Sets the given logger as the new global default logger.
480///
481/// # Examples
482///
483/// ```
484/// # use std::sync::Arc;
485/// use spdlog::prelude::*;
486///
487/// # let new_logger = spdlog::default_logger();
488/// spdlog::set_default_logger(new_logger);
489///
490/// info!("this log will be handled by `new_logger`");
491/// ```
492pub fn set_default_logger(logger: Arc<Logger>) {
493    swap_default_logger(logger);
494}
495
496/// Initializes environment variable level filters from environment variable
497/// `SPDLOG_RS_LEVEL`.
498///
499/// Returns whether the level in the environment variable was applied if there
500/// are no errors.
501///
502/// The default level filter of loggers built after calling this function will
503/// be configured based on the value of environment variable `SPDLOG_RS_LEVEL`.
504///
505/// If you want to read from a custom environment variable, see
506/// [`init_env_level_from`].
507///
508/// Users should call this function early, the level filter of loggers built
509/// before calling this function will not be configured by environment variable.
510///
511/// ## Formats of the environment variable value
512///
513/// The levels contained in the environment variable mean
514/// `LevelFilter::MoreSevereEqual(level)`.
515///
516/// ---
517///
518/// - Specifies the level filter for ***the default logger***.
519///
520///   Possible inputs: `off`, `trace`, `warn`, `all`, etc.
521///
522/// ---
523///
524/// - Specifies the level filter for ***unnamed loggers***.
525///
526///   Possible inputs: `=off`, `=info`, `=error`, `=all`, etc.
527///
528/// ---
529///
530/// - Specifies the level filter for ***loggers with the specified name***.
531///
532///   Possible inputs: `logger-name=info`, `network=warn`, `core=info`,
533///   `gui=critical`, etc.
534///
535/// ---
536///
537/// - Specifies the level filter for ***all loggers except the default logger***
538///   (respect the above rules first if they are matched).
539///
540///   Possible inputs: `*=error`, `*=off`, `*=critical`, etc.
541///
542/// ---
543///
544/// The levels are not case-sensitive, and these rules are combinable, separated
545/// by commas.
546///
547/// For example, these are legal:
548///
549/// ---
550///
551/// - `ALL,*=ALL`
552///
553///   Specifies the level filter for all loggers as `LevelFilter::All`.
554///
555/// ---
556///
557/// - `off,*=ERROR`
558///
559///   Specifies the level filter for the default logger as `LevelFilter::Off`,
560///   the rest of loggers as `LevelFilter::MoreSevereEqual(Level::Error)`.
561///
562/// ---
563///
564/// - `gui=warn,network=trace`
565///
566///   Specifies the level filter for loggers with name "gui" as
567///   `LevelFilter::MoreSevereEqual(Level::Warn)`, loggers with name "network"
568///   as `LevelFilter::MoreSevereEqual(Level::Trace)`.
569///
570/// ---
571///
572/// However, the same rule cannot be specified more than once.
573///
574/// # Examples
575///
576/// - Environment variable `SPDLOG_RS_LEVEL` is not present:
577///
578///   ```
579///   use spdlog::prelude::*;
580///
581///   # fn main() -> Result<(), Box<dyn std::error::Error>> {
582///   assert_eq!(spdlog::init_env_level()?, false);
583///
584///   assert_eq!(
585///       spdlog::default_logger().level_filter(),
586///       LevelFilter::MoreSevereEqual(Level::Info) // default level filter
587///   );
588///   assert_eq!(
589///       Logger::builder().build()?.level_filter(), // unnamed logger
590///       LevelFilter::MoreSevereEqual(Level::Info) // default level filter
591///   );
592///   assert_eq!(
593///       Logger::builder().name("gui").build()?.level_filter(),
594///       LevelFilter::MoreSevereEqual(Level::Info) // default level filter
595///   );
596///   assert_eq!(
597///       Logger::builder().name("network").build()?.level_filter(),
598///       LevelFilter::MoreSevereEqual(Level::Info) // default level filter
599///   );
600///   # Ok(()) }
601///   ```
602///
603/// ---
604///
605/// - `SPDLOG_RS_LEVEL="TRACE,network=Warn,*=error"`:
606///
607///   ```
608///   use spdlog::prelude::*;
609///
610///   # fn main() -> Result<(), Box<dyn std::error::Error>> {
611///   # std::env::set_var("SPDLOG_RS_LEVEL", "TRACE,network=Warn,*=error");
612///   assert_eq!(spdlog::init_env_level()?, true);
613///
614///   assert_eq!(
615///       spdlog::default_logger().level_filter(),
616///       LevelFilter::MoreSevereEqual(Level::Trace)
617///   );
618///   assert_eq!(
619///       Logger::builder().build()?.level_filter(), // unnamed logger
620///       LevelFilter::MoreSevereEqual(Level::Error)
621///   );
622///   assert_eq!(
623///       Logger::builder().name("gui").build()?.level_filter(),
624///       LevelFilter::MoreSevereEqual(Level::Error)
625///   );
626///   assert_eq!(
627///       Logger::builder().name("network").build()?.level_filter(),
628///       LevelFilter::MoreSevereEqual(Level::Warn)
629///   );
630///   # Ok(()) }
631///   ```
632///
633/// ---
634///
635/// - `SPDLOG_RS_LEVEL="network=Warn,network=Warn"` will fail, as the same rule
636///   is specified multiple times.
637///
638///   ```
639///   # std::env::set_var("SPDLOG_RS_LEVEL", "network=Warn,network=Warn");
640///   assert!(matches!(
641///       spdlog::init_env_level(),
642///       Err(spdlog::error::EnvLevelError::ParseEnvVar(_))
643///   ));
644///   ```
645pub fn init_env_level() -> StdResult<bool, EnvLevelError> {
646    init_env_level_from("SPDLOG_RS_LEVEL")
647}
648
649/// Initializes environment variable level filters from a specified environment
650/// variable.
651///
652/// For more information, see [`init_env_level`].
653///
654/// # Examples
655///
656/// - `MY_APP_LOG_LEVEL="TRACE,network=Warn,*=error"`:
657///
658///   ```
659///   use spdlog::prelude::*;
660///
661///   # fn main() -> Result<(), Box<dyn std::error::Error>> {
662///   # std::env::set_var("MY_APP_LOG_LEVEL", "TRACE,network=Warn,*=error");
663///   assert_eq!(spdlog::init_env_level_from("MY_APP_LOG_LEVEL")?, true);
664///
665///   assert_eq!(
666///       spdlog::default_logger().level_filter(),
667///       LevelFilter::MoreSevereEqual(Level::Trace)
668///   );
669///   assert_eq!(
670///       Logger::builder().build()?.level_filter(), // unnamed logger
671///       LevelFilter::MoreSevereEqual(Level::Error)
672///   );
673///   assert_eq!(
674///       Logger::builder().name("gui").build()?.level_filter(),
675///       LevelFilter::MoreSevereEqual(Level::Error)
676///   );
677///   assert_eq!(
678///       Logger::builder().name("network").build()?.level_filter(),
679///       LevelFilter::MoreSevereEqual(Level::Warn)
680///   );
681///   # Ok(()) }
682///   ```
683///
684/// For more examples, see [`init_env_level`].
685pub fn init_env_level_from<K: AsRef<OsStr>>(env_key: K) -> StdResult<bool, EnvLevelError> {
686    let var = match env::var(env_key.as_ref()) {
687        Err(VarError::NotPresent) => return Ok(false),
688        Err(err) => return Err(EnvLevelError::FetchEnvVar(err)),
689        Ok(var) => var,
690    };
691    env_level::from_str(&var)?;
692    Ok(true)
693}
694
695/// Initializes the log crate proxy.
696///
697/// This function calls [`log::set_logger`] to set up a [`LogCrateProxy`] and
698/// all logs from log crate will be forwarded to `spdlog-rs`'s logger.
699///
700/// Users should call this function only once, and then configure the proxy by
701/// calling [`log_crate_proxy()`].
702///
703/// Note that the `log` crate uses a different log level filter and by default
704/// it rejects all log messages. To log messages via the `log` crate, you have
705/// to call [`log::set_max_level`] manually before logging. For more
706/// information, please read the upstream documentation of
707/// [`log::set_max_level`].
708#[cfg(feature = "log")]
709pub fn init_log_crate_proxy() -> StdResult<(), re_export::log::SetLoggerError> {
710    log::set_logger(log_crate_proxy())
711}
712
713/// Returns the global instance of log crate proxy.
714#[cfg(feature = "log")]
715#[must_use]
716pub fn log_crate_proxy() -> &'static LogCrateProxy {
717    static PROXY: Lazy<LogCrateProxy> = Lazy::new(LogCrateProxy::new);
718    &PROXY
719}
720
721static IS_TEARING_DOWN: AtomicBool = AtomicBool::new(false);
722
723fn flush_default_logger_at_exit() {
724    // Rust never calls `drop` for static variables.
725    //
726    // Setting up an exit handler gives us a chance to flush the default logger
727    // once at the program exit, thus we don't lose the last logs.
728
729    extern "C" fn handler() {
730        IS_TEARING_DOWN.store(true, Ordering::SeqCst);
731        if let Some(default_logger) = DEFAULT_LOGGER.get() {
732            default_logger.load().flush()
733        }
734    }
735
736    #[must_use]
737    fn try_atexit() -> bool {
738        use std::os::raw::c_int;
739
740        extern "C" {
741            fn atexit(cb: extern "C" fn()) -> c_int;
742        }
743
744        (unsafe { atexit(handler) }) == 0
745    }
746
747    fn hook_panic() {
748        let previous_hook = panic::take_hook();
749
750        panic::set_hook(Box::new(move |info| {
751            handler();
752            previous_hook(info);
753        }));
754    }
755
756    if !try_atexit() {
757        hook_panic() // at least
758    }
759}
760
761fn default_error_handler(from: impl AsRef<str>, error: Error) {
762    if let Error::Multiple(errs) = error {
763        errs.into_iter()
764            .for_each(|err| default_error_handler(from.as_ref(), err));
765        return;
766    }
767
768    let date = chrono::Local::now()
769        .format("%Y-%m-%d %H:%M:%S.%3f")
770        .to_string();
771
772    eprintln!(
773        "[*** SPDLOG-RS UNHANDLED ERROR ***] [{}] [{}] {}",
774        date,
775        from.as_ref(),
776        error
777    );
778}
779
780// Used at log macros
781#[doc(hidden)]
782pub fn __log(
783    logger: &Logger,
784    level: Level,
785    srcloc: Option<SourceLocation>,
786    fmt_args: fmt::Arguments,
787) {
788    // Use `Cow` to avoid allocation as much as we can
789    let payload: Cow<str> = fmt_args
790        .as_str()
791        .map(Cow::Borrowed) // No format arguments, so it is a `&'static str`
792        .unwrap_or_else(|| Cow::Owned(fmt_args.to_string()));
793    let record = Record::new(level, payload, srcloc, logger.name());
794    logger.log(&record);
795}
796
797#[cfg(test)]
798mod tests {
799    use test_utils::*;
800
801    use super::*;
802
803    #[test]
804    fn test_default_logger() {
805        let test_sink = Arc::new(TestSink::new());
806
807        let test_logger = Arc::new(build_test_logger(|b| b.sink(test_sink.clone())));
808        let empty_logger = Arc::new(Logger::builder().build().unwrap());
809
810        set_default_logger(empty_logger.clone());
811        info!("hello");
812        error!("world");
813
814        set_default_logger(test_logger);
815        warn!("hello");
816        error!("rust");
817
818        set_default_logger(empty_logger);
819        info!("hello");
820        error!("spdlog");
821
822        assert_eq!(test_sink.log_count(), 2);
823        assert_eq!(
824            test_sink.payloads(),
825            vec!["hello".to_string(), "rust".to_string()]
826        );
827    }
828}