For a list of documents, see: Rust Mobile Complex Graphics Rendering Project Development Series summary (table of Contents)

(Last update: 2018-12-20 added [Log color]) Based on the usage summary of log, ENV_LOGGER, fern, etc., detailed configuration suggestions refer to the official instructions.

Add third-party log library dependencies to the project

To Cargo. Toml, log is the standard library for logging requirements of Rust projects. Env_logger provides a specific implementation, similar to the policy pattern: Log defines the operations, and env_logger implements the concrete behavior, making it easy to switch to another library that implements the log-defined interface, such as Daboross /fern.

[dependencies]
log = "0.4.0"
env_logger = "0.6.0"
Copy the code

Env_log configuration

The following describes the env_log configuration for our project. Error: Changing format causes terminal logs to be colorless.

Set the output time to the local time

Env_logger uses 0 time zone by default, while Beijing is east zone 8. Each log output is less than 8 hours, so it is inconvenient to analyze logs if the time is not matched. 0 time zone for example:

INFO 2018-11-18T02:00:08Z: webgpu-native::registry: env_logger initialized.
Copy the code

The example code of env_logger output local time is given below, referring to DCjanus/ Nabu. It can be used for ENv_logger by using Flexi_LOGGER with slight adjustment. The key to adding more customization information is to modify writeln! Macro.

#[macro_use]
extern crate log;
extern crate chrono;
extern crate env_logger;

fn init_log() {
    use chrono::Local;
    use std::io::Write;

    let env = env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "trace");
    env_logger::Builder::from_env(env)
        .format(|buf, record| {
            writeln!(
                buf,
                "{} {} [{}] {}",
                Local::now().format("%Y-%m-%d %H:%M:%S"),
                record.level(),
                record.module_path().unwrap_or("<unnamed>"), &record.args() ) }) .init(); info! ("env_logger initialized.");
}
Copy the code

The above code must start at fn main() or lazy_static, otherwise the initial part of the log will not be affected by the new configuration. The log configuration placed in lazy_static needs to be manually enabled, for example

/ / define lazy_static! { pub(crate) static ref HUB: Hub = { init_log(); Hub::default() }; } // Call the HUB first in an entry function and "force" it to execute the lazy_static block fnentry_point() { &*HUB; // "force" execution of the lazy_static block hub.some_method (); // Info! () and so on can be normally output to a file or console}Copy the code

As an example, the following information is displayed:

[2018-11-18T02:00:08z INFO webGPU-native ::registry] env_logger initialized. // 2018-11-1809:27:43 INFO [webgpu-native::registry] env_logger initialized.Copy the code

Add the line number to the log

writeln!(
    buf,
    "{} {} / {}, {} {}",
    Local::now().format("%Y-%m-%d %H:%M:%S"),
    record.level(),
    record.module_path().unwrap_or("<unnamed>"),
    record.line().unwrap_or(0),
    &record.args()
)
Copy the code

Execute the display:

2018-11-18 10:38:41 INFO [webgpu-native::registry:87] env_logger initialized.
Copy the code

Adding a log file name

writeln!(
    buf,
    "{} {} / {}, {}, {} {}",
    Local::now().format("%Y-%m-%d %H:%M:%S"),
    record.level(),
    record.module_path().unwrap_or("<unnamed>"),
    record.line().unwrap_or(0),
    &record.args()
)
Copy the code

Execute the display:

2018-11-18 10:38:48 INFO [webgpu-native::registry:webgpu-native/src/registry.rs:87] env_logger initialized.
Copy the code

Log levels are aligned left and right

  1. The left
    writeln!(
        buf,
        "{: < 5} {} / {}, {} {}",
        record.level(),
        // same as previous content
    )
    Copy the code
  2. Align right
    writeln!(
        buf,
        "{: > 5} {} / {}, {} {}",
        record.level(),
        // same as previous content
    )
    Copy the code

What is the easiest way to pad a string with 0 to the left?

Log color

Without changing format, env_LOGGER uses different colors to mark level logs by default. The previous modification makes this feature “invalid”, which is not intuitive. Of course, CLion and others can use Grep Console plug-in to color the log. If it is executed in Terminal, we have to change the format to add color. Env_logger-0.6.0 / SRC/FMT /mod. Rs DefaultFormatter source code.

    use std::io::Write;
    let env = env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "trace");
    let mut builder = env_logger::Builder::from_env(env);
    println!("builder = {:? }", builder);
    builder
        .format(|buf, record| {
            let level = { buf.default_styled_level(record.level()) };
            write!(buf, "{}".format_args!("{: > 5}", level));
            writeln!(buf, "{}", &record.args())
        })
        .init();
Copy the code

Filtering log Levels

env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "trace");
Copy the code

Filter_or () can be configured with the following built-in values to filter different levels of logs. In fact, we can also add filter tag:

  • “trace”
  • “info”
  • “debug”
  • “warn”
  • “error”

You can also pass command line arguments during program execution for filtering:

$ RUST_LOG=info ./main
[2018-11-03T06:09:06Z INFO  default] starting up
Copy the code

Combined filtration condition

Add depX=Y to filter all logs at the highest level. For example, if the log level is higher than info or debug, the log level is higher than info.

RUST_LOG=info,dep1=debug ./main
Copy the code

Dynamic Filtering information

In complex projects, there are often multiple modules. As a developer of one module, it is very common to filter out the logs of other modules in order to locate the problems of the module he is in charge of. Since the whole project usually uses the same log library, it is obviously unreasonable to comment the log output of other modules line by line. In addition, while the console can do filtering, multi-conditional filtering rules can be difficult to write and may not be supported by log viewing systems. In fact, we can add filtering logic to the format() we have been modifying, for example:

format(|buf, record| {
    // special format for debug messages coming from our own crate.
    if record.level() > log::LevelFilter::Info && record.target() == "my_module"{ write! (...). }else if/* some condition */ { write! (...). }else if/* some condition 2*/ { write! (...). }else{ write! (...). }}Copy the code

The implementation of the filtering logic is fern/cmd-program.rs.

Fern, another option for env_logger

daboross/fern

Simple, efficient logging for Rust

Fern is much more intuitive to configure (as shown below) and I haven’t tested its performance against ENv_Logger yet.

// Configure logger at runtime
fern::Dispatch::new()
    // Perform allocation-free log formatting
    .format(|out, message, record| {
        out.finish(format_args!(
            "[{}] [] {} {} {}",
            chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
            record.target(),
            record.level(),
            message
        ))
    })
    // Add blanket level filter -
    .level(log::LevelFilter::Debug)
    // - and per-module overrides
    .level_for("hyper", log::LevelFilter::Info)
    // Output to stdout, files, and other Dispatch configurations
    .chain(std::io::stdout())
    .chain(fern::log_file("output.log")?// Apply globally.apply()? ;// and log using log crate macros!info! ("helllo, world!");
Copy the code

Improving log Performance

todo