telemetry_rust/
fmt.rs

1//! JSON formatting utilities for structured logging with OpenTelemetry.
2
3use opentelemetry::trace::TraceContextExt;
4use serde::{
5    Deserializer as _, Serialize, Serializer as _,
6    de::{Error, MapAccess, Visitor as DeVisitor},
7    ser::{SerializeMap, SerializeSeq},
8};
9use serde_json::{Deserializer, Serializer, Value};
10use std::{fmt, io, marker::PhantomData, ops::Deref, str};
11use tracing::{Event, Span, Subscriber};
12use tracing_opentelemetry::OpenTelemetrySpanExt;
13use tracing_serde::{AsSerde, SerdeMapVisitor};
14use tracing_subscriber::{
15    fmt::{
16        FmtContext, FormatEvent, FormatFields, FormattedFields,
17        format::Writer,
18        time::{FormatTime, SystemTime},
19    },
20    registry::{LookupSpan, SpanRef},
21};
22
23/// JSON event formatter for structured logging with OpenTelemetry integration.
24///
25/// This formatter serializes tracing events into JSON format with additional OpenTelemetry
26/// metadata including trace IDs, span IDs, and span hierarchy information. It's designed
27/// to work with the tracing-subscriber ecosystem and provides rich context for log analysis.
28///
29/// # Features
30///
31/// - Structured JSON output with consistent field names
32/// - Automatic inclusion of OpenTelemetry trace and span IDs
33/// - Hierarchical span information for request tracing
34/// - ISO 8601 timestamp formatting
35/// - Proper handling of event fields and metadata
36///
37/// # JSON Structure
38///
39/// The generated JSON includes the following fields:
40/// - `timestamp`: ISO 8601 formatted timestamp
41/// - `level`: Log level (ERROR, WARN, INFO, DEBUG, TRACE)
42/// - `target`: The module path where the event was recorded
43/// - `trace_id`: OpenTelemetry trace ID (if available)
44/// - `span_id`: OpenTelemetry span ID (if available)
45/// - `spans`: Array of parent spans with their fields
46/// - Additional fields from the log event, including `message`
47pub struct JsonFormat;
48
49impl<S, N> FormatEvent<S, N> for JsonFormat
50where
51    S: Subscriber + for<'lookup> LookupSpan<'lookup>,
52    N: for<'writer> FormatFields<'writer> + 'static,
53{
54    fn format_event(
55        &self,
56        ctx: &FmtContext<'_, S, N>,
57        mut writer: Writer<'_>,
58        event: &Event<'_>,
59    ) -> fmt::Result {
60        let mut timestamp = String::new();
61        SystemTime.format_time(&mut Writer::new(&mut timestamp))?;
62
63        let meta = event.metadata();
64
65        let mut visit = || {
66            let mut serializer = Serializer::new(IoWriter(&mut writer));
67            let mut serializer = serializer.serialize_map(None)?;
68
69            serializer.serialize_entry("timestamp", &timestamp)?;
70            serializer.serialize_entry("level", &meta.level().as_serde())?;
71
72            // add all event fields to the json object
73            let mut visitor = SerdeMapVisitor::new(serializer);
74            event.record(&mut visitor);
75            serializer = visitor.take_serializer()?;
76
77            serializer.serialize_entry("target", meta.target())?;
78
79            // extract tracing information from the current span context
80            let current_span = Span::current();
81            if let Some(id) = current_span.id() {
82                let otel_ctx = current_span.context();
83                let span_ref = otel_ctx.span();
84                let span_context = span_ref.span_context();
85
86                if let Some(leaf_span) = ctx.span(&id).or_else(|| ctx.lookup_current()) {
87                    let spans = SpanScope(leaf_span, PhantomData::<N>);
88                    serializer.serialize_entry("spans", &spans)?;
89                }
90
91                let trace_id = span_context.trace_id().to_string();
92                serializer.serialize_entry("trace_id", &trace_id)?;
93
94                let span_id = span_context.span_id().to_string();
95                serializer.serialize_entry("span_id", &span_id)?;
96            }
97
98            SerializeMap::end(serializer)
99        };
100
101        visit().map_err(|_| fmt::Error)?;
102        writeln!(writer)
103    }
104}
105
106struct IoWriter<'a>(&'a mut dyn fmt::Write);
107
108impl io::Write for IoWriter<'_> {
109    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
110        let s = str::from_utf8(buf)
111            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
112
113        self.0.write_str(s).map_err(io::Error::other)?;
114
115        Ok(s.len())
116    }
117
118    fn flush(&mut self) -> io::Result<()> {
119        Ok(())
120    }
121}
122
123struct SpanData<'a, R, N>(SpanRef<'a, R>, PhantomData<N>)
124where
125    R: for<'lookup> LookupSpan<'lookup>,
126    N: for<'writer> FormatFields<'writer> + 'static;
127
128impl<R, N> Serialize for SpanData<'_, R, N>
129where
130    R: for<'lookup> LookupSpan<'lookup>,
131    N: for<'writer> FormatFields<'writer> + 'static,
132{
133    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
134        let mut serializer = serializer.serialize_map(None)?;
135        serializer.serialize_entry("name", self.0.name())?;
136
137        let extensions = self.0.extensions();
138        if let Some(fields) = extensions.get::<FormattedFields<N>>() {
139            let mut deserializer = Deserializer::from_str(fields);
140            let visitor = SerializeMapVisitor(&mut serializer);
141            if let Err(error) = deserializer.deserialize_map(visitor) {
142                serializer.serialize_entry("formatted_fields", fields.deref())?;
143                serializer.serialize_entry("parsing_error", &format!("{error:?}"))?;
144            }
145        }
146
147        serializer.end()
148    }
149}
150
151struct SpanScope<'a, R, N>(SpanRef<'a, R>, PhantomData<N>)
152where
153    R: Subscriber + for<'lookup> LookupSpan<'lookup>,
154    N: for<'writer> FormatFields<'writer> + 'static;
155
156impl<R, N> Serialize for SpanScope<'_, R, N>
157where
158    R: Subscriber + for<'lookup> LookupSpan<'lookup>,
159    N: for<'writer> FormatFields<'writer> + 'static,
160{
161    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
162        let mut serializer = serializer.serialize_seq(None)?;
163        for span in self.0.scope().from_root() {
164            serializer.serialize_element(&SpanData(span, self.1))?;
165        }
166        serializer.end()
167    }
168}
169
170/// The [serde::de::Visitor] which moves entries from one map to another.
171struct SerializeMapVisitor<'a, S: SerializeMap>(&'a mut S);
172
173impl<'de, S: SerializeMap> DeVisitor<'de> for SerializeMapVisitor<'_, S> {
174    type Value = ();
175
176    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
177        write!(formatter, "a map of values")
178    }
179
180    fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
181        while let Some((key, value)) = map.next_entry::<&str, Value>()? {
182            self.0
183                .serialize_entry(key, &value)
184                .map_err(A::Error::custom)?;
185        }
186        Ok(())
187    }
188}