|
| 1 | +//! MozLog formatting for tracing events |
| 2 | +
|
| 3 | +use serde::{ser::SerializeMap, Serialize, Serializer}; |
| 4 | +use std::{fmt, io, time::SystemTime}; |
| 5 | +use tracing::{field::Visit, Event, Level, Subscriber}; |
| 6 | +use tracing_subscriber::{ |
| 7 | + fmt::{ |
| 8 | + format::{self, FormatEvent, FormatFields}, |
| 9 | + FmtContext, |
| 10 | + }, |
| 11 | + registry::LookupSpan, |
| 12 | +}; |
| 13 | + |
| 14 | +/// Top-level formatter for a tracing [Event] |
| 15 | +pub struct EventFormatter { |
| 16 | + logger: String, |
| 17 | + hostname: String, |
| 18 | + pid: u32, |
| 19 | +} |
| 20 | + |
| 21 | +impl EventFormatter { |
| 22 | + pub fn new() -> Self { |
| 23 | + Self { |
| 24 | + logger: format!("{}-{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")), |
| 25 | + hostname: match hostname::get() { |
| 26 | + Ok(h) => h.to_string_lossy().to_string(), |
| 27 | + Err(_) => "<unknown>".to_owned(), |
| 28 | + }, |
| 29 | + pid: std::process::id(), |
| 30 | + } |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +impl<S, N> FormatEvent<S, N> for EventFormatter |
| 35 | +where |
| 36 | + S: Subscriber + for<'a> LookupSpan<'a>, |
| 37 | + N: for<'a> FormatFields<'a> + 'static, |
| 38 | +{ |
| 39 | + fn format_event( |
| 40 | + &self, |
| 41 | + _ctx: &FmtContext<'_, S, N>, |
| 42 | + writer: format::Writer<'_>, |
| 43 | + event: &Event<'_>, |
| 44 | + ) -> fmt::Result { |
| 45 | + // This closure returns a `serde_json::Result` which allows for nicer ergonomics with the |
| 46 | + // `?` operator. We map this to a `fmt::Result` at the bottom of the method. |
| 47 | + // |
| 48 | + // It serializes `event`, writes it out to `writer`, then returns the writer back so we can |
| 49 | + // write out the trailing newline. |
| 50 | + let format_with_serde = move || -> serde_json::Result<format::Writer<'_>> { |
| 51 | + let mut serializer = serde_json::Serializer::new(WriteAdaptor(writer)); |
| 52 | + let mut map = serializer.serialize_map(Some(7))?; |
| 53 | + map.serialize_entry( |
| 54 | + "Timestamp", |
| 55 | + &SystemTime::now() |
| 56 | + .duration_since(SystemTime::UNIX_EPOCH) |
| 57 | + .unwrap_or_default() |
| 58 | + .as_nanos(), |
| 59 | + )?; |
| 60 | + map.serialize_entry("Type", "syncserver:log")?; |
| 61 | + map.serialize_entry("Logger", &self.logger)?; |
| 62 | + map.serialize_entry("Hostname", &self.hostname)?; |
| 63 | + map.serialize_entry("EnvVersion", "2.0")?; |
| 64 | + map.serialize_entry("Pid", &self.pid)?; |
| 65 | + map.serialize_entry( |
| 66 | + "Severity", |
| 67 | + &match *event.metadata().level() { |
| 68 | + Level::ERROR => 3, |
| 69 | + Level::WARN => 4, |
| 70 | + Level::INFO => 5, |
| 71 | + Level::DEBUG => 6, |
| 72 | + Level::TRACE => 7, |
| 73 | + }, |
| 74 | + )?; |
| 75 | + map.serialize_entry("Fields", &SerializableEvent(event))?; |
| 76 | + map.end()?; |
| 77 | + Ok(serializer.into_inner().0) |
| 78 | + }; |
| 79 | + let mut writer = format_with_serde().map_err(|_| fmt::Error)?; |
| 80 | + writeln!(writer) |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +/// Newtype that wraps `tracing::Event` and implements `serde::Serialize`. This allows us to |
| 85 | +/// serialize the event into the `Fields` field. |
| 86 | +struct SerializableEvent<'a, 'event>(&'a Event<'event>); |
| 87 | + |
| 88 | +impl<'a, 'event> Serialize for SerializableEvent<'a, 'event> { |
| 89 | + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { |
| 90 | + let map = serializer.serialize_map(self.0.fields().size_hint().1)?; |
| 91 | + let mut visitor = SerdeFieldVisitor::new(map); |
| 92 | + self.0.record(&mut visitor); |
| 93 | + visitor.end() |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +/// Implements `tracing::Visit` by serializing the fields to a `serde` map. This is how we |
| 98 | +/// serialize the `tracing::Event` with `serde`. |
| 99 | +struct SerdeFieldVisitor<S> |
| 100 | +where |
| 101 | + S: SerializeMap, |
| 102 | +{ |
| 103 | + map: S, |
| 104 | + error: Option<S::Error>, |
| 105 | +} |
| 106 | + |
| 107 | +impl<S> SerdeFieldVisitor<S> |
| 108 | +where |
| 109 | + S: SerializeMap, |
| 110 | +{ |
| 111 | + fn new(serializer: S) -> Self { |
| 112 | + Self { |
| 113 | + map: serializer, |
| 114 | + error: None, |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | + fn should_serialize_field(&self, field: &tracing::field::Field) -> bool { |
| 119 | + !field.name().starts_with("slog.") |
| 120 | + } |
| 121 | + |
| 122 | + fn serialize_entry<V: Serialize + ?Sized>(&mut self, name: &str, value: &V) { |
| 123 | + if let Err(e) = self.map.serialize_entry(name, value) { |
| 124 | + // This is a bit awkward because serde's methods are failable, while the tracing::Visit |
| 125 | + // methods aren't. The best we can do is store the error and return it when |
| 126 | + // `check_error()` is eventually called. In practice this will probably be okay, since |
| 127 | + // the serializer will only fail on IO errors |
| 128 | + self.error = Some(e) |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + fn end(mut self) -> Result<S::Ok, S::Error> { |
| 133 | + match self.error.take() { |
| 134 | + Some(e) => Err(e), |
| 135 | + None => self.map.end(), |
| 136 | + } |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +impl<S> Visit for SerdeFieldVisitor<S> |
| 141 | +where |
| 142 | + S: SerializeMap, |
| 143 | +{ |
| 144 | + fn record_f64(&mut self, field: &tracing::field::Field, value: f64) { |
| 145 | + if self.should_serialize_field(field) { |
| 146 | + self.serialize_entry(field.name(), &value) |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + fn record_i64(&mut self, field: &tracing::field::Field, value: i64) { |
| 151 | + if self.should_serialize_field(field) { |
| 152 | + self.serialize_entry(field.name(), &value) |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + fn record_u64(&mut self, field: &tracing::field::Field, value: u64) { |
| 157 | + if self.should_serialize_field(field) { |
| 158 | + self.serialize_entry(field.name(), &value) |
| 159 | + } |
| 160 | + } |
| 161 | + |
| 162 | + fn record_i128(&mut self, field: &tracing::field::Field, value: i128) { |
| 163 | + if self.should_serialize_field(field) { |
| 164 | + self.serialize_entry(field.name(), &value) |
| 165 | + } |
| 166 | + } |
| 167 | + |
| 168 | + fn record_u128(&mut self, field: &tracing::field::Field, value: u128) { |
| 169 | + if self.should_serialize_field(field) { |
| 170 | + self.serialize_entry(field.name(), &value) |
| 171 | + } |
| 172 | + } |
| 173 | + |
| 174 | + fn record_bool(&mut self, field: &tracing::field::Field, value: bool) { |
| 175 | + if self.should_serialize_field(field) { |
| 176 | + self.serialize_entry(field.name(), &value) |
| 177 | + } |
| 178 | + } |
| 179 | + |
| 180 | + fn record_str(&mut self, field: &tracing::field::Field, value: &str) { |
| 181 | + if self.should_serialize_field(field) { |
| 182 | + self.serialize_entry(field.name(), value) |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + fn record_error( |
| 187 | + &mut self, |
| 188 | + field: &tracing::field::Field, |
| 189 | + value: &(dyn std::error::Error + 'static), |
| 190 | + ) { |
| 191 | + if self.should_serialize_field(field) { |
| 192 | + self.serialize_entry(field.name(), &value.to_string()) |
| 193 | + } |
| 194 | + } |
| 195 | + |
| 196 | + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { |
| 197 | + if self.should_serialize_field(field) { |
| 198 | + self.serialize_entry(field.name(), &format!("{value:?}")) |
| 199 | + } |
| 200 | + } |
| 201 | +} |
| 202 | + |
| 203 | +// Adapts tracing-subscriber's `Writer` struct to implement `std::io::Write` |
| 204 | +// |
| 205 | +// This is needed because `tracing` using the `std::fmt::Write` trait while `serde` uses |
| 206 | +// `std::io::Write`. |
| 207 | +struct WriteAdaptor<'writer>(format::Writer<'writer>); |
| 208 | + |
| 209 | +impl<'writer> io::Write for WriteAdaptor<'writer> { |
| 210 | + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
| 211 | + let s = |
| 212 | + std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; |
| 213 | + |
| 214 | + self.0 |
| 215 | + .write_str(s) |
| 216 | + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; |
| 217 | + |
| 218 | + Ok(s.as_bytes().len()) |
| 219 | + } |
| 220 | + |
| 221 | + fn flush(&mut self) -> io::Result<()> { |
| 222 | + Ok(()) |
| 223 | + } |
| 224 | +} |
0 commit comments