[Awesome Rust] log : A lightweight logging facade
log
log is a lightweight logging facade.
The log crate provides a single logging API that abstracts over the actual logging implementation. Libraries can use the logging API provided by this crate, and the consumer of those libraries can choose the logging implementation that is most suitable for its use case.
If no logging implementation is selected, the facade falls back to a “noop” implementation that ignores all log messages. The overhead in this case is very small - just an integer load, comparison and jump.
A log request consists of a target, a level, and a body. A target is a string which defaults to the module path of the location of the log request, though that default may be overridden. Logger implementations typically use the target to filter requests based on some user configuration.
Dependencies
By default, env_logger can be depended on with:
1 | # Cargo.toml |
Use
The basic use of the log crate is through the five logging macros: error!
, warn!
, info!
, debug!
and trace!
where error!
represents the highest-priority log messages and trace!
the lowest. The log messages are filtered by configuring the log level to exclude messages with a lower priority. Each of these macros accept format strings similarly to println!
.
Executables should choose a logging implementation and initialize it early in the runtime of the program. Logging implementations will typically include a function to do this. Any log messages generated before the implementation is initialized will be ignored.
The executable itself may use the log crate to log as well.
Warning
The logging system may only be initialized once.
Available logging implementations
In order to produce log output executables have to use a logger implementation compatible with the facade. There are many available implementations to choose from, here are some of the most popular ones:
-
Simple minimal loggers:
-
env_logger
-
simple_logger
-
simplelog
-
pretty_env_logger
-
stderrlog
-
flexi_logger
-
-
Complex configurable frameworks:
-
log4rs
-
fern
-
-
Adaptors for other facilities:
-
syslog
-
slog-stdlog
-
Implementing a Logger
Loggers implement the Log
trait. Here’s a very basic example that simply logs all messages at the Error
, Warn
or Info
levels to stdout
:
1 | use log::{Record, Level, Metadata}; |
Loggers are installed by calling the set_logger
function. The maximum log level also needs to be adjusted via the set_max_level
function. The logging facade uses this as an optimization to improve performance of log messages at levels that are disabled. It’s important to set it, as it defaults to Off
, so no log messages will ever be captured! In the case of our example logger, we’ll want to set the maximum log level to Info
, since we ignore any Debug
or Trace
level log messages. A logging implementation should provide a function that wraps a call to set_logger
and set_max_level
, handling initialization of the logger:
1 | use log::{SetLoggerError, LevelFilter}; |
Implementations that adjust their configurations at runtime should take care to adjust the maximum log level as well.
Use with std
set_logger requires you to provide a &'static
Log, which can be hard to obtain if your logger depends on some runtime configuration. The set_boxed_logger
function is available with the std Cargo feature. It is identical to set_logger
except that it takes a Box<Log>
rather than a &'static
Log:
1 | pub fn init() -> Result<(), SetLoggerError> { |
Compile time filters
Log levels can be statically disabled at compile time via Cargo features. Log invocations at disabled levels will be skipped and will not even be present in the resulting binary. This level is configured separately for release and debug builds. The features are:
-
max_level_off
-
max_level_error
-
max_level_warn
-
max_level_info
-
max_level_debug
-
max_level_trace
-
release_max_level_off
-
release_max_level_error
-
release_max_level_warn
-
release_max_level_info
-
release_max_level_debug
-
release_max_level_trace
These features control the value of the STATIC_MAX_LEVEL
constant. The logging macros check this value before logging a message. By default, no levels are disabled.
Libraries should avoid using the max level features because they’re global and can’t be changed once they’re set.
For example, a crate can disable trace level logs in debug builds and trace, debug, and info level logs in release builds with the following configuration:
1 | [dependencies] |
Crate Feature Flags
The following crate feature flags are available in addition to the filters. They are configured in your Cargo.toml.
std allows use of std crate instead of the default core. Enables using std::error and set_boxed_logger functionality.
serde enables support for serialization and deserialization of Level and LevelFilter.
1 | [dependencies] |
References
[1] rust-lang/log: Logging implementation for Rust - https://github.com/rust-lang/log
[2] log - crates.io: Rust Package Registry - https://crates.io/crates/log