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