diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index d7a01bb81..4b01e4622 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -224,6 +224,11 @@ pub trait Visit { self.record_debug(field, &value) } + /// Visit a byte slice. + fn record_byte_slice(&mut self, field: &Field, value: &[u8]) { + self.record_debug(field, &value) + } + /// Records a type implementing `Error`. /// ///
@@ -443,6 +448,14 @@ impl Value for str { } } +impl crate::sealed::Sealed for [u8] {} + +impl Value for [u8] { + fn record(&self, key: &Field, visitor: &mut dyn Visit) { + visitor.record_byte_slice(key, self) + } +} + #[cfg(feature = "std")] impl crate::sealed::Sealed for dyn std::error::Error + 'static {} diff --git a/tracing-subscriber/src/fmt/format/json.rs b/tracing-subscriber/src/fmt/format/json.rs index f4e61fb12..e7e0eea10 100644 --- a/tracing-subscriber/src/fmt/format/json.rs +++ b/tracing-subscriber/src/fmt/format/json.rs @@ -488,6 +488,11 @@ impl<'a> field::Visit for JsonVisitor<'a> { .insert(field.name(), serde_json::Value::from(value)); } + fn record_byte_slice(&mut self, field: &Field, value: &[u8]) { + self.values + .insert(field.name(), serde_json::Value::from(value)); + } + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { match field.name() { // Skip fields that are actually log metadata that have already been handled @@ -528,13 +533,13 @@ mod test { #[test] fn json() { let expected = - "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; + "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3,\"slice\":[97,98,99]},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3,\"slice\":[97,98,99]}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let collector = collector() .flatten_event(false) .with_current_span(true) .with_span_list(true); test_json(expected, collector, || { - let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); + let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3, slice = &b"abc"[..]); let _guard = span.enter(); tracing::info!("some json test"); }); diff --git a/tracing-subscriber/src/fmt/format/pretty.rs b/tracing-subscriber/src/fmt/format/pretty.rs index 1a0edd428..ed31ce7aa 100644 --- a/tracing-subscriber/src/fmt/format/pretty.rs +++ b/tracing-subscriber/src/fmt/format/pretty.rs @@ -426,6 +426,17 @@ impl<'a> PrettyVisitor<'a> { } impl<'a> field::Visit for PrettyVisitor<'a> { + fn record_byte_slice(&mut self, field: &Field, value: &[u8]) { + let hex = + value + .iter() + .fold(String::with_capacity(value.len() * 2), |mut acc, byte| { + acc.push_str(&format!("{byte:02x}")); + acc + }); + self.record_debug(field, &format_args!("{}", hex)); + } + fn record_str(&mut self, field: &Field, value: &str) { if self.result.is_err() { return;