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.
- Captured
Log - A log captured by calls to the logging macros (info!, warn!, etc.).
- LogPattern
- A pattern to match against captured logs.
Enums§
- Capture
Scope - 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.