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