Logging

Recipe Crates Categories
Log a debug message to the console log-badge env_logger-badge cat-debugging-badge
Log an error message to the console log-badge env_logger-badge cat-debugging-badge
Enable log levels per module log-badge env_logger-badge cat-debugging-badge
Log to stdout instead of stderr log-badge env_logger-badge cat-debugging-badge
Log messages with a custom logger log-badge cat-debugging-badge
Use a custom environment variable to set up logging log-badge env_logger-badge cat-debugging-badge
Include timestamp in log messages log-badge env_logger-badge chrono-badge cat-debugging-badge
Log to the Unix syslog log-badge syslog-badge cat-debugging-badge
Log messages to a custom location log-badge cat-debugging-badge

Log a debug message to the console

log-badge env_logger-badge cat-debugging-badge

The log crate provides logging utilities. The env_logger crate configures logging via an environment variable.

#[macro_use]
extern crate log;
extern crate env_logger;
#
# #[macro_use]
# extern crate error_chain;
#
# error_chain! {
#     foreign_links {
#         SetLogger(log::SetLoggerError);
#     }
# }

fn execute_query(query: &str) {
    debug!("Executing query: {}", query);

    // then do the thing
}

fn run() -> Result<()> {
    env_logger::init()?;

    execute_query("DROP TABLE students");

    Ok(())
}
#
# quick_main!(run);

If you run this code, you'll notice that no output is printed. By default, the log level is error, and any lower levels are dropped.

We can change that easily by setting the RUST_LOG environment variable:

$ RUST_LOG=debug cargo run

After running this, you'll likely see a pile of logs from cargo, as well as the following line at the very end of the output:

DEBUG:main: Executing query: DROP TABLE students

Log an error message to the console

log-badge env_logger-badge cat-debugging-badge

#[macro_use]
extern crate log;
extern crate env_logger;
#
# #[macro_use]
# extern crate error_chain;
#
# error_chain! {
#     foreign_links {
#         SetLogger(log::SetLoggerError);
#     }
# }

fn execute_query(_query: &str) -> Result<()> {
    // Do the thing, or maybe not

    bail!("I'm afraid I can't do that")
}

fn run() -> Result<()> {
    env_logger::init()?;

    let response = execute_query("DROP TABLE students");
    if let Err(err) = response {
        // Log the error message and continue
        error!("Failed to execute query: {}", err);
    }

    Ok(())
}
#
# quick_main!(run);

Run this code with cargo run and you should see the following line:

DEBUG:main: Failed to execute query: I'm afraid I can't do that

Enable log levels per module

log-badge env_logger-badge cat-debugging-badge

Creates two modules foo and nested foo::bar with logging directives controlled separately with RUST_LOG environmental variable.

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

mod foo {
    mod bar {
        pub fn run() {
            warn!("[bar] warn");
            info!("[bar] info");
            debug!("[bar] debug");
        }
    }

    pub fn run() {
        warn!("[foo] warn");
        info!("[foo] info");
        debug!("[foo] debug");
        bar::run();
    }
}
#
# error_chain! {
#     foreign_links {
#         SetLogger(log::SetLoggerError);
#     }
# }

fn run() -> Result<()> {
    env_logger::init()?;
    warn!("[root] warn");
    info!("[root] info");
    debug!("[root] debug");
    foo::run();

    Ok(())
}
#
# quick_main!(run);

env_logger output is controlled by RUST_LOG environmental variable on per module basis with comma separated entries in format path::to::module=log_level. Running the test application as follows:

RUST_LOG="warn,test::foo=info,test::foo::bar=debug" ./test

Sets the default log::LogLevel to warn, module's foo and module's foo::bar respectively to info and debug. The output is:

WARN:test: [root] warn
WARN:test::foo: [foo] warn
INFO:test::foo: [foo] info
WARN:test::foo::bar: [bar] warn
INFO:test::foo::bar: [bar] info
DEBUG:test::foo::bar: [bar] debug

Log to stdout instead of stderr

log-badge env_logger-badge cat-debugging-badge

Creates a custom logger configuration using the LogBuilder::target to set the target of the log output to Target::Stdout.

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

use env_logger::{LogBuilder, LogTarget};
#
# error_chain! {
#     foreign_links {
#         SetLogger(log::SetLoggerError);
#     }
# }

fn run() -> Result<()> {
    LogBuilder::new()
        .target(LogTarget::Stdout)
        .init()?;

    error!("This error has been printed to Stdout");
    Ok(())
}
#
# quick_main!(run);

Log messages with a custom logger

log-badge cat-debugging-badge

Implements a custom logger ConsoleLogger which prints to stdout. In order to use the logging macros, ConsoleLogger implements the log::Log trait and has to be installed via log::set_logger.

# #[macro_use]
# extern crate error_chain;
#[macro_use]
extern crate log;

use log::{LogRecord, LogLevel, LogMetadata, LogLevelFilter};

struct ConsoleLogger;

impl log::Log for ConsoleLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            println!("Rust says: {} - {}", record.level(), record.args());
        }
    }
}
#
# error_chain! {
#     foreign_links {
#         SetLogger(log::SetLoggerError);
#     }
# }

fn run() -> Result<()> {
    log::set_logger(|max_log_level| {
                        max_log_level.set(LogLevelFilter::Info);
                        Box::new(ConsoleLogger)
                    })?;

    info!("hello log");
    warn!("warning");
    error!("oops");
    Ok(())
}
#
# quick_main!(run);

Use a custom environment variable to set up logging

log-badge env_logger-badge cat-debugging-badge

Logging is configured with LogBuilder.

LogBuilder::parse parses MY_APP_LOG environmental variable contents in the form of RUST_LOG syntax. Then LogBuilder::init initializes the logger. All these steps are normally done internally by env_logger::init.

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

use std::env;
use env_logger::LogBuilder;

# error_chain! {
#     foreign_links {
#         EnvLogger(log::SetLoggerError);
#     }
# }
# 
fn run() -> Result<()> {
    LogBuilder::new()
        .parse(&env::var("MY_APP_LOG").unwrap_or_default())
        .init()?;

    info!("informational message");
    warn!("warning message");
    error!("this is an error {}", "message");

    Ok(())
}
#
# quick_main!(run);

Include timestamp in log messages

log-badge env_logger-badge chrono-badge cat-debugging-badge

Creates a custom logger configuration with LogBuilder. Each log entry calls Local::now to get the current DateTime in local timezone and uses DateTime::format with strftime::specifiers to format a timestamp used in the final log.

The example calls LogBuilder::format to set a closure which formats each message text with timestamp, LogRecord::level and body (LogRecord::args).

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

use std::env;
use env_logger::LogBuilder;
use chrono::Local;

#
# error_chain! {
#     foreign_links {
#         SetLogger(log::SetLoggerError);
#     }
# }

fn run() -> Result<()> {
    LogBuilder::new()
        .format(|record| {
                    format!("{} [{}] - {}",
                            Local::now().format("%Y-%m-%dT%H:%M:%S"),
                            record.level(),
                            record.args())
                })
        .parse(&env::var("MY_APP_LOG").unwrap_or_default())
        .init()?;

    warn!("warn");
    info!("info");
    debug!("debug");
    Ok(())
}
#
# quick_main!(run);

Calling MY_APP_LOG="info" cargo run will result in similar output:

2017-05-22T21:57:06 [WARN] - warn
2017-05-22T21:57:06 [INFO] - info

Log to the Unix syslog

log-badge syslog-badge cat-debugging-badge

Logs messages to UNIX syslog. Initializes logger backend with syslog::init. syslog::Facility indicates type of program submitting log, log::LogLevelFilter denotes allowed log verbosity and Option<&str> holds optional application name.

# #![allow(unused_imports)]
# #[macro_use]
# extern crate error_chain;
#[macro_use]
extern crate log;
# #[cfg(target_os = "linux")]
extern crate syslog;

use log::LogLevelFilter;
# #[cfg(target_os = "linux")]
use syslog::Facility;
#
# #[cfg(target_os = "linux")]
# error_chain! {
#     foreign_links {
#         SetLogger(syslog::SyslogError);
#     }
# }

# #[cfg(target_os = "linux")]
fn run() -> Result<()> {
    syslog::init(Facility::LOG_USER,
                 LogLevelFilter::Debug,
                 Some("My app name"))?;
    debug!("this is a debug {}", "message");
    error!("this is an error!");
    Ok(())
}
#
# #[cfg(not(target_os = "linux"))]
# error_chain! {}
# #[cfg(not(target_os = "linux"))]
# fn run() -> Result<()> {
#     Ok(())
# }
#
# quick_main!(run);

Log messages to a custom location

log-badge log4rs-badge cat-debugging-badge

Configures log to be output into custom location with log4rs. log4rs can use either an external YAML file or a programmatically constructed configuration.

Firstly creates the log configuration with log4rs::append::file::FileAppender using a custom pattern from log4rs::encode::pattern.

Secondly assigns it to the log4rs::config::Config which has a root appender that uses the previously created logfile appender. Subsequently sets the default log::LogLevelFilter so that any logs with Info level or higher will be sent to the logger.

# #[macro_use]
# extern crate error_chain;
#[macro_use]
extern crate log;
extern crate log4rs;

use log::LogLevelFilter;
use log4rs::append::file::FileAppender;
use log4rs::encode::pattern::PatternEncoder;
use log4rs::config::{Appender, Config, Root};
#
# error_chain! {
#     foreign_links {
#         Io(std::io::Error);
#         LogConfig(log4rs::config::Errors);
#         SetLogger(log::SetLoggerError);
#     }
# }

fn run() -> Result<()> {
    let logfile = FileAppender::builder()
        .encoder(Box::new(PatternEncoder::new("{l} - {m}\n")))
        .build("log/output.log")?;

    let config = Config::builder()
        .appender(Appender::builder().build("logfile", Box::new(logfile)))
        .build(Root::builder()
                   .appender("logfile")
                   .build(LogLevelFilter::Info))?;

    log4rs::init_config(config)?;

    info!("Hello, world!");

    Ok(())
}
#
# quick_main!(run);