mirror of
https://github.com/tokio-rs/tracing.git
synced 2025-09-28 21:42:15 +00:00
subscriber: Flatten json event metadata (#599)
Signed-off-by: Lucio Franco <luciofranco14@gmail.com> Co-authored-by: Eliza Weisman <eliza@buoyant.io>
This commit is contained in:
parent
ccf49fede5
commit
7e8b1140bf
@ -183,7 +183,9 @@ impl<'a> Serialize for SerializeRecord<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
struct SerdeMapVisitor<S: SerializeMap> {
|
||||
/// Implements `tracing_core::field::Visit` for some `serde::ser::SerializeMap`.
|
||||
#[derive(Debug)]
|
||||
pub struct SerdeMapVisitor<S: SerializeMap> {
|
||||
serializer: S,
|
||||
state: Result<(), S::Error>,
|
||||
}
|
||||
@ -192,7 +194,8 @@ impl<S> SerdeMapVisitor<S>
|
||||
where
|
||||
S: SerializeMap,
|
||||
{
|
||||
fn new(serializer: S) -> Self {
|
||||
/// Create a new map visitor.
|
||||
pub fn new(serializer: S) -> Self {
|
||||
Self {
|
||||
serializer,
|
||||
state: Ok(()),
|
||||
@ -243,13 +246,15 @@ impl<S: SerializeMap> SerdeMapVisitor<S> {
|
||||
/// Completes serializing the visited object, returning `Ok(())` if all
|
||||
/// fields were serialized correctly, or `Error(S::Error)` if a field could
|
||||
/// not be serialized.
|
||||
fn finish(self) -> Result<S::Ok, S::Error> {
|
||||
pub fn finish(self) -> Result<S::Ok, S::Error> {
|
||||
self.state?;
|
||||
self.serializer.end()
|
||||
}
|
||||
}
|
||||
|
||||
struct SerdeStructVisitor<S: SerializeStruct> {
|
||||
/// Implements `tracing_core::field::Visit` for some `serde::ser::SerializeStruct`.
|
||||
#[derive(Debug)]
|
||||
pub struct SerdeStructVisitor<S: SerializeStruct> {
|
||||
serializer: S,
|
||||
state: Result<(), S::Error>,
|
||||
}
|
||||
@ -297,7 +302,7 @@ impl<S: SerializeStruct> SerdeStructVisitor<S> {
|
||||
/// Completes serializing the visited object, returning `Ok(())` if all
|
||||
/// fields were serialized correctly, or `Error(S::Error)` if a field could
|
||||
/// not be serialized.
|
||||
fn finish(self) -> Result<S::Ok, S::Error> {
|
||||
pub fn finish(self) -> Result<S::Ok, S::Error> {
|
||||
self.state?;
|
||||
self.serializer.end()
|
||||
}
|
||||
|
@ -263,6 +263,21 @@ where
|
||||
}
|
||||
|
||||
/// Sets the layer being built to use a [JSON formatter](../fmt/format/struct.Json.html).
|
||||
///
|
||||
/// The full format includes fields from all entered spans.
|
||||
///
|
||||
/// # Example Output
|
||||
///
|
||||
/// ```ignore,json
|
||||
/// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate","fields":{"message":"some message", "key": "value"}}
|
||||
/// ```
|
||||
///
|
||||
/// # Options
|
||||
///
|
||||
/// - [`LayerBuilder::flatten_event`] can be used to enable flattening event fields into the root
|
||||
/// object.
|
||||
///
|
||||
/// [`LayerBuilder::flatten_event`]: #method.flatten_event
|
||||
#[cfg(feature = "json")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
|
||||
pub fn json(self) -> LayerBuilder<S, format::JsonFields, format::Format<format::Json, T>, W> {
|
||||
@ -275,6 +290,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
|
||||
impl<S, T, W> LayerBuilder<S, format::JsonFields, format::Format<format::Json, T>, W> {
|
||||
/// Sets the JSON layer being built to flatten event metadata.
|
||||
///
|
||||
/// See [`format::Json`](../fmt/format/struct.Json.html)
|
||||
pub fn flatten_event(
|
||||
self,
|
||||
flatten_event: bool,
|
||||
) -> LayerBuilder<S, format::JsonFields, format::Format<format::Json, T>, W> {
|
||||
LayerBuilder {
|
||||
fmt_event: self.fmt_event.flatten_event(flatten_event),
|
||||
fmt_fields: format::JsonFields::new(),
|
||||
make_writer: self.make_writer,
|
||||
_inner: self._inner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, N, E, W> LayerBuilder<S, N, E, W> {
|
||||
/// Sets the field formatter that the layer being built will use to record
|
||||
/// fields.
|
||||
|
@ -22,8 +22,30 @@ use tracing_log::NormalizeEvent;
|
||||
/// Marker for `Format` that indicates that the verbose json log format should be used.
|
||||
///
|
||||
/// The full format includes fields from all entered spans.
|
||||
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Json;
|
||||
///
|
||||
/// # Example Output
|
||||
///
|
||||
/// ```ignore,json
|
||||
/// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate","fields":{"message":"some message", "key": "value"}}
|
||||
/// ```
|
||||
///
|
||||
/// # Options
|
||||
///
|
||||
/// - [`Json::flatten_event`] can be used to enable flattening event fields into the root
|
||||
/// object.
|
||||
///
|
||||
/// [`Json::flatten_event`]: #method.flatten_event
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Json {
|
||||
pub(crate) flatten_event: bool,
|
||||
}
|
||||
|
||||
impl Json {
|
||||
/// If set to `true` event metadata will be flattened into the root object.
|
||||
pub fn flatten_event(&mut self, flatten_event: bool) {
|
||||
self.flatten_event = flatten_event;
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, N, T> FormatEvent<S, N> for Format<Json, T>
|
||||
where
|
||||
@ -41,7 +63,6 @@ where
|
||||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||||
{
|
||||
use serde_json::{json, Value};
|
||||
use tracing_serde::fields::AsMap;
|
||||
let mut timestamp = String::new();
|
||||
self.timer.format_time(&mut timestamp)?;
|
||||
|
||||
@ -52,8 +73,11 @@ where
|
||||
#[cfg(not(feature = "tracing-log"))]
|
||||
let meta = event.metadata();
|
||||
|
||||
let flatten_event = self.format.flatten_event;
|
||||
|
||||
let mut visit = || {
|
||||
let mut serializer = Serializer::new(WriteAdaptor::new(writer));
|
||||
|
||||
let mut serializer = serializer.serialize_map(None)?;
|
||||
|
||||
serializer.serialize_entry("timestamp", ×tamp)?;
|
||||
@ -82,8 +106,16 @@ where
|
||||
serializer.serialize_entry("target", meta.target())?;
|
||||
}
|
||||
|
||||
serializer.serialize_entry("fields", &event.field_map())?;
|
||||
serializer.end()
|
||||
if !flatten_event {
|
||||
use tracing_serde::fields::AsMap;
|
||||
serializer.serialize_entry("fields", &event.field_map())?;
|
||||
serializer.end()
|
||||
} else {
|
||||
let mut visitor = tracing_serde::SerdeMapVisitor::new(serializer);
|
||||
event.record(&mut visitor);
|
||||
|
||||
visitor.finish()
|
||||
}
|
||||
};
|
||||
|
||||
visit().map_err(|_| fmt::Error)?;
|
||||
@ -91,6 +123,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Json {
|
||||
fn default() -> Json {
|
||||
Json {
|
||||
flatten_event: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The JSON [`FormatFields`] implementation.
|
||||
///
|
||||
/// [`FormatFields`]: trait.FormatFields.html
|
||||
@ -288,16 +328,31 @@ mod test {
|
||||
let expected =
|
||||
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n";
|
||||
|
||||
test_json(make_writer, expected, &BUF);
|
||||
test_json(make_writer, expected, &BUF, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_flattened_event() {
|
||||
lazy_static! {
|
||||
static ref BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
|
||||
}
|
||||
|
||||
let make_writer = || MockWriter::new(&BUF);
|
||||
|
||||
let expected =
|
||||
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"target\":\"tracing_subscriber::fmt::format::json::test\",\"message\":\"some json test\"}\n";
|
||||
|
||||
test_json(make_writer, expected, &BUF, true);
|
||||
}
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
fn test_json<T>(make_writer: T, expected: &str, buf: &Mutex<Vec<u8>>)
|
||||
fn test_json<T>(make_writer: T, expected: &str, buf: &Mutex<Vec<u8>>, flatten_event: bool)
|
||||
where
|
||||
T: crate::fmt::MakeWriter + Send + Sync + 'static,
|
||||
{
|
||||
let subscriber = crate::fmt::Subscriber::builder()
|
||||
.json()
|
||||
.flatten_event(flatten_event)
|
||||
.with_writer(make_writer)
|
||||
.with_timer(MockTime)
|
||||
.finish();
|
||||
|
@ -7,10 +7,7 @@ use crate::{
|
||||
registry::LookupSpan,
|
||||
};
|
||||
|
||||
use std::{
|
||||
fmt::{self, Write},
|
||||
marker::PhantomData,
|
||||
};
|
||||
use std::fmt::{self, Write};
|
||||
use tracing_core::{
|
||||
field::{self, Field, Visit},
|
||||
Event, Level, Subscriber,
|
||||
@ -133,7 +130,7 @@ pub struct Full;
|
||||
/// span.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Format<F = Full, T = SystemTime> {
|
||||
format: PhantomData<F>,
|
||||
format: F,
|
||||
pub(crate) timer: T,
|
||||
pub(crate) ansi: bool,
|
||||
pub(crate) display_target: bool,
|
||||
@ -143,7 +140,7 @@ pub struct Format<F = Full, T = SystemTime> {
|
||||
impl Default for Format<Full, SystemTime> {
|
||||
fn default() -> Self {
|
||||
Format {
|
||||
format: PhantomData,
|
||||
format: Full,
|
||||
timer: SystemTime,
|
||||
ansi: true,
|
||||
display_target: true,
|
||||
@ -158,7 +155,7 @@ impl<F, T> Format<F, T> {
|
||||
/// See [`Compact`].
|
||||
pub fn compact(self) -> Format<Compact, T> {
|
||||
Format {
|
||||
format: PhantomData,
|
||||
format: Compact,
|
||||
timer: self.timer,
|
||||
ansi: self.ansi,
|
||||
display_target: self.display_target,
|
||||
@ -168,12 +165,25 @@ impl<F, T> Format<F, T> {
|
||||
|
||||
/// Use the full JSON format.
|
||||
///
|
||||
/// See [`Json`].
|
||||
/// The full format includes fields from all entered spans.
|
||||
///
|
||||
/// # Example Output
|
||||
///
|
||||
/// ```ignore,json
|
||||
/// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate","fields":{"message":"some message", "key": "value"}}
|
||||
/// ```
|
||||
///
|
||||
/// # Options
|
||||
///
|
||||
/// - [`Format::flatten_event`] can be used to enable flattening event fields into the root
|
||||
/// object.
|
||||
///
|
||||
/// [`Format::flatten_event`]: #method.flatten_event
|
||||
#[cfg(feature = "json")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
|
||||
pub fn json(self) -> Format<Json, T> {
|
||||
Format {
|
||||
format: PhantomData,
|
||||
format: Json::default(),
|
||||
timer: self.timer,
|
||||
ansi: self.ansi,
|
||||
display_target: self.display_target,
|
||||
@ -235,6 +245,25 @@ impl<F, T> Format<F, T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
|
||||
impl<T> Format<Json, T> {
|
||||
/// Use the full JSON format with the event's event fields flattened.
|
||||
///
|
||||
/// # Example Output
|
||||
///
|
||||
/// ```ignore,json
|
||||
/// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate", "message":"some message", "key": "value"}
|
||||
/// ```
|
||||
/// See [`Json`](../format/struct.Json.html).
|
||||
#[cfg(feature = "json")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
|
||||
pub fn flatten_event(mut self, flatten_event: bool) -> Format<Json, T> {
|
||||
self.format.flatten_event(flatten_event);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, N, T> FormatEvent<S, N> for Format<Full, T>
|
||||
where
|
||||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||||
|
@ -419,7 +419,7 @@ where
|
||||
|
||||
/// Sets the subscriber being built to use a less verbose formatter.
|
||||
///
|
||||
/// See [`format::Compact`].
|
||||
/// See [`format::Compact`](../fmt/format/struct.Compact.html).
|
||||
pub fn compact(self) -> SubscriberBuilder<N, format::Format<format::Compact, T>, F, W>
|
||||
where
|
||||
N: for<'writer> FormatFields<'writer> + 'static,
|
||||
@ -432,7 +432,7 @@ where
|
||||
|
||||
/// Sets the subscriber being built to use a JSON formatter.
|
||||
///
|
||||
/// See [`format::Json`]
|
||||
/// See [`format::Json`](../fmt/format/struct.Json.html)
|
||||
#[cfg(feature = "json")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
|
||||
pub fn json(
|
||||
@ -448,6 +448,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
|
||||
impl<T, F, W> SubscriberBuilder<format::JsonFields, format::Format<format::Json, T>, F, W> {
|
||||
/// Sets the json subscriber being built to flatten event metadata.
|
||||
///
|
||||
/// See [`format::Json`](../fmt/format/struct.Json.html)
|
||||
pub fn flatten_event(
|
||||
self,
|
||||
flatten_event: bool,
|
||||
) -> SubscriberBuilder<format::JsonFields, format::Format<format::Json, T>, F, W> {
|
||||
SubscriberBuilder {
|
||||
filter: self.filter,
|
||||
inner: self.inner.flatten_event(flatten_event),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "env-filter")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
|
||||
impl<N, E, W> SubscriberBuilder<N, E, crate::EnvFilter, W>
|
||||
|
Loading…
x
Reference in New Issue
Block a user