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