[][src]Module flexi_logger::code_examples

Here are some examples for the flexi_logger initialization.

Contents

Write logs to stderr

Expect the log specification in the environment variable RUST_LOG:

Logger::with_env().start()?;

(if RUST_LOG is not set, or if its value cannot be interpreted, nothing is logged)

or provide the log spec programmatically:

Logger::with_str("info").start()?;

or combine both options:

Logger::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 Logger::log_target you can send the logs to stdout, a file, an implementation of LogWriter, or write them not at all.

When writing to files, you sometimes want to have parts of the log still 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::with_str("info")
   .log_target(LogTarget::File)              // write logs to file
   .duplicate_to_stderr(Duplicate::Warn)     // print warnings and errors also to the console
   .start()?;

Use buffering to reduce I/O overhead

By default, every log line is directly written to the output, without buffering. This allows seeing new log lines in real time.

Using buffering reduces the program's I/O overhead, and thus increases overall performance, which can be important if logging is used heavily.

Note that with buffering you should keep the LoggerHandle and call shutdown at the very end of your program to ensure that all buffered log lines are flushed before the program terminates.

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let logger = Logger::with_str("info")
       .log_target(LogTarget::File)
       .use_buffering(true)
       .start()?;
    // ... do all your work ...
    logger.shutdown();
    Ok(())
}

If logging is used with low frequency, buffering can delay the appearance of log lines significantly. To avoid that, you can get the buffers flushed automatically in regular intervals.

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let logger = Logger::with_str("info")
       .log_target(LogTarget::File)
       .buffer_and_flush()
       .start()?;
    // ... do all your work ...
    logger.shutdown();
    Ok(())
}

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 Logger:directory you can specify a concrete folder in which the files should be created.

Using Logger::discriminant you can add a discriminating infix to the log file name.

With Logger::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.

create_symlink(path) creates (on unix-systems only) a symbolic link at the specified path that points to the log file.

Logger::with_str("info")
   .log_to_file()                            // write logs to file
   .directory("traces")                      // create files in folder ./traces
   .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 "Log is written to ./traces/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

Adaptive Coloring

You can use coloring for stdout and/or stderr conditionally, such that colors are used when the output goes to a tty, and suppressed if you e.g. pipe the output to some other program. With Logger::adaptive_format_for_stderr or Logger::adaptive_format_for_stdout you can specify one of the provided format pairs (which are based on the format functions listed above), or you can provide your own colored and non-colored format functions.

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 Logger::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::with_str("info")      // Write all error, warn, and info messages
    .log_to_file()            // Write the log to a file
    .suppress_timestamp()     // use a simple filename without a timestamp
    .append()                 // do not truncate the log file when the program is restarted
    .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 to define its behavior:

  • Criterion

    • 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
  • Naming
    The current file is then renamed

    • with Naming::Timestamps to something like foo_r2020-11-16_08-56-52.log
    • with Naming::Numbers to something like foo_r00000.log

    and a fresh rCURRENT file is created.

  • 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.
Logger::with_str("info")          // Write all error, warn, and info messages
    .log_to_file()                // Write the log to a file
    .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 traces only for a short instant.

Obtain the LoggerHandle

let mut logger = Logger::with_str("info")
    // ... 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::with_str("info")
    // ... 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 traces for some requests to a long running server.

See Logger::start_with_specfile for more information.

Miscellaneous

For the sake of completeness, we refer here to some more configuration methods. See their documentation for more details.

Logger::check_parser_error

Logger::set_palette

Logger::cleanup_in_background_thread

Logger::use_windows_line_ending

Logger::add_writer