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}