Skip to content

Commit 5f1bacc

Browse files
committed
tracing-subscriber: optimise span fields serialisation
Move formatted fields from Span to json log without storing them in an intermediate `serde_json::Value` by using a smart serde visitor.
1 parent e63ef57 commit 5f1bacc

File tree

2 files changed

+49
-42
lines changed

2 files changed

+49
-42
lines changed

tracing-subscriber/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ env-filter = ["matchers", "once_cell", "tracing", "std", "thread_local", "dep:re
3131
fmt = ["registry", "std"]
3232
ansi = ["fmt", "nu-ansi-term"]
3333
registry = ["sharded-slab", "thread_local", "std"]
34-
json = ["tracing-serde", "serde", "serde_json"]
34+
json = ["tracing-serde", "serde", "serde_json", "serde/derive"]
3535
valuable = ["tracing-core/valuable", "valuable_crate", "valuable-serde", "tracing-serde/valuable"]
3636
# Enables support for local time when using the `time` crate timestamp
3737
# formatters.

tracing-subscriber/src/fmt/format/json.rs

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use crate::{
77
},
88
registry::LookupSpan,
99
};
10-
use serde::ser::{SerializeMap, Serializer as _};
11-
use serde_json::Serializer;
10+
use serde::{ser::SerializeMap, Deserialize, Deserializer as _, Serialize, Serializer as _};
11+
use serde_json::{Deserializer, Serializer};
1212
use std::{
1313
collections::BTreeMap,
1414
fmt::{self, Write},
@@ -165,46 +165,24 @@ where
165165
.get::<FormattedFields<N>>()
166166
.expect("Unable to find FormattedFields in extensions; this is a bug");
167167

168-
// TODO: let's _not_ do this, but this resolves
169-
// https://github.com/tokio-rs/tracing/issues/391.
170-
// We should probably rework this to use a `serde_json::Value` or something
171-
// similar in a JSON-specific layer, but I'd (david)
172-
// rather have a uglier fix now rather than shipping broken JSON.
173-
match serde_json::from_str::<serde_json::Value>(data) {
174-
Ok(serde_json::Value::Object(fields)) => {
175-
for field in fields {
176-
serializer.serialize_entry(&field.0, &field.1)?;
177-
}
178-
}
179-
// We have fields for this span which are valid JSON but not an object.
180-
// This is probably a bug, so panic if we're in debug mode
181-
Ok(_) if cfg!(debug_assertions) => panic!(
182-
"span '{}' had malformed fields! this is a bug.\n error: invalid JSON object\n fields: {:?}",
183-
self.0.metadata().name(),
184-
data
185-
),
186-
// If we *aren't* in debug mode, it's probably best not to
187-
// crash the program, let's log the field found but also an
188-
// message saying it's type is invalid
189-
Ok(value) => {
190-
serializer.serialize_entry("field", &value)?;
191-
serializer.serialize_entry("field_error", "field was no a valid object")?
168+
let mut deserializer = Deserializer::from_str(data);
169+
if let Err(error) = deserializer.deserialize_map(SerializeMapVisitor(&mut serializer)) {
170+
// We have fields for this span which are not a valid JSON object.
171+
if cfg!(debug_assertions) {
172+
// This is probably a bug, so panic if we're in debug mode
173+
panic!(
174+
"span '{}' had malformed fields! this is a bug.\n error: {}\n fields: {:?}",
175+
self.0.metadata().name(),
176+
error,
177+
data
178+
);
179+
} else {
180+
// If we *aren't* in debug mode, it's probably best not to crash the program,
181+
// let's log the raw fields together with the parsing error from serde
182+
serializer.serialize_entry("fields", &data.fields)?;
183+
serializer.serialize_entry("fields_error", &format!("{error:?}"))?;
192184
}
193-
// We have previously recorded fields for this span
194-
// should be valid JSON. However, they appear to *not*
195-
// be valid JSON. This is almost certainly a bug, so
196-
// panic if we're in debug mode
197-
Err(e) if cfg!(debug_assertions) => panic!(
198-
"span '{}' had malformed fields! this is a bug.\n error: {}\n fields: {:?}",
199-
self.0.metadata().name(),
200-
e,
201-
data
202-
),
203-
// If we *aren't* in debug mode, it's probably best not
204-
// crash the program, but let's at least make sure it's clear
205-
// that the fields are not supposed to be missing.
206-
Err(e) => serializer.serialize_entry("field_error", &format!("{}", e))?,
207-
};
185+
}
208186
serializer.serialize_entry("name", self.0.metadata().name())?;
209187
serializer.end()
210188
}
@@ -417,6 +395,35 @@ impl<'a> FormatFields<'a> for JsonFields {
417395
}
418396
}
419397

398+
#[derive(Serialize, Deserialize)]
399+
#[serde(untagged)]
400+
enum SpanFieldsValue<'a> {
401+
/// String which could be borrowed directly from the input json
402+
Borrowed(&'a str),
403+
/// Any other value
404+
Owned(serde_json::Value),
405+
}
406+
407+
/// The [serde::de::Visitor] which moves entries from one map to another.
408+
struct SerializeMapVisitor<'a, S: SerializeMap>(&'a mut S);
409+
410+
impl<'de, S: SerializeMap> serde::de::Visitor<'de> for SerializeMapVisitor<'_, S> {
411+
type Value = ();
412+
413+
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
414+
write!(formatter, "a map of values")
415+
}
416+
417+
fn visit_map<A: serde::de::MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
418+
while let Some((key, value)) = map.next_entry::<&str, SpanFieldsValue<'_>>()? {
419+
self.0
420+
.serialize_entry(key, &value)
421+
.map_err(serde::de::Error::custom)?;
422+
}
423+
Ok(())
424+
}
425+
}
426+
420427
/// The [visitor] produced by [`JsonFields`]'s [`MakeVisitor`] implementation.
421428
///
422429
/// [visitor]: crate::field::Visit

0 commit comments

Comments
 (0)