forked from LukeMathWalker/tracing-bunyan-formatter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Prevent infinite loop when using reserved fields
closes: LukeMathWalker#33
- Loading branch information
Showing
5 changed files
with
105 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
use std::collections::HashMap; | ||
use std::sync::{Arc, Mutex}; | ||
|
||
use mock_writer::MockWriter; | ||
use serde_json::{json, Value}; | ||
use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer}; | ||
use tracing_subscriber::layer::SubscriberExt; | ||
|
||
mod mock_writer; | ||
|
||
// Run a closure and collect the output emitted by the tracing instrumentation using an in-memory buffer. | ||
// | ||
// If `global` is `true` the subscriber is globally installed for all threads. If `false`, it is | ||
// only set for the current thread, for the duration of `action`. | ||
pub fn run_with_subscriber_and_get_raw_output<F: Fn()>(action: F, global: bool) -> String { | ||
let buffer = Arc::new(Mutex::new(vec![])); | ||
let buffer_clone = buffer.clone(); | ||
|
||
let mut default_fields = HashMap::new(); | ||
default_fields.insert("custom_field".to_string(), json!("custom_value")); | ||
let skipped_fields = vec!["skipped"]; | ||
let formatting_layer = BunyanFormattingLayer::with_default_fields( | ||
"test".into(), | ||
move || MockWriter::new(buffer_clone.clone()), | ||
default_fields, | ||
) | ||
.skip_fields(skipped_fields.into_iter()) | ||
.unwrap(); | ||
let subscriber = tracing_subscriber::Registry::default() | ||
.with(JsonStorageLayer) | ||
.with(formatting_layer); | ||
|
||
if global { | ||
tracing::subscriber::set_global_default(subscriber) | ||
.expect("Failed to install global subscriber"); | ||
action(); | ||
} else { | ||
tracing::subscriber::with_default(subscriber, action); | ||
} | ||
|
||
// Return the formatted output as a string to make assertions against | ||
let buffer_guard = buffer.lock().unwrap(); | ||
let output = buffer_guard.to_vec(); | ||
String::from_utf8(output).unwrap() | ||
} | ||
|
||
pub fn parse_output(output: String) -> Vec<Value> { | ||
output | ||
.lines() | ||
.filter(|&l| !l.trim().is_empty()) | ||
.inspect(|l| println!("{}", l)) | ||
.map(|line| serde_json::from_str::<Value>(line).unwrap()) | ||
.collect() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use common::parse_output; | ||
use tracing::{info, info_span}; | ||
|
||
mod common; | ||
|
||
/// Make sure the `tracing::debug!()` calls in `BunyanFormattingLayer` don't cause an infinite loop | ||
/// (and a stack overflow). | ||
#[test] | ||
pub fn no_infinite_loop() { | ||
// Note: the infinite loop bug could only happen if `BunyanFormattingLayer` | ||
// is set as the global subscriber. (`tracing` guards against a thread-local | ||
// subscriber being aquired twice, returning `NONE` the second time, so any | ||
// `tracing::debug!()` statement withing `BunyanFormattingLayer` are dropped | ||
// in that case) | ||
let output = common::run_with_subscriber_and_get_raw_output( | ||
|| { | ||
info_span!("my span", name = "foo").in_scope(|| { | ||
info!("Calling foo"); | ||
}); | ||
}, | ||
true, | ||
); | ||
|
||
// If we get here, that means the code above didn't crash with a stack overflow. | ||
let logs = parse_output(output); | ||
// We expect 6 log lines: 3 for the span start, log event, span end, but each one is preceded by | ||
// the debug log from `BunyanFormattingLayer` regarding using a reserved field. | ||
assert_eq!(6, logs.len()); | ||
} |