Skip to content

Commit 01267c6

Browse files
committed
subscriber: Add with_buf_capacity_limit to fmt Layer
Shrink the thread-local formatting buffer after each event if its capacity exceeds the configured limit. Prevents outlier large log lines from permanently inflating TLS buffers across worker threads. Signed-off-by: Jon Doron <jond@wiz.io>
1 parent efc690f commit 01267c6

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

tracing-subscriber/src/fmt/fmt_layer.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub struct Layer<
7373
fmt_span: format::FmtSpanConfig,
7474
is_ansi: bool,
7575
log_internal_errors: bool,
76+
max_buf_capacity: Option<usize>,
7677
_inner: PhantomData<fn(S)>,
7778
}
7879

@@ -123,6 +124,7 @@ where
123124
make_writer: self.make_writer,
124125
is_ansi: self.is_ansi,
125126
log_internal_errors: self.log_internal_errors,
127+
max_buf_capacity: self.max_buf_capacity,
126128
_inner: self._inner,
127129
}
128130
}
@@ -153,6 +155,7 @@ where
153155
make_writer: self.make_writer,
154156
is_ansi: self.is_ansi,
155157
log_internal_errors: self.log_internal_errors,
158+
max_buf_capacity: self.max_buf_capacity,
156159
_inner: self._inner,
157160
}
158161
}
@@ -186,6 +189,7 @@ impl<S, N, E, W> Layer<S, N, E, W> {
186189
fmt_span: self.fmt_span,
187190
is_ansi: self.is_ansi,
188191
log_internal_errors: self.log_internal_errors,
192+
max_buf_capacity: self.max_buf_capacity,
189193
make_writer,
190194
_inner: self._inner,
191195
}
@@ -291,6 +295,7 @@ impl<S, N, E, W> Layer<S, N, E, W> {
291295
fmt_span: self.fmt_span,
292296
is_ansi: self.is_ansi,
293297
log_internal_errors: self.log_internal_errors,
298+
max_buf_capacity: self.max_buf_capacity,
294299
make_writer: TestWriter::default(),
295300
_inner: self._inner,
296301
}
@@ -358,6 +363,17 @@ impl<S, N, E, W> Layer<S, N, E, W> {
358363
}
359364
}
360365

366+
/// Sets the maximum capacity (in bytes) retained by the thread-local
367+
/// formatting buffer between events. After formatting, if the buffer
368+
/// capacity exceeds `max_capacity` it is shrunk back. By default there
369+
/// is no limit and the buffer retains whatever capacity it grew to.
370+
pub fn with_buf_capacity_limit(self, max_capacity: usize) -> Self {
371+
Self {
372+
max_buf_capacity: Some(max_capacity),
373+
..self
374+
}
375+
}
376+
361377
/// Updates the [`MakeWriter`] by applying a function to the existing [`MakeWriter`].
362378
///
363379
/// This sets the [`MakeWriter`] that the layer being built will use to write events.
@@ -387,6 +403,7 @@ impl<S, N, E, W> Layer<S, N, E, W> {
387403
fmt_span: self.fmt_span,
388404
is_ansi: self.is_ansi,
389405
log_internal_errors: self.log_internal_errors,
406+
max_buf_capacity: self.max_buf_capacity,
390407
make_writer: f(self.make_writer),
391408
_inner: self._inner,
392409
}
@@ -419,6 +436,7 @@ where
419436
make_writer: self.make_writer,
420437
is_ansi: self.is_ansi,
421438
log_internal_errors: self.log_internal_errors,
439+
max_buf_capacity: self.max_buf_capacity,
422440
_inner: self._inner,
423441
}
424442
}
@@ -432,6 +450,7 @@ where
432450
make_writer: self.make_writer,
433451
is_ansi: self.is_ansi,
434452
log_internal_errors: self.log_internal_errors,
453+
max_buf_capacity: self.max_buf_capacity,
435454
_inner: self._inner,
436455
}
437456
}
@@ -561,6 +580,7 @@ where
561580
make_writer: self.make_writer,
562581
is_ansi: self.is_ansi,
563582
log_internal_errors: self.log_internal_errors,
583+
max_buf_capacity: self.max_buf_capacity,
564584
_inner: self._inner,
565585
}
566586
}
@@ -576,6 +596,7 @@ where
576596
make_writer: self.make_writer,
577597
is_ansi: self.is_ansi,
578598
log_internal_errors: self.log_internal_errors,
599+
max_buf_capacity: self.max_buf_capacity,
579600
_inner: self._inner,
580601
}
581602
}
@@ -607,6 +628,7 @@ where
607628
// always disable ANSI escapes in JSON mode!
608629
is_ansi: false,
609630
log_internal_errors: self.log_internal_errors,
631+
max_buf_capacity: self.max_buf_capacity,
610632
_inner: self._inner,
611633
}
612634
}
@@ -674,6 +696,7 @@ impl<S, N, E, W> Layer<S, N, E, W> {
674696
make_writer: self.make_writer,
675697
is_ansi: self.is_ansi,
676698
log_internal_errors: self.log_internal_errors,
699+
max_buf_capacity: self.max_buf_capacity,
677700
_inner: self._inner,
678701
}
679702
}
@@ -705,6 +728,7 @@ impl<S, N, E, W> Layer<S, N, E, W> {
705728
make_writer: self.make_writer,
706729
is_ansi: self.is_ansi,
707730
log_internal_errors: self.log_internal_errors,
731+
max_buf_capacity: self.max_buf_capacity,
708732
_inner: self._inner,
709733
}
710734
}
@@ -723,6 +747,7 @@ impl<S> Default for Layer<S> {
723747
make_writer: io::stdout,
724748
is_ansi: ansi,
725749
log_internal_errors: false,
750+
max_buf_capacity: None,
726751
_inner: PhantomData,
727752
}
728753
}
@@ -1018,6 +1043,9 @@ where
10181043
}
10191044

10201045
buf.clear();
1046+
if let Some(max_cap) = self.max_buf_capacity {
1047+
buf.shrink_to(max_cap);
1048+
}
10211049
});
10221050
}
10231051

@@ -1666,4 +1694,40 @@ mod test {
16661694
actual.as_str()
16671695
);
16681696
}
1697+
1698+
#[test]
1699+
fn buf_capacity_limit_does_not_break_formatting() {
1700+
// Verify that setting a buf capacity limit still produces correct output
1701+
// for both small and large events.
1702+
let make_writer = MockMakeWriter::default();
1703+
let subscriber = crate::fmt::Subscriber::builder()
1704+
.with_writer(make_writer.clone())
1705+
.with_level(false)
1706+
.with_ansi(false)
1707+
.with_timer(MockTime)
1708+
.with_buf_capacity_limit(256)
1709+
.finish();
1710+
1711+
with_default(subscriber, || {
1712+
// A large event that exceeds the 256-byte limit
1713+
let big = "x".repeat(1024);
1714+
tracing::info!(big_field = big.as_str(), "large event");
1715+
1716+
// A small event afterwards -- this verifies that the buffer was
1717+
// usable after being shrunk.
1718+
tracing::info!("small event");
1719+
});
1720+
1721+
let actual = make_writer.get_string();
1722+
assert!(
1723+
actual.contains("large event"),
1724+
"large event should have been written, got: {}",
1725+
actual
1726+
);
1727+
assert!(
1728+
actual.contains("small event"),
1729+
"small event should have been written after buf shrink, got: {}",
1730+
actual
1731+
);
1732+
}
16691733
}

tracing-subscriber/src/fmt/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,20 @@ where
658658
}
659659
}
660660

661+
/// Sets the maximum capacity (in bytes) retained by the thread-local
662+
/// formatting buffer between events.
663+
///
664+
/// See [`Layer::with_buf_capacity_limit`] for details.
665+
pub fn with_buf_capacity_limit(
666+
self,
667+
max_capacity: usize,
668+
) -> SubscriberBuilder<N, format::Format<L, T>, F, W> {
669+
SubscriberBuilder {
670+
inner: self.inner.with_buf_capacity_limit(max_capacity),
671+
..self
672+
}
673+
}
674+
661675
/// Sets whether or not an event's target is displayed.
662676
pub fn with_target(
663677
self,

0 commit comments

Comments
 (0)