Logging

Kompact uses the slog crate to provide system wide logging facilities. The basic macros for this, slog::{crit, debug, error, info, o, trace, warn} are re-exported in the prelude for convenience. Logging works out of the box with a default asynchronous console logger implementation that roughly corresponds to the following setup code:

let decorator = slog_term::TermDecorator::new().stdout().build();
let drain = slog_term::FullFormat::new(decorator).build().fuse();
let drain = slog_async::Async::new(drain).chan_size(1024).build().fuse();
let logger = slog::Logger::root_typed(Arc::new(drain));

The actual logging levels are controlled via build features. The default features correspond to max_level_trace and release_max_level_info, that is in debug builds all levels are shown, while in the release profile only info and more severe message are shown. Alternatively, Kompact provides a slightly less verbose feature variant called silent_logging, which is equivalent to max_level_info and release_max_level_error.

This is exemplified in the following very simple code example:

#![allow(clippy::unused_unit)]
use kompact::prelude::*;
use std::time::Duration;

pub fn main() {
    let system = KompactConfig::default().build().expect("system");
    trace!(
        system.logger(),
        "You will only see this in debug builds with default features"
    );
    debug!(
        system.logger(),
        "You will only see this in debug builds with default features"
    );
    info!(system.logger(), "You will only see this in debug builds with silent_logging or in release builds with default features");
    warn!(system.logger(), "You will only see this in debug builds with silent_logging or in release builds with default features");
    error!(system.logger(), "You will always see this");

    // remember that logging is asynchronous and won't happen if the system is shut down already
    std::thread::sleep(Duration::from_millis(100));
    system.shutdown().expect("shutdown");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_logging() {
        main();
    }
}

Try to run it with a few different build settings and see what you get.

Note: As before, if you have checked out the examples folder you can run the concrete binary with:

cargo run --release --bin logging

Custom Logger

Sometimes the default logging configuration is not sufficient for a particular application. For example, you might need a larger queue size in the Async drain, or you may want to write to a file instead of the terminal.

In the following example we replace the default terminal logger with a file logger, logging to /tmp/myloggingfile instead. We also increase the queue size in the Async drain to 2048, so that it fits the 2048 logging events we are sending it short succession later. In order to replace the default logger, we use the KompactConfig::logger(...) function.

#![allow(clippy::unused_unit)]
use kompact::prelude::*;
use std::{fs::OpenOptions, sync::Arc, time::Duration};

const FILE_NAME: &str = "/tmp/myloggingfile";

pub fn main() {
    let mut conf = KompactConfig::default();
    let logger = {
        let file = OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(FILE_NAME)
            .expect("logging file");

        // create logger
        let decorator = slog_term::PlainSyncDecorator::new(file);
        let drain = slog_term::FullFormat::new(decorator).build().fuse();
        let drain = slog_async::Async::new(drain).chan_size(2048).build().fuse();
        slog::Logger::root_typed(
            Arc::new(drain),
            o!(
            "location" => slog::PushFnValue(|r: &slog::Record<'_>, ser: slog::PushFnValueSerializer<'_>| {
                ser.emit(format_args!("{}:{}", r.file(), r.line()))
            })),
        )
    };
    conf.logger(logger);
    let system = conf.build().expect("system");
    trace!(
        system.logger(),
        "You will only see this in debug builds with default features"
    );
    debug!(
        system.logger(),
        "You will only see this in debug builds with default features"
    );
    info!(system.logger(), "You will only see this in debug builds with silent_logging or in release builds with default features");
    warn!(system.logger(), "You will only see this in debug builds with silent_logging or in release builds with default features");
    error!(system.logger(), "You will always see this");

    for i in 0..2048 {
        info!(system.logger(), "Logging number {}.", i);
    }

    // remember that logging is asynchronous and won't happen if the system is shut down already
    std::thread::sleep(Duration::from_millis(1000));
    system.shutdown().expect("shutdown");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_logging() {
        main();
        std::fs::remove_file(FILE_NAME).expect("remove log file");
    }
}

Note: As before, if you have checked out the examples folder you can run the concrete binary and then show the logging file with:

cargo run --release --bin logging_custom
cat /tmp/myloggingfile