tracing_stackdriver/
serializers.rs

1use serde::ser::{Serialize, SerializeMap, SerializeSeq};
2use serde_json::Value;
3use tracing_core::Subscriber;
4use tracing_subscriber::{
5    fmt::{format::JsonFields, FmtContext, FormattedFields},
6    registry::{LookupSpan, SpanRef},
7};
8
9/// Serializable tracing span for nesting formatted event fields
10pub(crate) struct SerializableSpan<'a, 'b, S>(&'b SpanRef<'a, S>)
11where
12    S: for<'lookup> LookupSpan<'lookup>;
13
14impl<'a, 'b, S> SerializableSpan<'a, 'b, S>
15where
16    S: for<'lookup> LookupSpan<'lookup>,
17{
18    pub(crate) fn new(span: &'b SpanRef<'a, S>) -> Self {
19        Self(span)
20    }
21}
22
23impl<'a, 'b, S> Serialize for SerializableSpan<'a, 'b, S>
24where
25    S: for<'lookup> LookupSpan<'lookup>,
26{
27    fn serialize<R>(&self, serializer: R) -> Result<R::Ok, R::Error>
28    where
29        R: serde::Serializer,
30    {
31        let name = self.0.name();
32        let extensions = self.0.extensions();
33
34        let formatted_fields = extensions
35            .get::<FormattedFields<JsonFields>>()
36            .expect("No fields!");
37
38        let span_length = formatted_fields.fields.len() + 1;
39        let mut map = serializer.serialize_map(Some(span_length))?;
40
41        match serde_json::from_str::<Value>(formatted_fields) {
42            // handle string escaping "properly" (this should be fixed upstream)
43            // https://github.com/tokio-rs/tracing/issues/391
44            Ok(Value::Object(fields)) => {
45                for (key, value) in fields {
46                    map.serialize_entry(&key, &value)?;
47                }
48            }
49            // these two options should be impossible
50            Ok(value) => panic!("Invalid value: {}", value),
51            Err(error) => panic!("Error parsing logs: {}", error),
52        };
53
54        map.serialize_entry("name", &name)?;
55        map.end()
56    }
57}
58
59/// Serializable tracing context for serializing a collection of spans
60pub(crate) struct SerializableContext<'a, 'b, S>(&'b FmtContext<'a, S, JsonFields>)
61where
62    S: Subscriber + for<'lookup> LookupSpan<'lookup>;
63
64impl<'a, 'b, S> SerializableContext<'a, 'b, S>
65where
66    S: Subscriber + for<'lookup> LookupSpan<'lookup>,
67{
68    pub(crate) fn new(context: &'b FmtContext<'a, S, JsonFields>) -> Self {
69        Self(context)
70    }
71}
72
73impl<'a, 'b, S> Serialize for SerializableContext<'a, 'b, S>
74where
75    S: Subscriber + for<'lookup> LookupSpan<'lookup>,
76{
77    fn serialize<R>(&self, serializer: R) -> Result<R::Ok, R::Error>
78    where
79        R: serde::Serializer,
80    {
81        let mut list = serializer.serialize_seq(None)?;
82
83        if let Some(leaf_span) = self.0.lookup_current() {
84            for span in leaf_span.scope().from_root() {
85                list.serialize_element(&SerializableSpan::new(&span))?;
86            }
87        }
88
89        list.end()
90    }
91}
92
93pub(crate) struct SourceLocation<'a> {
94    pub(crate) file: &'a str,
95    pub(crate) line: Option<u32>,
96}
97
98impl<'a> Serialize for SourceLocation<'a> {
99    fn serialize<R>(&self, serializer: R) -> Result<R::Ok, R::Error>
100    where
101        R: serde::Serializer,
102    {
103        let mut map = serializer.serialize_map(Some(if self.line.is_some() { 2 } else { 1 }))?;
104        map.serialize_entry("file", self.file)?;
105        if let Some(line) = self.line {
106            // Stackdriver expects the line number to be serialised as a string:
107            // https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogEntrySourceLocation
108            map.serialize_entry("line", &line.to_string())?;
109        }
110        map.end()
111    }
112}