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