Crate logcap

Crate logcap 

Source
Expand description

§Overview

This crate contains a log::Log implementation that captures logs created by the logging macros info!, warn!, etc. Similar to testing_logger, it is useful for making assertions about log output, but adds the ability to capture logs of multithreaded applications.

§Usage

To capture logs from a single thread, configure the logger with the CaptureScope::Thread scope. Logs will be stored in thread_local storage which cannot be accessed by another thread, nor, by extension, any other test.

To capture logs across threads, use the CaptureScope::Process scope. This comes with the caveat that tests should be executed serially to avoid clobbering the logs. Rust runs tests concurrently by default. This can be overridden with --test-threads=1 or by serializing test execution using a mutex.

§Capture logs within a single thread

Setup the logger in each test. If it has already been setup, it will be a no-op:

use log::{info,LevelFilter};
use logcap::{assert_logs, CaptureScope};

#[test]
fn test_logs() {
    logcap::builder()
        .scope(CaptureScope::Thread)
        .max_level(LevelFilter::Debug)
        .setup();

    // test logic outputting logs
    info!("foobar");
    info!("moocow");
    info!("hello, world!");

    // make assertions on logs
    assert_logs!(
        "foobar",
        "^m.*w$",
        (Level::Info, "hello, world!")
    );
}

Or just call setup which is shorthand for capturing all levels with a threaded scope.

#[test]
fn test_logs() {
    logcap::setup();

    // ...
}

§Capture logs across multiple threads

Run tests with --test_threads=1 to avoid clobbering logs between tests.

use log::{LevelFilter,warn};
use logcap::CaptureScope;

#[test]
fn test_logs() {
    logcap::builder()
        .scope(CaptureScope::Process)
        .max_level(LevelFilter::Trace)
        .setup();

    // test multi-threaded logic outputting logs
    warn!("warning from main thread");
    let _ = thread::spawn(|| warn!("warning from thread 1")).join();
    let _ = thread::spawn(|| warn!("warning from thread 2")).join();

    // make assertions on logs
    logcap::consume(|logs| {
        assert_eq!(3, logs.len());
        assert!(logs.iter.find(|log| log.body.contains("thread 1")).is_some());
        assert!(logs.iter.find(|log| log.body.contains("thread 2")).is_some());
        assert!(logs.iter.find(|log| log.body.contains("main thread")).is_some());
    });
}

§Processing logs

Use the assert_logs! macro to assert a sequence of logs using an expressive format.

Call consume or examine to process the captured logs and run assertions over the full collection of logs.

Call clear to remove existing logs without processing them.

§Ouptutting logs

Logs can be output to stderr (default), stdout, or not at all.

To silently capture logs:

logcap::builder().output(None).setup();

To output logs to stdout:

use logcap::LogOutput;

logcap::builder().output(Some(LogOutput::Stdout)).setup();

Macros§

assert_logs
Assert a sequence of matching log patterns over captured logs.

Structs§

Builder
A builder to customize behaviour of the capture logger.
CapturedLog
A log captured by calls to the logging macros (info!, warn!, etc.).
LogPattern
A pattern to match against captured logs.

Enums§

CaptureScope
The scope of logs to capture.
LogOutput
Where to output logs printed with logging macros.

Functions§

builder
Create a configuration builder for setting up capture logging.
clear
Clear any captured logs.
consume
Consume captured logs and perform and processing or assertions. Threaded capture will capture logs in order. Logging between threads is synchronized and captured first come, first serve.
examine
Examine captured logs and perform and processing or assertions. Threaded capture will capture logs in order. Logging between threads is synchronized and captured first come, first serve.
setup
A shortcut for setting up the capture logger with default settings: threaded capture, captures all levels, and outputs logs to stderr.