Module flexi_logger::code_examples [−][src]
Expand description
Here are some examples for the flexi_logger
initialization.
Contents
- Start minimal: Write logs to stderr
- Choose the log output channel
- Choose the write mode
- Influence the location and name of the log file
- Specify the format for the log lines explicitly
- Use a fixed log file, and truncate or append the file on each program start
- Rotate the log file
- Reconfigure the log specification programmatically
- Reconfigure the log specification dynamically by editing a spec-file
- Reconfigure the file log writer
Start minimal: Write logs to stderr
Choose one of three options to specify which log output you want to see, and call start:
-
Use
Logger::try_with_env
to provide the log specification in the environment variableRUST_LOG
:Logger::try_with_env()?.start()?;
Note that if
RUST_LOG
is not set, or if its value cannot be interpreted, nothing is logged. -
Use
Logger::try_with_str
to provide the log specification programmatically:Logger::try_with_str("info")?.start()?;
-
or use
Logger::try_with_env_or_str
to combine both options:Logger::try_with_env_or_str("info")?.start()?;
After that, you just use the log-macros from the log crate.
Choose the log output channel
By default, logs are written to stderr
.
With one of
Logger::log_to_stdout
,
Logger::log_to_file
,
Logger::log_to_writer
,
Logger::log_to_file_and_writer
,
or Logger::do_not_log
,
you can send the logs to other destinations, or write them not at all.
When writing to files or to a writer,
you sometimes want to see some parts of the log additionally on the terminal;
this can be achieved with
Logger::duplicate_to_stderr
or
Logger::duplicate_to_stdout
,
which duplicate log messages to the terminal.
Logger::try_with_str("info")?
.log_to_file(FileSpec::default()) // write logs to file
.duplicate_to_stderr(Duplicate::Warn) // print warnings and errors also to the console
.start()?;
Choose the write mode
By default, every log line is directly written to the output, without buffering. This allows seeing new log lines in real time.
With Logger::write_mode
you have some options to change this behavior, e.g.
-
with
WriteMode::BufferAndFlushWith
, you can reduce the program’s I/O overhead and thus increase overall performance, which can be relevant if logging is used heavily. In addition, to keep a short maximum wait time until a log line is visible in the output channel, an extra thread is created that flushes the buffers regularly.fn main() -> Result<(), Box<dyn std::error::Error>> { let _logger = Logger::try_with_str("info")? .log_to_file(FileSpec::default()) .write_mode(WriteMode::BufferAndFlush) .start()?; // ... do all your work ... Ok(()) }
-
with
WriteMode::Async
orWriteMode::AsyncWith
, logs are sent from your application threads through an unbounded channel to an output thread, which does the output (and the rotation and the cleanup, if applicable). Additionally the output is buffered, and a bounded message pool is used to reduce allocations, and flushing is used to avoid long delays. If duplication is used, the messages tostdout
orstderr
are written synchronously.fn main() -> Result<(), Box<dyn std::error::Error>> { let _logger = Logger::try_with_str("info")? .log_to_file(FileSpec::default()) .write_mode(WriteMode::Async) .start()?; // ... do all your work ... Ok(()) }
Note that with all write modes
except WriteMode::Direct
(which is the default)
you should keep the LoggerHandle
alive
up to the very end of your program,
because it will, in its Drop implementation, flush all writers
to ensure that all buffered log lines are flushed before the program terminates,
and then it calls their shutdown method.
Influence the location and name of the log file
By default, the log files are created in the current directory (where the program was started).
With FileSpec:directory
you can specify a concrete folder in which the files should be created.
Using FileSpec::discriminant
you can add a discriminating infix to the log file name.
With FileSpec::suffix
you can change the suffix that is used for the log files.
When writing to files, especially when they are in a distant folder, you may want to let the user know where the log file is.
Logger::print_message
prints an info to stdout
to which file the log is written.
Logger::create_symlink
creates (on unix-systems only) a symbolic link at the specified path that points to the log file.
Logger::try_with_str("info")?
.log_to_file(
FileSpec::default()
.directory("log_files") // create files in folder ./log_files
.basename("foo")
.discriminant("Sample4711A") // use infix in log file name
.suffix("trc") // use suffix .trc instead of .log
)
.print_message() //
.create_symlink("current_run") // create a symbolic link to the current log file
.start()?;
This example will print a message like
“Log is written to ./log_files/foo_Sample4711A_2020-11-17_19-24-35.trc
”
and, on unix, create a symbolic link called current_run
.
Specify the format for the log lines explicitly
With Logger::format
you set the format for all used output channels of flexi_logger
.
flexi_logger
provides a couple of format functions, and you can also create and use your own,
e.g. by copying and modifying one of the provided format functions.
Depending on the configuration, flexi_logger
can write logs to multiple channels
(stdout, stderr, files, or additional writers)
at the same time. You can control the format for each output channel individually, using
Logger::format_for_files
,
Logger::format_for_stderr
,
Logger::format_for_stdout
, or
Logger::format_for_writer
.
As argument for these functions you can use one of the provided non-coloring format functions
or one of their coloring pendants
or your own method.
Adaptive Coloring
You can use coloring for stdout
and/or stderr
conditionally, such that colors
- are used when the output goes to a tty,
- are suppressed when you e.g. pipe the output to some other program.
You achieve that
by providing one of the variants of AdaptiveFormat
to the respective
format method, e.g.
flexi_logger::Logger::try_with_str("info")?
.adaptive_format_for_stderr(AdaptiveFormat::Detailed);
Defaults
flexi_logger
initializes by default equivalently to this:
// ...
.adaptive_format_for_stderr(AdaptiveFormat::Default)
.adaptive_format_for_stdout(AdaptiveFormat::Default)
.format_for_files(default_format)
.format_for_writer(default_format)
Use a fixed log file, and truncate or append the file on each program start
With Logger::log_to_file
and without rotation,
flexi_logger
uses by default files with a timestamp in the name, like
foo_2020-11-16_08-37-44.log
(for a program called foo
), which are quite unique for each
program start.
With FileSpec::suppress_timestamp
you get a simple fixed filename, like foo.log
.
In that case, a restart of the program will truncate an existing log file.
Use additionally Logger::append
to append the logs of each new run to the existing file.
Logger::try_with_str("info")? // Write all error, warn, and info messages
// use a simple filename without a timestamp
.log_to_file(
FileSpec::default().suppress_timestamp()
)
// do not truncate the log file when the program is restarted
.append()
.start()?;
Rotate the log file
With rotation, the logs are always written to a file
with the infix rCURRENT
, like e.g. foo_rCURRENT.log
.
Logger::rotate
takes three enum arguments that define its behavior:
-
- with
Criterion::Age
the rotation happens when the clock switches to a new day, hour, minute, or second - with
Criterion::Size
the rotation happens when the current log file exceeds the specified limit - with
Criterion::AgeOrSize
the rotation happens when either of the two limits is reached
- with
-
Naming
The current file is then renamed- with
Naming::Timestamps
to something likefoo_r2020-11-16_08-56-52.log
- with
Naming::Numbers
to something likefoo_r00000.log
and a fresh
rCURRENT
file is created. - with
-
Cleanup
defines if and how you avoid accumulating log files indefinitely:- with
Cleanup::KeepLogFiles
you specify the number of log files that should be retained; if there are more, the older ones are getting deleted - with
Cleanup::KeepCompressedFiles
you specify the number of log files that should be retained, and these are being compressed additionally - with
Cleanup::KeepLogAndCompressedFiles
you specify the number of log files that should be retained as is, and an additional number that are being compressed - with
Cleanup::Never
no cleanup is done, all files are retained.
- with
Logger::try_with_str("info")? // Write all error, warn, and info messages
.log_to_file(
FileSpec::default()
)
.rotate( // If the program runs long enough,
Criterion::Age(Age::Day), // - create a new file every day
Naming::Timestamps, // - let the rotated files have a timestamp in their name
Cleanup::KeepLogFiles(7), // - keep at most 7 log files
)
.start()?;
Reconfigure the log specification programmatically
This can be especially handy in debugging situations where you want to see log output only for a short instant.
Obtain the LoggerHandle
let mut logger = Logger::try_with_str("info").unwrap()
// ... logger configuration ...
.start()
.unwrap();
and modify the effective log specification from within your code:
// ...
logger.parse_and_push_temp_spec("info, critical_mod = trace");
// ... critical calls ...
logger.pop_temp_spec();
// ... continue with the log spec you had before.
Reconfigure the log specification dynamically by editing a spec-file
If you start flexi_logger
with a specfile,
Logger::try_with_str("info").unwrap()
// ... logger configuration ...
.start_with_specfile("/server/config/logspec.toml")
.unwrap();
then you can change the log specification dynamically, while your program is running, by editing the specfile. This can be a great help e.g. if you want to get detailed log output for some requests to a long running server.
See Logger::start_with_specfile
for more information.
Reconfigure the file log writer
When using Logger::log_to_file()
, you can change most of the properties of the
embedded FileLogWriter
while the program is running using
Logger::reset_flw
.
Obtain the LoggerHandle
when the program is started
use flexi_logger::{writers::FileLogWriter, Cleanup, Criterion, FileSpec, Naming};
let logger = flexi_logger::Logger::try_with_str("info")?
.log_to_file(
FileSpec::default()
.basename("phase1")
.directory("./log_files")
)
.start()?;
log::info!("start of phase 1");
and modify the file log writer later:
logger.reset_flw(
&FileLogWriter::builder(
FileSpec::default()
.basename("phase2")
.directory("./log_files")
)
.append()
.rotate(
Criterion::Size(1024 * 1000 * 1),
Naming::Numbers,
Cleanup::KeepLogFiles(3),
),
)?;
log::info!("start of phase 2");
Miscellaneous
For the sake of completeness, we refer here to some more configuration methods. See their documentation for more details.
Logger::cleanup_in_background_thread