Skip to content

Commit ca3c05b

Browse files
bryangarzahawkwdavidbarsky
committed
subscriber: if error occurs when formatting event, write error to Writer (#2102)
Motivation: When `Format_event::format_event(...)` returns an error, we are currently silently dropping that Event. tokio-rs/valuable#88 explains one such case in which this was encountered (due to a bug in valuable-serde). We want to be made aware whenever an Event is dropped. Solution: Write to the Writer with an error message to let the user know that we were unable to format a specific event. If writing to the Writer fails, we fall back to writing to stderr. We are not emitting an actual tracing Event, to avoid the risk of a cycle (the new Event could trigger the same formatting error again). Resolves #1965. Co-authored-by: Eliza Weisman <eliza@buoyant.io> Co-authored-by: David Barsky <me@davidbarsky.com>
1 parent f880d1e commit ca3c05b

File tree

2 files changed

+136
-1
lines changed

2 files changed

+136
-1
lines changed

tracing-subscriber/src/fmt/fmt_subscriber.rs

+114-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ pub struct Subscriber<C, N = format::DefaultFields, E = format::Format, W = fn()
6868
fmt_event: E,
6969
fmt_span: format::FmtSpanConfig,
7070
is_ansi: bool,
71+
log_internal_errors: bool,
7172
_inner: PhantomData<fn(C)>,
7273
}
7374

@@ -117,6 +118,7 @@ where
117118
fmt_span: self.fmt_span,
118119
make_writer: self.make_writer,
119120
is_ansi: self.is_ansi,
121+
log_internal_errors: self.log_internal_errors,
120122
_inner: self._inner,
121123
}
122124
}
@@ -146,6 +148,7 @@ where
146148
fmt_span: self.fmt_span,
147149
make_writer: self.make_writer,
148150
is_ansi: self.is_ansi,
151+
log_internal_errors: self.log_internal_errors,
149152
_inner: self._inner,
150153
}
151154
}
@@ -181,6 +184,7 @@ impl<C, N, E, W> Subscriber<C, N, E, W> {
181184
fmt_event: self.fmt_event,
182185
fmt_span: self.fmt_span,
183186
is_ansi: self.is_ansi,
187+
log_internal_errors: self.log_internal_errors,
184188
make_writer,
185189
_inner: self._inner,
186190
}
@@ -264,6 +268,7 @@ impl<C, N, E, W> Subscriber<C, N, E, W> {
264268
fmt_event: self.fmt_event,
265269
fmt_span: self.fmt_span,
266270
is_ansi: self.is_ansi,
271+
log_internal_errors: self.log_internal_errors,
267272
make_writer: TestWriter::default(),
268273
_inner: self._inner,
269274
}
@@ -279,6 +284,24 @@ impl<C, N, E, W> Subscriber<C, N, E, W> {
279284
}
280285
}
281286

287+
/// Sets whether to write errors from [`FormatEvent`] to the writer.
288+
/// Defaults to true.
289+
///
290+
/// By default, `fmt::Subscriber` will write any `FormatEvent`-internal errors to
291+
/// the writer. These errors are unlikely and will only occur if there is a
292+
/// bug in the `FormatEvent` implementation or its dependencies.
293+
///
294+
/// If writing to the writer fails, the error message is printed to stderr
295+
/// as a fallback.
296+
///
297+
/// [`FormatEvent`]: crate::fmt::FormatEvent
298+
pub fn log_internal_errors(self, log_internal_errors: bool) -> Self {
299+
Self {
300+
log_internal_errors,
301+
..self
302+
}
303+
}
304+
282305
/// Updates the [`MakeWriter`] by applying a function to the existing [`MakeWriter`].
283306
///
284307
/// This sets the [`MakeWriter`] that the subscriber being built will use to write events.
@@ -307,6 +330,7 @@ impl<C, N, E, W> Subscriber<C, N, E, W> {
307330
fmt_event: self.fmt_event,
308331
fmt_span: self.fmt_span,
309332
is_ansi: self.is_ansi,
333+
log_internal_errors: self.log_internal_errors,
310334
make_writer: f(self.make_writer),
311335
_inner: self._inner,
312336
}
@@ -338,6 +362,7 @@ where
338362
fmt_span: self.fmt_span,
339363
make_writer: self.make_writer,
340364
is_ansi: self.is_ansi,
365+
log_internal_errors: self.log_internal_errors,
341366
_inner: self._inner,
342367
}
343368
}
@@ -350,6 +375,7 @@ where
350375
fmt_span: self.fmt_span.without_time(),
351376
make_writer: self.make_writer,
352377
is_ansi: self.is_ansi,
378+
log_internal_errors: self.log_internal_errors,
353379
_inner: self._inner,
354380
}
355381
}
@@ -481,6 +507,7 @@ where
481507
fmt_span: self.fmt_span,
482508
make_writer: self.make_writer,
483509
is_ansi: self.is_ansi,
510+
log_internal_errors: self.log_internal_errors,
484511
_inner: self._inner,
485512
}
486513
}
@@ -495,6 +522,7 @@ where
495522
fmt_span: self.fmt_span,
496523
make_writer: self.make_writer,
497524
is_ansi: self.is_ansi,
525+
log_internal_errors: self.log_internal_errors,
498526
_inner: self._inner,
499527
}
500528
}
@@ -524,6 +552,7 @@ where
524552
make_writer: self.make_writer,
525553
// always disable ANSI escapes in JSON mode!
526554
is_ansi: false,
555+
log_internal_errors: self.log_internal_errors,
527556
_inner: self._inner,
528557
}
529558
}
@@ -590,6 +619,7 @@ impl<C, N, E, W> Subscriber<C, N, E, W> {
590619
fmt_span: self.fmt_span,
591620
make_writer: self.make_writer,
592621
is_ansi: self.is_ansi,
622+
log_internal_errors: self.log_internal_errors,
593623
_inner: self._inner,
594624
}
595625
}
@@ -620,6 +650,7 @@ impl<C, N, E, W> Subscriber<C, N, E, W> {
620650
fmt_span: self.fmt_span,
621651
make_writer: self.make_writer,
622652
is_ansi: self.is_ansi,
653+
log_internal_errors: self.log_internal_errors,
623654
_inner: self._inner,
624655
}
625656
}
@@ -633,6 +664,7 @@ impl<C> Default for Subscriber<C> {
633664
fmt_span: format::FmtSpanConfig::default(),
634665
make_writer: io::stdout,
635666
is_ansi: cfg!(feature = "ansi"),
667+
log_internal_errors: false,
636668
_inner: PhantomData,
637669
}
638670
}
@@ -752,6 +784,11 @@ where
752784
{
753785
fields.was_ansi = self.is_ansi;
754786
extensions.insert(fields);
787+
} else {
788+
eprintln!(
789+
"[tracing-subscriber] Unable to format the following event, ignoring: {:?}",
790+
attrs
791+
);
755792
}
756793
}
757794

@@ -898,7 +935,20 @@ where
898935
.is_ok()
899936
{
900937
let mut writer = self.make_writer.make_writer_for(event.metadata());
901-
let _ = io::Write::write_all(&mut writer, buf.as_bytes());
938+
let res = io::Write::write_all(&mut writer, buf.as_bytes());
939+
if self.log_internal_errors {
940+
if let Err(e) = res {
941+
eprintln!("[tracing-subscriber] Unable to write an event to the Writer for this Subscriber! Error: {}\n", e);
942+
}
943+
}
944+
} else if self.log_internal_errors {
945+
let err_msg = format!("Unable to format the following event. Name: {}; Fields: {:?}\n",
946+
event.metadata().name(), event.fields());
947+
let mut writer = self.make_writer.make_writer_for(event.metadata());
948+
let res = io::Write::write_all(&mut writer, err_msg.as_bytes());
949+
if let Err(e) = res {
950+
eprintln!("[tracing-subscriber] Unable to write an \"event formatting error\" to the Writer for this Subscriber! Error: {}\n", e);
951+
}
902952
}
903953

904954
buf.clear();
@@ -1197,6 +1247,69 @@ mod test {
11971247
re.replace_all(s.as_str(), "timing").to_string()
11981248
}
11991249

1250+
#[test]
1251+
fn format_error_print_to_stderr() {
1252+
struct AlwaysError;
1253+
1254+
impl std::fmt::Debug for AlwaysError {
1255+
fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1256+
Err(std::fmt::Error)
1257+
}
1258+
}
1259+
1260+
let make_writer = MockMakeWriter::default();
1261+
let subscriber = crate::fmt::Collector::builder()
1262+
.with_writer(make_writer.clone())
1263+
.with_level(false)
1264+
.with_ansi(false)
1265+
.with_timer(MockTime)
1266+
.finish();
1267+
1268+
with_default(subscriber, || {
1269+
tracing::info!(?AlwaysError);
1270+
});
1271+
let actual = sanitize_timings(make_writer.get_string());
1272+
1273+
// Only assert the start because the line number and callsite may change.
1274+
let expected = concat!(
1275+
"Unable to format the following event. Name: event ",
1276+
file!(),
1277+
":"
1278+
);
1279+
assert!(
1280+
actual.as_str().starts_with(expected),
1281+
"\nactual = {}\nshould start with expected = {}\n",
1282+
actual,
1283+
expected
1284+
);
1285+
}
1286+
1287+
#[test]
1288+
fn format_error_ignore_if_log_internal_errors_is_false() {
1289+
struct AlwaysError;
1290+
1291+
impl std::fmt::Debug for AlwaysError {
1292+
fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1293+
Err(std::fmt::Error)
1294+
}
1295+
}
1296+
1297+
let make_writer = MockMakeWriter::default();
1298+
let subscriber = crate::fmt::Collector::builder()
1299+
.with_writer(make_writer.clone())
1300+
.with_level(false)
1301+
.with_ansi(false)
1302+
.with_timer(MockTime)
1303+
.log_internal_errors(false)
1304+
.finish();
1305+
1306+
with_default(subscriber, || {
1307+
tracing::info!(?AlwaysError);
1308+
});
1309+
let actual = sanitize_timings(make_writer.get_string());
1310+
assert_eq!("", actual.as_str());
1311+
}
1312+
12001313
#[test]
12011314
fn synthesize_span_none() {
12021315
let make_writer = MockMakeWriter::default();

tracing-subscriber/src/fmt/mod.rs

+22
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ impl Default for CollectorBuilder {
463463
filter: Collector::DEFAULT_MAX_LEVEL,
464464
inner: Default::default(),
465465
}
466+
.log_internal_errors(true)
466467
}
467468
}
468469

@@ -619,6 +620,27 @@ where
619620
}
620621
}
621622

623+
/// Sets whether to write errors from [`FormatEvent`] to the writer.
624+
/// Defaults to true.
625+
///
626+
/// By default, `fmt::Collector` will write any `FormatEvent`-internal errors to
627+
/// the writer. These errors are unlikely and will only occur if there is a
628+
/// bug in the `FormatEvent` implementation or its dependencies.
629+
///
630+
/// If writing to the writer fails, the error message is printed to stderr
631+
/// as a fallback.
632+
///
633+
/// [`FormatEvent`]: crate::fmt::FormatEvent
634+
pub fn log_internal_errors(
635+
self,
636+
log_internal_errors: bool,
637+
) -> CollectorBuilder<N, format::Format<L, T>, F, W> {
638+
CollectorBuilder {
639+
inner: self.inner.log_internal_errors(log_internal_errors),
640+
..self
641+
}
642+
}
643+
622644
/// Sets whether or not an event's target is displayed.
623645
pub fn with_target(
624646
self,

0 commit comments

Comments
 (0)