Crate captains_log

Crate captains_log 

Source
Expand description

§captains-log

A minimalist, customizable, easy to use logger for rust, based on the log crate, also adapted to tracing, for production and testing scenario.

§Features

  • Allow customize log format and time format. Refer to LogFormat.

  • Support subscribe log from tracing: (feature tracing). Refer to tracing_bridge.

  • Supports multiple types of sink stacking, each with its own log level.

    • LogConsole: Console output to stdout/stderr.

    • LogRawFile: Support atomic appending from multi-process on linux (with ext4, xfs)

    • LogBufFile: Write to log file with merged I/O and delay flush, and optional self-rotation.

    • Syslog: (feature syslog), usage: syslog

      Write to local or remote syslog server, with timeout and auto reconnect.

    • LogRingFile: (feature ringfile), usage: ringfile

      For deadlock / race condition debugging, collect log to ring buffer in memory, flush on panic, or triggered by signal.

  • Provide panic hook by default.

  • Provide additional macros, for example: log_assert!(), logger_assert!() ..

  • Supports signal listening for log-rotate. Refer to Builder::signal()

  • Provides many preset recipes in recipe module for convenience.

  • Supports configure by environment

  • Fine-grain log filtering. By functionality, or track the log by API request. Refer to crate::filter

  • For test suits usage:

  • Provides a LogParser to work on your log files.

§Usage

Cargo.toml

[dependencies]
log = { version = "0.4", features = ["std", "kv_unstable"] }
captains_log = "0"

lib.rs or main.rs:

// By default, reexport the macros from log crate
#[macro_use]
extern crate captains_log;

Optional feature flags:

  • syslog: Enable Syslog sink

  • ringfile: Enable RingFile sink

  • tracing: Receive log from tracing

§Recipes

You can refer to various preset recipe in recipe module.

Buffered sink with log rotation (See the definition of rotation::Rotation):

#[macro_use]
extern crate captains_log;
use captains_log::{*, rotation::*};
// rotate when log file reaches 512M. Keep max 10 archiveed files, with recent 2 not compressed.
// All archived log is moved to "/tmp/rotation/old"
let rotation = Rotation::by_size(512 * 1024 * 1024, Some(10))
    .compress_exclude(2).archive_dir("/tmp/rotation/old");
let _ = recipe::buffered_rotated_file_logger(
    "/tmp/rotation.log", Level::Debug, rotation
).build();

The following is setup two log files for different log-level:

#[macro_use]
extern crate captains_log;
use captains_log::recipe;

// You'll get /tmp/test.log with all logs, and /tmp/test.log.wf only with error logs.
let log_builder = recipe::split_error_file_logger("/tmp", "test", log::Level::Debug);
// Builder::build() is equivalent of setup_log().
log_builder.build();
// non-error msg will only appear in /tmp/test.log
debug!("Set a course to Sol system");
info!("Engage");
// will appear in both /tmp/test.log and /tmp/test.log.wf
error!("Engine over heat!");

§Customize format example

use captains_log::*;

fn format_f(r: FormatRecord) -> String {
    let time = r.time();
    let level = r.level();
    let file = r.file();
    let line = r.line();
    let msg = r.msg();
    format!("{time}|{level}|{file}:{line}|{msg}\n").to_string()
}
let debug_format = LogFormat::new(
    "%Y%m%d %H:%M:%S%.6f",
    format_f,
);
let debug_file = LogRawFile::new(
    "/tmp", "test.log", log::Level::Trace, debug_format,
);
let config = Builder::default()
    .signal(signal_hook::consts::SIGINT)
    .add_sink(debug_file);
config.build();

§Unit test example

To setup different log config on different tests.

Make sure that you call Builder::test() in test cases. which enable dynamic log config and disable signal_hook.

use captains_log::*;

#[test]
fn test1() {
    recipe::raw_file_logger(
        "/tmp/test1.log", Level::Debug).test().build().expect("setup log");
    info!("doing test1");
}

#[test]
fn test2() {
    recipe::raw_file_logger(
        "/tmp/test2.log", Level::Debug).test().build().expect("setup log");
    info!("doing test2");
}

§Best practice with rstest

We provides proc macro logfn, the following example shows how to combine with rstest.

  • When you have large test suit, you want to know which logs belong to which test case.

  • Sometimes your test crashes, you want to find the responsible test case.

  • The time spend in each test.


use rstest::*;
use captains_log::*;

// A show case that setup() fixture will be called twice, before each test.
// In order make logs available.
#[fixture]
fn setup() {
    let _logger = recipe::raw_file_logger(
        "/tmp/log_rstest.log", log::Level::Debug)
        .test().build().expect("setup_log");
}

#[logfn]
#[rstest(file_size, case(0), case(1))]
fn test_rstest_foo(setup: (), file_size: usize) {
    info!("do something111");
}

#[logfn]
#[rstest]
fn test_rstest_bar(setup: ()) {
    info!("do something222");
}

// NOTE rstest must be at the bottom to make fixture effective
#[tokio::test]
#[logfn]
#[rstest]
async fn test_rstest_async(setup: ()) {
    info!("something333")
}

Notice: the order when combine tokio::test with rstest, #[rstest] attribute must be at the bottom to make setup fixture effective.

After running the test with:

cargo test – –test-threads=1

/tmp/log_rstest.log will have this content:

[2025-07-13 18:22:39.159642][INFO][test_rstest.rs:33] <<< test_rstest_async (setup = ()) enter <<<
[2025-07-13 18:22:39.160255][INFO][test_rstest.rs:37] something333
[2025-07-13 18:22:39.160567][INFO][test_rstest.rs:33] >>> test_rstest_async return () in 564.047µs >>>
[2025-07-13 18:22:39.161299][INFO][test_rstest.rs:26] <<< test_rstest_bar (setup = ()) enter <<<
[2025-07-13 18:22:39.161643][INFO][test_rstest.rs:29] do something222
[2025-07-13 18:22:39.161703][INFO][test_rstest.rs:26] >>> test_rstest_bar return () in 62.681µs >>>
[2025-07-13 18:22:39.162169][INFO][test_rstest.rs:20] <<< test_rstest_foo (setup = (), file_size = 0) enter <<<
[2025-07-13 18:22:39.162525][INFO][test_rstest.rs:23] do something111
[2025-07-13 18:22:39.162600][INFO][test_rstest.rs:20] >>> test_rstest_foo return () in 78.457µs >>>
[2025-07-13 18:22:39.163050][INFO][test_rstest.rs:20] <<< test_rstest_foo (setup = (), file_size = 1) enter <<<
[2025-07-13 18:22:39.163320][INFO][test_rstest.rs:23] do something111
[2025-07-13 18:22:39.163377][INFO][test_rstest.rs:20] >>> test_rstest_foo return () in 58.747µs >>>

Modules§

env
filter
Fine-grain log filtering
macros
parser
recipe
The recipe module contains some prelude functions that construct a Builder for convenience use.
ringfileringfile
High speed Ring Buffer that maintained the message on memory
rotation
Rotation configuration
signal_consts
Re-export from signal_hook::consts: The signal constants.
syslogsyslog
Syslog support
tracing_bridgetracing
Tracing support

Macros§

debug
Logs a message at the debug level.
error
Logs a message at the error level.
info
Logs a message at the info level.
log_assert
Will log and panic when condition not met.
log_assert_eq
Will log and panic when condition not met.
log_debug_assert
On Debug build, will log and panic when condition not met. Skip the check on release build.
log_debug_assert_eq
On Debug build, will log and panic when condition not met. Skip the check on release build.
log_eprintln
log and println to stderr.
log_println
log and println to stdout.
logger_assert
Will log with log_filter and panic when condition not met.
logger_assert_eq
Will log with log_filter and panic when condition not met.
logger_debug
Similar to debug!(), but the first argument is LogFilter or KeyFilter
logger_debug_assert
On debug build, will log with log_filter and panic when condition not met. Skip the check on release build.
logger_debug_assert_eq
On debug build, will log with log_filter and panic when condition not met. Skip the check on release build.
logger_error
Similar to error!(), but the first argument is LogFilter or KeyFilter.
logger_info
Similar to info!(), but the first argument is LogFilter or KeyFilter.
logger_trace
Similar to trace!(), but the first argument is LogFilter or KeyFilter
logger_warn
Similar to warn!(), but the first argument is LogFilter or KeyFilter.
trace
Logs a message at the trace level.
warn
Logs a message at the warn level.

Structs§

Builder
Global config to setup logger See crate::recipe for usage
FormatRecord
For accessing log Record in crate::LogFormat
GlobalLogger
Global static structure to hold the logger
LogBufFile
Config for buffered file sink which merged I/O and delay flush. Optional log rotation can be configured.
LogConsole
Log config for output to console
LogFormat
Custom formatter which adds into a log sink
LogRawFile
Config for file sink that supports atomic append from multiprocess.

Enums§

ConsoleTarget
Level
Re-export log::Level: An enum representing the available verbosity levels of the logger.
LevelFilter
Re-export log::LevelFilter: An enum representing the available verbosity level filters of the logger.

Traits§

SinkConfigTrait

Functions§

get_global_logger
Return the GlobalLogger after initialized.
setup_log
Initialize global logger from Builder

Type Aliases§

FormatFunc

Attribute Macros§

logfn
Provide an proc_macro #[logfn] which log the information: