# sensible-env-logger [github](https://github.com/rnag/sensible-env-logger) [crates.io](https://crates.io/crates/sensible-env-logger) [docs.rs](https://docs.rs/sensible-env-logger) [build status](https://github.com/rnag/sensible-env-logger/actions?query=branch%3Amain) > A pretty, sensible logger for Rust - ideal for running *examples* and *tests* on a crate of choice. This is a thin wrapper around [pretty_env_logger]. It requires minimal configuration to set up, and writes to standard error with nice colored output for log levels. ![example-output](readme-example.png) [pretty_env_logger]: https://docs.rs/pretty_env_logger --- This crate works with Cargo with a `Cargo.toml` like: ```toml [dependencies] log = "0.4" sensible-env-logger = "0.3" ``` ## Getting started The `sensible-env-logger` is ideally used when writing out [examples] or [tests] for a Cargo project or crate. It can be added as a [`dev-dependency`] if needed. [examples]: http://xion.io/post/code/rust-examples.html [tests]: https://doc.rust-lang.org/book/ch11-01-writing-tests.html [`dev-dependency`]: https://doc.rust-lang.org/rust-by-example/testing/dev_dependencies.html First, add some usage to your application: ```rust #[macro_use] extern crate log; fn main() { sensible_env_logger::init!(); info!("such information"); warn!("o_O"); error!("much error"); } ``` Then run your app with `cargo`, and you should see the full log output: ```console cargo run ``` Alternatively, run your app with the environment variables that control the log level for *external* crates as well as *your* crate explicitly set: ```console GLOBAL_RUST_LOG=warn RUST_LOG=trace cargo run ``` Even though this crate has the name *env* in it, using the `sensible-env-logger` in code is dead simple, and requires minimal configuration. ## Examples You can check out sample usage of this crate in the [examples/](https://github.com/rnag/sensible-env-logger/tree/main/examples) folder in the project repo on GitHub. ## Defaults The helper macros below can be used to configure the global logger with *sensible* defaults. Their sample log output is also shown. > Note: any helper macros, such as `init!()`, should be called > early in the execution of a Rust program. ### `init!()` Initializes the global logger with a pretty, sensible env logger. ```console INFO my_module > an informational message ``` ### `init_timed!()` Initializes the global logger with a *timed* pretty, sensible env logger. ```console 2022-03-24T17:15:31.683Z INFO my_module > an informational message ``` ### `init_timed_short!()` - [`*`] Initializes the global logger with a *localized time* pretty, sensible env logger. ```console 12:15:31.683 INFO my_module > an informational message ``` ### `init_timed_local!()` - [`*`] Initializes the global logger with a "no-frills" local date/time pretty, sensible env logger. ```console 2022-03-24 12:15:31.683 - INFO my_module > an informational message ``` ### `init_timed_local_iso!()` - [`*`] Initializes the global logger with a local-timed pretty, sensible env logger. > This variant formats log messages with a localized timestamp, > in ISO-8601/ RFC 3339 date & time format. ```console 2022-03-24T12:15:31.683+08:00 INFO my_module > an informational message ``` ## Optional Features [`*`]: #local-time ### Local Time Using the macros marked with a `*` above, require the `local-time` feature to be enabled: ```toml [dev-dependencies] sensible-env-logger = { version = "0.3", features = ["local-time"] } ``` ## In Tests When running *tests* on a crate, you can use the `safe_init!()` macro as shown below. This should ignore errors when initializing the global logger multiple times. ```rust #[cfg(test)] mod tests { use super::*; #[test] fn test_one() { sensible_env_logger::safe_init!(); trace!("hello from the first test") } #[test] fn test_two() { sensible_env_logger::safe_init!(); trace!("hello from the second test") } } ``` ## Rationale Imagine you are testing out a Cargo project named `my_rust_project`. That is, the `Cargo.toml` in your project would look something like this: ```toml,no_sync [package] name = "my-rust-project" [dependencies] log = "*" ``` Assuming you are building a library, your `src/lib.rs` could look like this: ```rust #[macro_use] extern crate log; pub fn my_awesome_fn() { trace!("getting ready to do something cool..."); std::thread::sleep(std::time::Duration::from_millis(500)); info!("finished!"); } ``` You then create a new [example] file named `examples/my_example.rs`, with the following contents: ```rust use my_rust_project::my_awesome_fn; #[macro_use] extern crate log; fn main() { debug!("my debug message"); my_awesome_fn(); error!("oops! something went wrong!"); } ``` [example]: http://xion.io/post/code/rust-examples.html You can run the new file with `cargo run --example my_example`. The problem here is that you won't get any terminal output by default; this is because you need to set up the `RUST_LOG` environment variable beforehand, in order to see the expected log output. Setting the `RUST_LOG` variable works, but there are a few issues with this. For example, what if your Cargo project uses other external libraries? Ideally you want to see the `trace` logs from your own project (the crate under test), but *not* the `trace` logs from these other libraries. In that case, setting `RUST_LOG=trace` doesn't seem the best approach here. You could then set the `RUST_LOG` environment variable to the following [log format]: ```shell $ export RUST_LOG='warning,my_rust_project=trace,my_example=trace' ``` When leveraging the [pretty_env_logger] crate and adding a `pretty_env_logger::init()` at the top of the `main` function, this does now work as expected and produce the desired log output. However, there are few limitations with this approach: * In your Cargo project, you'd need to update the documentation for running examples to mention that you need to export the `RUST_LOG` env variable explicitly. For instance, you'd need to mention that an example is ideally run like `RUST_LOG=trace cargo run --example my_example`. * You'd need to remember to set the `RUST_LOG` env variable each time. This can be troublesome when your Windows machine reboots for example, or whenever you open a new terminal window. To solve these minor issues you can simply use the `sensible_env_logger` crate, which automatically sets up *sensible* defaults; this involves generating and using a directive string in the same form as the `$RUST_LOG` environment variable. Now, the updated code in the `examples/my_example.rs` would look like this: ```rust use my_rust_project::my_awesome_fn; #[macro_use] extern crate log; fn main() { sensible_env_logger::init!(); debug!("my debug message"); my_awesome_fn(); error!("oops! something went wrong!"); } ``` [log format]: https://rust-lang-nursery.github.io/rust-cookbook/development_tools/debugging/config_log.html ## Contributing Contributions are welcome! Open a pull request to fix a bug, or [open an issue][] to discuss a new feature or change. Check out the [Contributing][] section in the docs for more info. [Contributing]: CONTRIBUTING.md [open an issue]: https://github.com/rnag/sensible-env-logger/issues ## License This project is proudly licensed under the MIT license ([LICENSE](LICENSE) or http://opensource.org/licenses/MIT). `sensible-env-logger` can be distributed according to the MIT license. Contributions will be accepted under the same license. ## Authors * [Ritvik Nag](https://github.com/rnag)