tracing_actix_web_mozlog/
subscriber.rs1use gethostname::gethostname;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::{collections::HashMap, io::Write, time::SystemTime};
5use tracing::{Event, Level, Subscriber};
6use tracing_bunyan_formatter::JsonStorage;
7use tracing_subscriber::{fmt::MakeWriter, layer::Context};
8
9const MOZLOG_VERSION: &str = "2.0";
10
11pub struct MozLogFormatLayer<W: for<'a> MakeWriter<'a> + 'static> {
26 name: String,
27 pid: u32,
28 hostname: String,
29 make_writer: W,
30}
31
32#[derive(Clone, Default, Debug, PartialEq, Deserialize, Serialize)]
34#[serde(rename_all = "PascalCase")]
35pub struct MozLogMessage {
36 pub timestamp: i64,
38
39 #[serde(rename = "type")]
41 pub message_type: String,
42
43 pub logger: String,
45
46 pub hostname: String,
48
49 pub env_version: String,
51
52 pub pid: u32,
54
55 pub severity: u32,
57
58 pub fields: HashMap<String, Value>,
60}
61
62impl<W: for<'a> MakeWriter<'a> + 'static> MozLogFormatLayer<W> {
63 pub fn new<S: AsRef<str>>(name: S, make_writer: W) -> Self {
65 Self {
66 name: name.as_ref().to_string(),
67 make_writer,
68 pid: std::process::id(),
69 hostname: gethostname().to_string_lossy().into_owned(),
70 }
71 }
72
73 fn emit(&self, mut buffer: Vec<u8>) -> Result<(), std::io::Error> {
74 buffer.write_all(b"\n")?;
75 self.make_writer.make_writer().write_all(&buffer)
76 }
77}
78
79impl<S, W> tracing_subscriber::Layer<S> for MozLogFormatLayer<W>
80where
81 S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
82 W: for<'a> MakeWriter<'a> + 'static,
83{
84 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
85 let make_log_line = || {
88 let mut event_visitor = JsonStorage::default();
89 event.record(&mut event_visitor);
90
91 let mut values: HashMap<String, Value> = event_visitor
92 .values()
93 .iter()
94 .map(|(k, v)| (k.to_string(), v.clone()))
95 .collect();
96
97 let spans = {
98 let mut span_names = vec![];
99 let mut current = ctx.lookup_current();
100 while let Some(span) = ¤t {
101 {
102 let ext = span.extensions();
103 let span_visitor = ext
104 .get::<JsonStorage>()
105 .expect("MozLogFormatLayer requires JsonStorage layer");
106 for (k, v) in span_visitor.values() {
107 values.entry(k.to_string()).or_insert_with(|| v.clone());
108 }
109 }
110
111 span_names.push(span.name());
112 current = span.parent();
113 }
114 span_names.reverse();
115 span_names.join(",")
116 };
117
118 let severity = match *event.metadata().level() {
120 Level::ERROR => 3, Level::WARN => 4, Level::INFO => 5, Level::DEBUG => 6, Level::TRACE => 7, };
126
127 let type_field = values.remove("type");
128 let raw_type_field = values.remove("r#type");
129 values.insert("spans".to_string(), spans.into());
130
131 let v = MozLogMessage {
132 timestamp: SystemTime::now()
133 .duration_since(SystemTime::UNIX_EPOCH)
134 .unwrap_or_default()
135 .as_nanos() as i64,
136 message_type: type_field
137 .or(raw_type_field)
138 .and_then(|v| v.as_str().map(|s| s.to_string()))
139 .unwrap_or_else(|| "<unknown>".to_string()),
140 logger: self.name.clone(),
141 hostname: self.hostname.clone(),
142 env_version: MOZLOG_VERSION.to_string(),
143 pid: self.pid,
144 severity,
145 fields: values,
146 };
147
148 serde_json::to_vec(&v).map_err(|_| ())
151 };
152
153 let log_line_result: Result<Vec<u8>, ()> = make_log_line();
154 if let Ok(log_line) = log_line_result {
156 let _ = self.emit(log_line);
157 }
158 }
159}