simple_logger-5.0.0/.cargo_vcs_info.json0000644000000001360000000000100136660ustar { "git": { "sha1": "fb96a82c274f92eff417904e6e819f25f3b51ccc" }, "path_in_vcs": "" }simple_logger-5.0.0/.github/workflows/ci.yml000064400000000000000000000042411046102023000171720ustar 00000000000000--- name: ci on: pull_request: push: branches: - main release: types: - created env: # Just a reassurance to mitigate sudden network connection problems CARGO_NET_RETRY: 10 RUSTUP_MAX_RETRIES: 10 CARGO_INCREMENTAL: 0 RUST_BACKTRACE: full # We don't need any debug symbols on ci, this also speeds up builds a bunch RUSTFLAGS: --deny warnings -Cdebuginfo=0 RUSTDOCFLAGS: --deny warnings jobs: rust-lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: rustfmt, clippy - run: cargo clippy --workspace - run: cargo fmt --all -- --check rust-test: runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.toolchain != 'stable' }} strategy: matrix: os: - ubuntu-latest - windows-latest - macos-latest toolchain: - stable features: - "colors" - "threads" - "timestamps" - "stderr" exclude: - os: macos-latest toolchain: stable features: "timestamps" include: - os: ubuntu-latest toolchain: beta features: "colors,threads,timestamps" - os: ubuntu-latest toolchain: nightly features: "colors,threads,timestamps,nightly" steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.toolchain }} profile: minimal - run: cargo +${{ matrix.toolchain }} build --no-default-features --features ${{ matrix.features }} - run: cargo +${{ matrix.toolchain }} test --no-default-features --features ${{ matrix.features }} rust-publish-crates: if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest needs: - rust-lint - rust-test steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal - run: cargo publish --token ${{ secrets.CRATES_TOKEN }} simple_logger-5.0.0/.gitignore000064400000000000000000000000311046102023000144400ustar 00000000000000target Cargo.lock .idea/ simple_logger-5.0.0/Cargo.lock0000644000000141540000000000100116460ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "colored" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ "lazy_static", "windows-sys", ] [[package]] name = "deranged" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num_threads" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro2" version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "serde" version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "simple_logger" version = "5.0.0" dependencies = [ "colored", "log", "time", "windows-sys", ] [[package]] name = "syn" version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "time" version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", "num-conv", "num_threads", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" simple_logger-5.0.0/Cargo.toml0000644000000034770000000000100116770ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "simple_logger" version = "5.0.0" authors = ["Sam Clements "] description = "A logger that prints all messages with a readable output format" readme = "README.md" license = "MIT" repository = "https://github.com/borntyping/rust-simple_logger" [[example]] name = "colors" required-features = ["colors"] [[example]] name = "stderr" required-features = [ "colors", "stderr", ] [[example]] name = "threads" required-features = ["threads"] [[example]] name = "timestamps_local" required-features = ["timestamps"] [[example]] name = "timestamps_none" required-features = ["timestamps"] [[example]] name = "timestamps_utc" required-features = ["timestamps"] [[example]] name = "timestamps_utc_offset" required-features = ["timestamps"] [[example]] name = "timestamps_format" required-features = ["timestamps"] [dependencies.colored] version = "2" optional = true [dependencies.log] version = "^0.4.17" features = ["std"] [dependencies.time] version = "^0.3.16" features = [ "formatting", "local-offset", "macros", ] optional = true [features] colors = ["colored"] default = [ "colors", "timestamps", ] nightly = [] stderr = [] threads = [] timestamps = ["time"] [target."cfg(windows)".dependencies.windows-sys] version = "^0.48.0" features = [ "Win32_System_Console", "Win32_Foundation", ] simple_logger-5.0.0/Cargo.toml.orig000064400000000000000000000024441046102023000153510ustar 00000000000000[package] name = "simple_logger" version = "5.0.0" license = "MIT" authors = ["Sam Clements "] description = "A logger that prints all messages with a readable output format" repository = "https://github.com/borntyping/rust-simple_logger" edition = "2018" [features] default = ["colors", "timestamps"] colors = ["colored"] threads = [] timestamps = ["time"] nightly = [] stderr = [] [dependencies] log = { version = "^0.4.17", features = ["std"] } time = { version = "^0.3.16", features = ["formatting", "local-offset", "macros"], optional = true } colored = { version = "2", optional = true } [target.'cfg(windows)'.dependencies] windows-sys = { version = "^0.48.0", features = ["Win32_System_Console", "Win32_Foundation"] } [[example]] name = "colors" required-features = ["colors"] [[example]] name = "stderr" required-features = ["colors", "stderr"] [[example]] name = "threads" required-features = ["threads"] [[example]] name = "timestamps_local" required-features = ["timestamps"] [[example]] name = "timestamps_none" required-features = ["timestamps"] [[example]] name = "timestamps_utc" required-features = ["timestamps"] [[example]] name = "timestamps_utc_offset" required-features = ["timestamps"] [[example]] name = "timestamps_format" required-features = ["timestamps"] simple_logger-5.0.0/LICENSE000064400000000000000000000020411046102023000134600ustar 00000000000000Copyright 2015-2021 Sam Clements Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. simple_logger-5.0.0/README.md000064400000000000000000000100031046102023000137270ustar 00000000000000# simple_logger [![](https://img.shields.io/github/tag/borntyping/rust-simple_logger.svg)](https://github.com/borntyping/rust-simple_logger/tags) A logger that prints all messages with a readable output format. The output format is based on the format used by [Supervisord](https://github.com/Supervisor/supervisor), with timestamps in [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) format. The format and timezone used for timestamps can be customised, simple colours based on log level can be enabled, thread metadata can be included, and output can be toggled between STDOUT and STDERR. * [Source on GitHub](https://github.com/borntyping/rust-simple_logger) * [Packages on Crates.io](https://crates.io/crates/simple_logger) * [Documentation on Docs.rs](https://docs.rs/simple_logger) Notices ------- ### Project status I wrote the initial version of this library in 2015, and haven't written Rust professionally since then. I consider this as close as I'll ever get to a "finished" project and don't plan on adding any more features to it. It is still maintained and I try and merge pull requests for fixes, improvements, and features; though I generally turn down pull requests for large or complex features that go outside the library's aim to be a simple logger. If you need more, the `log` module has a list of [available logging implementations](https://docs.rs/log/latest/log/#available-logging-implementations), or you could consider forking `simple_logger` and building on top of it. ### Breaking changes - **Version 2.0.0 changes the default from displaying timestamps in the local timezone to displaying timestamps in UTC.** See issue [#52](https://github.com/borntyping/rust-simple_logger/issues/52) for more information. Usage ----- ```rust use simple_logger::SimpleLogger; fn main() { SimpleLogger::new().init().unwrap(); log::warn!("This is an example message."); } ``` This outputs: ```txt 2022-01-19T17:27:07.013874956Z WARN [logging_example] This is an example message. ``` You can run the above example with: ```sh cargo run --example init ``` ### Optional features The `colors` and `timestamps` features are enabled by default. You can remove these features and their respective dependencies by disabling all features in your `Cargo.toml`. ```toml [dependencies.simple_logger] default-features = false ``` To include the `timestamps` feature, but not the `colors` feature: ```toml [dependencies.simple_logger] default-features = false features = ["timestamps"] ``` To include the `colors` feature, but not the `timestamps` feature: ```toml [dependencies.simple_logger] default-features = false features = ["colors"] ``` To include thread metadata use the `threads` and `nightly` features: ```toml [dependencies.simple_logger] features = ["threads", "nightly"] ``` To direct logging output to `stderr` use the `stderr` feature: ```toml [dependencies.simple_logger] features = ["stderr"] ``` Multiple features can be combined. ```toml [dependencies.simple_logger] features = ["colors", "threads", "timestamps", "nightly", "stderr"] ``` ### Wrapping with another logger You might want to wrap this logger to do your own processing before handing events to a SimpleLogger instance. Instead of calling `init()` which calls `log::set_max_level` and `log::set_boxed_logger`, you can call those functions directly giving you the chance to wrap or adjust the logger. See [wrap.rs](examples/wrap.rs) for a more detailed example. ### Console setup The `SimpleLogger.init()` function attempts to configure colours support as best it can in various situations: - On Windows, it will enable colour output. _See `set_up_windows_color_terminal()`._ - When using the `colors` *and* `stderr` features, it will instruct the `colored` library to display colors if STDERR is a terminal (instead of checking if STDOUT is a terminal). _See `use_stderr_for_colors()`._ Licence ------- `simple_logger` is licenced under the [MIT Licence](http://opensource.org/licenses/MIT). Authors ------- Written by [Sam Clements](sam@borntyping.co.uk). simple_logger-5.0.0/examples/colors.rs000064400000000000000000000002331046102023000161410ustar 00000000000000use simple_logger::SimpleLogger; fn main() { SimpleLogger::new().with_colors(true).init().unwrap(); log::warn!("This is an example message."); } simple_logger-5.0.0/examples/flush.rs000064400000000000000000000002451046102023000157640ustar 00000000000000use simple_logger::SimpleLogger; fn main() { SimpleLogger::new().init().unwrap(); log::warn!("This is an example message."); log::logger().flush(); } simple_logger-5.0.0/examples/init.rs000064400000000000000000000002111046102023000155770ustar 00000000000000use simple_logger::SimpleLogger; fn main() { SimpleLogger::new().init().unwrap(); log::warn!("This is an example message."); } simple_logger-5.0.0/examples/init_with_level.rs000064400000000000000000000003421046102023000200260ustar 00000000000000use log::LevelFilter; use simple_logger::SimpleLogger; fn main() { SimpleLogger::new().with_level(LevelFilter::Warn).init().unwrap(); log::warn!("This will be logged."); log::info!("This will NOT be logged."); } simple_logger-5.0.0/examples/init_with_target_level.rs000064400000000000000000000004561046102023000214020ustar 00000000000000use log::LevelFilter; use simple_logger::SimpleLogger; fn main() { SimpleLogger::new() .with_level(LevelFilter::Info) .with_module_level("init_with_target_level", LevelFilter::Off) .init() .unwrap(); log::info!("This will NOT be logged. (Target disabled)"); } simple_logger-5.0.0/examples/stderr.rs000064400000000000000000000002331046102023000161430ustar 00000000000000use simple_logger::SimpleLogger; fn main() { SimpleLogger::new().with_colors(true).init().unwrap(); log::warn!("This is an example message."); } simple_logger-5.0.0/examples/threads.rs000064400000000000000000000011301046102023000162670ustar 00000000000000use simple_logger::SimpleLogger; fn main() { SimpleLogger::new().with_threads(true).init().unwrap(); log::info!("Main thread logs here."); // If the "nightly" feature is enabled, the output will include thread ids. for _ in 1..=5 { std::thread::spawn(|| { log::info!("Unnamed thread logs here."); }) .join() .unwrap(); } std::thread::Builder::new() .name("named_thread".to_string()) .spawn(|| { log::info!("Named thread logs here."); }) .unwrap() .join() .unwrap(); } simple_logger-5.0.0/examples/timestamps_format.rs000064400000000000000000000005221046102023000203770ustar 00000000000000use simple_logger::SimpleLogger; use time::macros::format_description; fn main() { SimpleLogger::new() .env() .with_timestamp_format(format_description!("[year]-[month]-[day] [hour]:[minute]:[second]")) .init() .unwrap(); log::warn!("This is an example message with custom timestamp format."); } simple_logger-5.0.0/examples/timestamps_local.rs000064400000000000000000000002411046102023000201770ustar 00000000000000use simple_logger::SimpleLogger; fn main() { SimpleLogger::new().with_local_timestamps().init().unwrap(); log::warn!("This is an example message."); } simple_logger-5.0.0/examples/timestamps_none.rs000064400000000000000000000002361046102023000200500ustar 00000000000000use simple_logger::SimpleLogger; fn main() { SimpleLogger::new().without_timestamps().init().unwrap(); log::warn!("This is an example message."); } simple_logger-5.0.0/examples/timestamps_utc.rs000064400000000000000000000002371046102023000177050ustar 00000000000000use simple_logger::SimpleLogger; fn main() { SimpleLogger::new().with_utc_timestamps().init().unwrap(); log::warn!("This is an example message."); } simple_logger-5.0.0/examples/timestamps_utc_offset.rs000064400000000000000000000005401046102023000212500ustar 00000000000000use simple_logger::SimpleLogger; use time::UtcOffset; fn main() { SimpleLogger::new() .with_utc_offset(UtcOffset::from_hms(14, 0, 0).unwrap()) .init() .unwrap(); log::warn!("This is an example message using a static UTC offset."); log::info!("Daylight savings or other timezone changes will not be respected."); } simple_logger-5.0.0/examples/wrap.rs000064400000000000000000000012611046102023000156130ustar 00000000000000use log::{Log, Metadata, Record}; use simple_logger::SimpleLogger; struct WrapperLogger { simple_logger: SimpleLogger, } impl Log for WrapperLogger { fn enabled(&self, metadata: &Metadata) -> bool { self.simple_logger.enabled(metadata) } fn log(&self, record: &Record) { self.simple_logger.log(record) } fn flush(&self) { self.simple_logger.flush() } } fn main() { let simple_logger = SimpleLogger::new(); log::set_max_level(simple_logger.max_level()); let wrapper_logger = WrapperLogger { simple_logger }; log::set_boxed_logger(Box::new(wrapper_logger)).unwrap(); log::warn!("This is an example message."); } simple_logger-5.0.0/rustfmt.toml000064400000000000000000000000201046102023000150470ustar 00000000000000max_width = 120 simple_logger-5.0.0/src/lib.rs000064400000000000000000000613561046102023000143740ustar 00000000000000//! A logger that prints all messages with a simple, readable output format. //! //! Optional features include timestamps, colored output and logging to stderr. //! //! ```rust //! simple_logger::SimpleLogger::new().env().init().unwrap(); //! //! log::warn!("This is an example message."); //! ``` //! //! Some shortcuts are available for common use cases. //! //! Just initialize logging without any configuration: //! //! ```rust //! simple_logger::init().unwrap(); //! ``` //! //! Set the log level from the `RUST_LOG` environment variable: //! //! ```rust //! simple_logger::init_with_env().unwrap(); //! ``` //! //! Hardcode a default log level: //! //! ```rust //! simple_logger::init_with_level(log::Level::Warn).unwrap(); //! ``` #![cfg_attr(feature = "nightly", feature(thread_id_value))] #[cfg(feature = "colors")] use colored::*; use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError}; use std::{collections::HashMap, str::FromStr}; #[cfg(feature = "timestamps")] use time::{format_description::FormatItem, OffsetDateTime, UtcOffset}; #[cfg(feature = "timestamps")] const TIMESTAMP_FORMAT_OFFSET: &[FormatItem] = time::macros::format_description!( "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3][offset_hour sign:mandatory]:[offset_minute]" ); #[cfg(feature = "timestamps")] const TIMESTAMP_FORMAT_UTC: &[FormatItem] = time::macros::format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3]Z"); #[cfg(feature = "timestamps")] #[derive(PartialEq)] enum Timestamps { None, Local, Utc, UtcOffset(UtcOffset), } /// Implements [`Log`] and a set of simple builder methods for configuration. /// /// Use the various "builder" methods on this struct to configure the logger, /// then call [`init`] to configure the [`log`] crate. pub struct SimpleLogger { /// The default logging level default_level: LevelFilter, /// The specific logging level for each module /// /// This is used to override the default value for some specific modules. /// /// This must be sorted from most-specific to least-specific, so that [`enabled`](#method.enabled) can scan the /// vector for the first match to give us the desired log level for a module. module_levels: Vec<(String, LevelFilter)>, /// Whether to include thread names (and IDs) or not /// /// This field is only available if the `threads` feature is enabled. #[cfg(feature = "threads")] threads: bool, /// Control how timestamps are displayed. /// /// This field is only available if the `timestamps` feature is enabled. #[cfg(feature = "timestamps")] timestamps: Timestamps, #[cfg(feature = "timestamps")] timestamps_format: Option<&'static [FormatItem<'static>]>, /// Whether to use color output or not. /// /// This field is only available if the `color` feature is enabled. #[cfg(feature = "colors")] colors: bool, } impl SimpleLogger { /// Initializes the global logger with a SimpleLogger instance with /// default log level set to `Level::Trace`. /// /// ```no_run /// use simple_logger::SimpleLogger; /// SimpleLogger::new().env().init().unwrap(); /// log::warn!("This is an example message."); /// ``` /// /// [`init`]: #method.init #[must_use = "You must call init() to begin logging"] pub fn new() -> SimpleLogger { SimpleLogger { default_level: LevelFilter::Trace, module_levels: Vec::new(), #[cfg(feature = "threads")] threads: false, #[cfg(feature = "timestamps")] timestamps: Timestamps::Utc, #[cfg(feature = "timestamps")] timestamps_format: None, #[cfg(feature = "colors")] colors: true, } } /// Initializes the global logger with log level read from `RUST_LOG` environment /// variable value. Deprecated in favor of `env()`. /// /// You may use the various builder-style methods on this type to configure /// the logger, and you must call [`init`] in order to start logging messages. /// /// ```no_run /// use simple_logger::SimpleLogger; /// SimpleLogger::from_env().init().unwrap(); /// log::warn!("This is an example message."); /// ``` /// /// [`init`]: #method.init #[must_use = "You must call init() to begin logging"] #[deprecated( since = "1.12.0", note = "Use [`env`](#method.env) instead. Will be removed in version 2.0.0." )] pub fn from_env() -> SimpleLogger { SimpleLogger::new().with_level(log::LevelFilter::Error).env() } /// Enables the user to choose log level by setting `RUST_LOG=` /// environment variable. This will use the default level set by /// [`with_level`] if `RUST_LOG` is not set or can't be parsed as a /// standard log level. /// /// This must be called after [`with_level`]. If called before /// [`with_level`], it will have no effect. /// /// [`with_level`]: #method.with_level #[must_use = "You must call init() to begin logging"] pub fn env(mut self) -> SimpleLogger { self.default_level = std::env::var("RUST_LOG") .ok() .as_deref() .map(log::LevelFilter::from_str) .and_then(Result::ok) .unwrap_or(self.default_level); self } /// Set the 'default' log level. /// /// You can override the default level for specific modules and their sub-modules using [`with_module_level`] /// /// This must be called before [`env`]. If called after [`env`], it will override the value loaded from the environment. /// /// [`env`]: #method.env /// [`with_module_level`]: #method.with_module_level #[must_use = "You must call init() to begin logging"] pub fn with_level(mut self, level: LevelFilter) -> SimpleLogger { self.default_level = level; self } /// Override the log level for some specific modules. /// /// This sets the log level of a specific module and all its sub-modules. /// When both the level for a parent module as well as a child module are set, /// the more specific value is taken. If the log level for the same module is /// specified twice, the resulting log level is implementation defined. /// /// # Examples /// /// Silence an overly verbose crate: /// /// ```no_run /// use simple_logger::SimpleLogger; /// use log::LevelFilter; /// /// SimpleLogger::new().with_module_level("chatty_dependency", LevelFilter::Warn).init().unwrap(); /// ``` /// /// Disable logging for all dependencies: /// /// ```no_run /// use simple_logger::SimpleLogger; /// use log::LevelFilter; /// /// SimpleLogger::new() /// .with_level(LevelFilter::Off) /// .with_module_level("my_crate", LevelFilter::Info) /// .init() /// .unwrap(); /// ``` // // This method *must* sort `module_levels` for the [`enabled`](#method.enabled) method to work correctly. #[must_use = "You must call init() to begin logging"] pub fn with_module_level(mut self, target: &str, level: LevelFilter) -> SimpleLogger { self.module_levels.push((target.to_string(), level)); self.module_levels .sort_by_key(|(name, _level)| name.len().wrapping_neg()); self } /// Override the log level for specific targets. // This method *must* sort `module_levels` for the [`enabled`](#method.enabled) method to work correctly. #[must_use = "You must call init() to begin logging"] #[deprecated( since = "1.11.0", note = "Use [`with_module_level`](#method.with_module_level) instead. Will be removed in version 2.0.0." )] pub fn with_target_levels(mut self, target_levels: HashMap) -> SimpleLogger { self.module_levels = target_levels.into_iter().collect(); self.module_levels .sort_by_key(|(name, _level)| name.len().wrapping_neg()); self } /// Control whether thread names (and IDs) are printed or not. /// /// This method is only available if the `threads` feature is enabled. /// Thread names are disabled by default. #[must_use = "You must call init() to begin logging"] #[cfg(feature = "threads")] pub fn with_threads(mut self, threads: bool) -> SimpleLogger { self.threads = threads; self } /// Control whether timestamps are printed or not. /// /// Timestamps will be displayed in the local timezone. /// /// This method is only available if the `timestamps` feature is enabled. #[must_use = "You must call init() to begin logging"] #[cfg(feature = "timestamps")] #[deprecated( since = "1.16.0", note = "Use [`with_local_timestamps`] or [`with_utc_timestamps`] instead. Will be removed in version 2.0.0." )] pub fn with_timestamps(mut self, timestamps: bool) -> SimpleLogger { if timestamps { self.timestamps = Timestamps::Local } else { self.timestamps = Timestamps::None } self } /// Control the format used for timestamps. /// /// Without this, a default format is used depending on the timestamps type. /// /// The syntax for the format_description macro can be found in the /// [`time` crate book](https://time-rs.github.io/book/api/format-description.html). /// /// ``` /// simple_logger::SimpleLogger::new() /// .with_level(log::LevelFilter::Debug) /// .env() /// .with_timestamp_format(time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second]")) /// .init() /// .unwrap(); /// ``` #[must_use = "You must call init() to begin logging"] #[cfg(feature = "timestamps")] pub fn with_timestamp_format(mut self, format: &'static [FormatItem<'static>]) -> SimpleLogger { self.timestamps_format = Some(format); self } /// Don't display any timestamps. /// /// This method is only available if the `timestamps` feature is enabled. #[must_use = "You must call init() to begin logging"] #[cfg(feature = "timestamps")] pub fn without_timestamps(mut self) -> SimpleLogger { self.timestamps = Timestamps::None; self } /// Display timestamps using the local timezone. /// /// This method is only available if the `timestamps` feature is enabled. #[must_use = "You must call init() to begin logging"] #[cfg(feature = "timestamps")] pub fn with_local_timestamps(mut self) -> SimpleLogger { self.timestamps = Timestamps::Local; self } /// Display timestamps using UTC. /// /// This method is only available if the `timestamps` feature is enabled. #[must_use = "You must call init() to begin logging"] #[cfg(feature = "timestamps")] pub fn with_utc_timestamps(mut self) -> SimpleLogger { self.timestamps = Timestamps::Utc; self } /// Display timestamps using a static UTC offset. /// /// This method is only available if the `timestamps` feature is enabled. #[must_use = "You must call init() to begin logging"] #[cfg(feature = "timestamps")] pub fn with_utc_offset(mut self, offset: UtcOffset) -> SimpleLogger { self.timestamps = Timestamps::UtcOffset(offset); self } /// Control whether messages are colored or not. /// /// This method is only available if the `colored` feature is enabled. #[must_use = "You must call init() to begin logging"] #[cfg(feature = "colors")] pub fn with_colors(mut self, colors: bool) -> SimpleLogger { self.colors = colors; self } /// Configure the logger pub fn max_level(&self) -> LevelFilter { let max_level = self.module_levels.iter().map(|(_name, level)| level).copied().max(); max_level .map(|lvl| lvl.max(self.default_level)) .unwrap_or(self.default_level) } /// 'Init' the actual logger and instantiate it, /// this method MUST be called in order for the logger to be effective. pub fn init(self) -> Result<(), SetLoggerError> { #[cfg(all(windows, feature = "colored"))] set_up_windows_color_terminal(); #[cfg(all(feature = "colored", feature = "stderr"))] use_stderr_for_colors(); log::set_max_level(self.max_level()); log::set_boxed_logger(Box::new(self)) } } impl Default for SimpleLogger { /// See [this](struct.SimpleLogger.html#method.new) fn default() -> Self { SimpleLogger::new() } } impl Log for SimpleLogger { fn enabled(&self, metadata: &Metadata) -> bool { &metadata.level().to_level_filter() <= self .module_levels .iter() /* At this point the Vec is already sorted so that we can simply take * the first match */ .find(|(name, _level)| metadata.target().starts_with(name)) .map(|(_name, level)| level) .unwrap_or(&self.default_level) } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { let level_string = { #[cfg(feature = "colors")] { if self.colors { match record.level() { Level::Error => format!("{:<5}", record.level().to_string()).red().to_string(), Level::Warn => format!("{:<5}", record.level().to_string()).yellow().to_string(), Level::Info => format!("{:<5}", record.level().to_string()).cyan().to_string(), Level::Debug => format!("{:<5}", record.level().to_string()).purple().to_string(), Level::Trace => format!("{:<5}", record.level().to_string()).normal().to_string(), } } else { format!("{:<5}", record.level().to_string()) } } #[cfg(not(feature = "colors"))] { format!("{:<5}", record.level().to_string()) } }; let target = if !record.target().is_empty() { record.target() } else { record.module_path().unwrap_or_default() }; let thread = { #[cfg(feature = "threads")] if self.threads { let thread = std::thread::current(); format!("@{}", { #[cfg(feature = "nightly")] { thread.name().unwrap_or(&thread.id().as_u64().to_string()) } #[cfg(not(feature = "nightly"))] { thread.name().unwrap_or("?") } }) } else { "".to_string() } #[cfg(not(feature = "threads"))] "" }; let timestamp = { #[cfg(feature = "timestamps")] match self.timestamps { Timestamps::None => "".to_string(), Timestamps::Local => format!( "{} ", OffsetDateTime::now_local() .expect(concat!( "Could not determine the UTC offset on this system. ", "Consider displaying UTC time instead. ", "Possible causes are that the time crate does not implement \"local_offset_at\" ", "on your system, or that you are running in a multi-threaded environment and ", "the time crate is returning \"None\" from \"local_offset_at\" to avoid unsafe ", "behaviour. See the time crate's documentation for more information. ", "(https://time-rs.github.io/internal-api/time/index.html#feature-flags)" )) .format(&self.timestamps_format.unwrap_or(TIMESTAMP_FORMAT_OFFSET)) .unwrap() ), Timestamps::Utc => format!( "{} ", OffsetDateTime::now_utc() .format(&self.timestamps_format.unwrap_or(TIMESTAMP_FORMAT_UTC)) .unwrap() ), Timestamps::UtcOffset(offset) => format!( "{} ", OffsetDateTime::now_utc() .to_offset(offset) .format(&self.timestamps_format.unwrap_or(TIMESTAMP_FORMAT_OFFSET)) .unwrap() ), } #[cfg(not(feature = "timestamps"))] "" }; let message = format!("{}{} [{}{}] {}", timestamp, level_string, target, thread, record.args()); #[cfg(not(feature = "stderr"))] println!("{}", message); #[cfg(feature = "stderr")] eprintln!("{}", message); } } fn flush(&self) {} } /// Configure the console to display colours. /// /// This is only needed on Windows when using the 'colors' feature. /// It doesn't currently handle combining the 'colors' and 'stderr' features. #[cfg(all(windows, feature = "colors"))] pub fn set_up_windows_color_terminal() { use std::io::{stdout, IsTerminal}; if stdout().is_terminal() { unsafe { use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE; use windows_sys::Win32::System::Console::{ GetConsoleMode, GetStdHandle, SetConsoleMode, CONSOLE_MODE, ENABLE_VIRTUAL_TERMINAL_PROCESSING, STD_OUTPUT_HANDLE, }; let stdout = GetStdHandle(STD_OUTPUT_HANDLE); if stdout == INVALID_HANDLE_VALUE { return; } let mut mode: CONSOLE_MODE = 0; if GetConsoleMode(stdout, &mut mode) == 0 { return; } SetConsoleMode(stdout, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); } } } /// The colored crate will disable colors when STDOUT is not a terminal. This method overrides this /// behaviour to check the status of STDERR instead. #[cfg(all(feature = "colored", feature = "stderr"))] fn use_stderr_for_colors() { use std::io::{stderr, IsTerminal}; colored::control::set_override(stderr().is_terminal()); } /// Initialise the logger with its default configuration. /// /// Log messages will not be filtered. /// The `RUST_LOG` environment variable is not used. pub fn init() -> Result<(), SetLoggerError> { SimpleLogger::new().init() } /// Initialise the logger with its default configuration. /// /// Log messages will not be filtered. /// The `RUST_LOG` environment variable is not used. /// /// This function is only available if the `timestamps` feature is enabled. #[cfg(feature = "timestamps")] pub fn init_utc() -> Result<(), SetLoggerError> { SimpleLogger::new().with_utc_timestamps().init() } /// Initialise the logger with the `RUST_LOG` environment variable. /// /// Log messages will be filtered based on the `RUST_LOG` environment variable. pub fn init_with_env() -> Result<(), SetLoggerError> { SimpleLogger::new().env().init() } /// Initialise the logger with a specific log level. /// /// Log messages below the given [`Level`] will be filtered. /// The `RUST_LOG` environment variable is not used. pub fn init_with_level(level: Level) -> Result<(), SetLoggerError> { SimpleLogger::new().with_level(level.to_level_filter()).init() } /// Use [`init_with_env`] instead. /// /// This does the same as [`init_with_env`] but unwraps the result. #[deprecated( since = "1.12.0", note = "Use [`init_with_env`] instead, which does not unwrap the result. Will be removed in version 2.0.0." )] pub fn init_by_env() { init_with_env().unwrap() } #[cfg(test)] mod test { use super::*; #[test] fn test_module_levels_allowlist() { let logger = SimpleLogger::new() .with_level(LevelFilter::Off) .with_module_level("my_crate", LevelFilter::Info); assert!(logger.enabled(&create_log("my_crate", Level::Info))); assert!(logger.enabled(&create_log("my_crate::module", Level::Info))); assert!(!logger.enabled(&create_log("my_crate::module", Level::Debug))); assert!(!logger.enabled(&create_log("not_my_crate", Level::Debug))); assert!(!logger.enabled(&create_log("not_my_crate::module", Level::Error))); } #[test] fn test_module_levels_denylist() { let logger = SimpleLogger::new() .with_level(LevelFilter::Debug) .with_module_level("my_crate", LevelFilter::Trace) .with_module_level("chatty_dependency", LevelFilter::Info); assert!(logger.enabled(&create_log("my_crate", Level::Info))); assert!(logger.enabled(&create_log("my_crate", Level::Trace))); assert!(logger.enabled(&create_log("my_crate::module", Level::Info))); assert!(logger.enabled(&create_log("my_crate::module", Level::Trace))); assert!(logger.enabled(&create_log("not_my_crate", Level::Debug))); assert!(!logger.enabled(&create_log("not_my_crate::module", Level::Trace))); assert!(logger.enabled(&create_log("chatty_dependency", Level::Info))); assert!(!logger.enabled(&create_log("chatty_dependency", Level::Debug))); assert!(!logger.enabled(&create_log("chatty_dependency::module", Level::Debug))); assert!(logger.enabled(&create_log("chatty_dependency::module", Level::Warn))); } /// Test that enabled() looks for the most specific target. #[test] fn test_module_levels() { let logger = SimpleLogger::new() .with_level(LevelFilter::Off) .with_module_level("a", LevelFilter::Off) .with_module_level("a::b::c", LevelFilter::Off) .with_module_level("a::b", LevelFilter::Info); assert_eq!(logger.enabled(&create_log("a", Level::Info)), false); assert_eq!(logger.enabled(&create_log("a::b", Level::Info)), true); assert_eq!(logger.enabled(&create_log("a::b::c", Level::Info)), false); } #[test] fn test_max_level() { let builder = SimpleLogger::new(); assert_eq!(builder.max_level(), LevelFilter::Trace); } #[test] #[cfg(feature = "timestamps")] fn test_timestamps_defaults() { let builder = SimpleLogger::new(); assert!(builder.timestamps == Timestamps::Utc); } #[test] #[cfg(feature = "timestamps")] #[allow(deprecated)] fn test_with_timestamps() { let builder = SimpleLogger::new().with_timestamps(false); assert!(builder.timestamps == Timestamps::None); } #[test] #[cfg(feature = "timestamps")] fn test_with_utc_timestamps() { let builder = SimpleLogger::new().with_utc_timestamps(); assert!(builder.timestamps == Timestamps::Utc); } #[test] #[cfg(feature = "timestamps")] fn test_with_local_timestamps() { let builder = SimpleLogger::new().with_local_timestamps(); assert!(builder.timestamps == Timestamps::Local); } #[test] #[cfg(feature = "timestamps")] #[allow(deprecated)] fn test_with_timestamps_format() { let builder = SimpleLogger::new().with_timestamp_format(time::macros::format_description!("[hour]:[minute]:[second]")); assert!(builder.timestamps_format.is_some()); } #[test] #[cfg(feature = "colored")] fn test_with_colors() { let mut builder = SimpleLogger::new(); assert!(builder.colors == true); builder = builder.with_colors(false); assert!(builder.colors == false); } /// > And, without sorting, this would lead to all serde_json logs being treated as if they were configured to /// > Error level instead of Trace (since to determine the logging level for target, the code finds first match in /// > module_levels by a string prefix). #[test] fn test_issue_90() { let logger = SimpleLogger::new() .with_level(LevelFilter::Off) .with_module_level("serde", LevelFilter::Error) .with_module_level("serde_json", LevelFilter::Trace); assert_eq!(logger.enabled(&create_log("serde", Level::Trace)), false); assert_eq!(logger.enabled(&create_log("serde_json", Level::Trace)), true); } fn create_log(name: &str, level: Level) -> Metadata { let mut builder = Metadata::builder(); builder.level(level); builder.target(name); builder.build() } }