-
Notifications
You must be signed in to change notification settings - Fork 834
tracing-subscriber: optimise span fields serialisation #3343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
tracing-subscriber: optimise span fields serialisation #3343
Conversation
Move formatted fields from Span to json log without storing them in an intermediate `serde_json::Value` by using a smart serde visitor.
b2b0aa9 to
5f1bacc
Compare
|
I tried to measure effect on memory allocations from this change using stats_alloc crate. diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml
index 1ea08eb..91d83fe 100644
--- a/tracing-subscriber/Cargo.toml
+++ b/tracing-subscriber/Cargo.toml
@@ -68,6 +68,7 @@ chrono = { version = "0.4.26", default-features = false, features = ["clock", "s
# registry
sharded-slab = { version = "0.1.4", optional = true }
thread_local = { version = "1.1.4", optional = true }
+stats_alloc = "0.1.10"
[target.'cfg(tracing_unstable)'.dependencies]
valuable_crate = { package = "valuable", version = "0.1.0", optional = true, default-features = false }I used it with existing tests in diff --git a/tracing-subscriber/src/fmt/format/json.rs b/tracing-subscriber/src/fmt/format/json.rs
index 3ef0fcd..c332f58 100644
--- a/tracing-subscriber/src/fmt/format/json.rs
+++ b/tracing-subscriber/src/fmt/format/json.rs
@@ -548,6 +548,9 @@ impl field::Visit for JsonVisitor<'_> {
}
#[cfg(test)]
mod test {
+ use stats_alloc::{self, Region, StatsAlloc, INSTRUMENTED_SYSTEM};
+ use std::alloc::System;
+
use super::*;
use crate::fmt::{format::FmtSpan, test::MockMakeWriter, time::FormatTime, SubscriberBuilder};
use tracing::{self, subscriber::with_default};
@@ -555,6 +558,9 @@ mod test {
use std::fmt;
use std::path::Path;
+ #[global_allocator]
+ static GLOBAL: &StatsAlloc<System> = &INSTRUMENTED_SYSTEM;
+
struct MockTime;
impl FormatTime for MockTime {
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
@@ -853,7 +859,12 @@ mod test {
.with_timer(MockTime)
.finish();
- with_default(subscriber, producer);
+ with_default(subscriber, || {
+ let reg = Region::new(&GLOBAL);
+ producer();
+ let stat = reg.change();
+ println!("{stat:#?}");
+ });
let buf = make_writer.buf();
let actual = std::str::from_utf8(&buf[..]).unwrap();Here is what I got from running cargo test --package tracing-subscriber --lib --all-features --release -- fmt::format::json::test::json_nested_span --exact --show-outputon my branch: and on So, while my patch reduces And now I'm not sure if this actually improves anything. |
|
Hm... I tried dropping So, maybe this is the way to go. I think it's |
|
Looks like I was right. I tried using only strings in span fields and But we can't assume those would be strings, so I'm going to drop |
Move formatted fields from Span to json log without storing them in an intermediate
serde_json::Valueby using a smartserdevisitor.Motivation
Current
serde::ser::Serializeimplementation forSerializableSpanparses the entireFormattedFieldsstring intoserde_json::Valueto fix #391, but there is no need to create an intermediate object just to move json fields from one json string to another.Solution
Instead this PR introduces a new
SerializeMapVisitorstruct implementingserde::de::Visitortrait to move all json fields fromDeserializertoSerializeras json object is being parsed.I assume here that
FormattedFieldsshould always contain a json object.FormattedFieldsis still parsed usingserde. But instead of deserialising it into one bigserde_json::Value, I iterate over it's entriesusing a customserde::de::Visitor.FormattedFieldskeys are always parsed as&str. This could fail if any of the keys contains escape sequences, i.e.{"boo\\"bar":"baz"}. But I don't think this corner case is worth being supported.FormattedFieldsvalues are parsed asserde_json::Value. I tried being smart there, but it didn't work as expected (see comments below).