Expand description
Efficient, configurable logging in Rust.
§fern 0.4.4, 0.5.*, 0.6.* security warning - colored
feature + global allocator
One of our downstream dependencies, atty, through colored, has an unsoundness issue: https://rustsec.org/advisories/RUSTSEC-2021-0145.html.
This shows up in one situation: if you’re using colored
0.1.0 (the crate, or our
feature), and a custom global allocator.
Upgrade to fern
0.7.0, and colored
0.2.0 if you depend on it directly, to fix this issue.
§Depending on fern
Ensure you require both fern and log in your project’s Cargo.toml
:
[dependencies]
log = "0.4"
fern = "0.7"
§Example setup
With fern, all logger configuration is done via builder-like methods on
instances of the Dispatch
structure.
Here’s an example logger which formats messages, and sends everything Debug and above to both stdout and an output.log file:
use log::{debug, error, info, trace, warn};
use std::time::SystemTime;
fn setup_logger() -> Result<(), fern::InitError> {
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"[{} {} {}] {}",
humantime::format_rfc3339_seconds(SystemTime::now()),
record.level(),
record.target(),
message
))
})
.level(log::LevelFilter::Debug)
.chain(std::io::stdout())
.chain(fern::log_file("output.log")?)
.apply()?;
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
setup_logger()?;
info!("Hello, world!");
warn!("Warning!");
debug!("Now exiting.");
Ok(())
}
Let’s unwrap this:
fern::Dispatch::new()
Dispatch::new
creates an empty configuration.
.format(|out, message, record| {
out.finish(format_args!(
"..."
))
})
This incantation sets the Dispatch
format! The closure taking in
out, message, record
will be called once for each message going through
the dispatch, and the formatted log message will be used for any downstream
consumers.
Do any work you want in this closure, and then call out.finish
at the end.
The callback-style result passing with out.finish(format_args!())
lets us
format without any intermediate string allocation.
format_args!
has the same format as println!
, just returning a
not-yet-written result we can use internally.
std::time::SystemTime::now()
std::time::SystemTime::now
retrieves the current time.
humantime::format_rfc3339_seconds(
// ...
)
humantime::format_rfc3339_seconds
formats the current time into an
RFC3339 timestamp, with second-precision.
RFC3339 looks like 2018-02-14T00:28:07Z
, always using UTC, ignoring system
timezone.
humantime
is a nice light dependency, but only offers this one format.
For more custom time formatting, I recommend
chrono
or time
.
Now, back to the Dispatch
methods:
.level(log::LevelFilter::Debug)
Sets the minimum logging level for all modules, if not overwritten with
Dispatch::level_for
, to Level::Debug
.
.chain(std::io::stdout())
Adds a child to the logger. With this, all messages which pass the filters will be sent to stdout.
Dispatch::chain
accepts Stdout
, Stderr
, File
and other
Dispatch
instances.
.chain(fern::log_file("output.log")?)
Adds a second child sending messages to the file “output.log”.
See log_file
.
// ...
.apply()
Consumes the configuration and instantiates it as the current runtime global logger.
This will fail if and only if .apply()
or equivalent form another crate
has already been used this runtime.
Since the binary crate is the only one ever setting up logging, and it’s
usually done near the start of main
, the Dispatch::apply
result can be
reasonably unwrapped: it’s a bug if any crate is calling this method more
than once.
The final output will look like:
[2023-03-18T20:12:50Z INFO cmd_program] Hello, world!
[2023-03-18T20:12:50Z WARN cmd_program] Warning!
[2023-03-18T20:12:50Z DEBUG cmd_program] Now exiting.
§Logging
Once the logger has been set, it will pick up all logging calls from your crate and all libraries you depend on.
fern::Dispatch::new()
// ...
.apply()?;
trace!("Trace message");
debug!("Debug message");
info!("Info message");
warn!("Warning message");
error!("Error message");
§More
The Dispatch
documentation has example usages of each method, and the
full example program might be useful for using fern in a larger
application context.
See the colors module for examples using ANSI terminal coloring.
See the syslog module for examples outputting to the unix syslog, or the syslog full example program for a more realistic sample.
See the meta module for information on getting logging-within-logging working correctly.
Modules§
- Support for ANSI terminal colors via the colored crate.
- Fern supports logging most things by default, except for one kind of struct: structs which make log calls to the global logger from within their
Display
orDebug
implementations. - Example usage of
fern
with thesyslog
crate.
Structs§
- This is used to generate log file suffixed based on date, hour, and minute.
- The base dispatch logger.
- Callback struct for use within a formatter closure
- Configuration for a logger output.
- Logger which will panic whenever anything is logged. The panic will be exactly the message of the log.
Enums§
- Convenience error combining possible errors which could occur while initializing logging.
Functions§
- Convenience method for opening a log file with common options.
- Convenience method for opening a re-openable log file with common options.
- Convenience method for opening a re-openable log file with common options.
Type Aliases§
- A type alias for a log filter. Returning true means the record should succeed - false means it should fail.
- A type alias for a log formatter.