1use anyhow::Context;
4use anyhow::Result;
5use itertools::Itertools;
6use nu_ansi_term::Color;
7use serde_json::Value as JsonValue;
8use std::collections::HashMap;
9use std::fmt::{Display, Formatter, Result as FmtResult};
10
11#[derive(serde::Deserialize, Debug, Clone, PartialEq)]
16pub struct Event {
17 pub timestamp: String,
19 pub level: Level,
21 pub target: String,
23 pub fields: Fields,
25 #[serde(default)]
27 pub span: HashMap<String, JsonValue>,
28 #[serde(default)]
30 pub spans: Vec<HashMap<String, JsonValue>>,
31}
32
33#[derive(serde::Deserialize, Debug, Clone, PartialEq)]
35pub enum Level {
36 #[serde(rename = "ERROR")]
37 Error,
38 #[serde(rename = "WARN")]
39 Warn,
40 #[serde(rename = "INFO")]
41 Info,
42 #[serde(rename = "DEBUG")]
43 Debug,
44 #[serde(rename = "TRACE")]
45 Trace,
46}
47
48impl Display for Level {
49 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
50 match self {
51 Level::Error => write!(f, "{}", Color::Red.paint("ERROR")),
52 Level::Warn => write!(f, " {}", Color::Yellow.paint("WARN")),
53 Level::Info => write!(f, " {}", Color::Green.paint("INFO")),
54 Level::Debug => write!(f, " {}", Color::Blue.paint("DEBUG")),
55 Level::Trace => write!(f, " {}", Color::Purple.paint("TRACE")),
56 }
57 }
58}
59
60#[derive(serde::Deserialize, Debug, Clone, PartialEq)]
62pub struct Fields {
63 #[serde(default)]
66 pub message: String,
67 #[serde(flatten)]
71 pub fields: HashMap<String, JsonValue>,
72}
73
74impl Display for Event {
75 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
76 write!(
77 f,
78 "{}",
79 Color::Default.dimmed().paint(
80 if chrono::DateTime::parse_from_rfc3339(&self.timestamp).is_ok() {
83 &self.timestamp[11..]
85 } else {
86 &self.timestamp
87 }
88 )
89 )?;
90
91 if let Some(backtrace) = self.fields.fields.get("panic.backtrace") {
92 writeln!(
95 f,
96 " {} {}",
97 Color::Red.reverse().paint("PANIC"),
98 self.fields.message,
99 )?;
100 match backtrace {
101 JsonValue::String(backtrace) => {
102 for line in backtrace.lines() {
103 if line.trim().starts_with("at ") {
105 writeln!(f, "{}", Color::Default.dimmed().paint(line))?;
106 } else {
107 writeln!(f, "{}", line)?;
108 }
109 }
110 }
111 backtrace => write!(f, "{backtrace}")?,
112 }
113 } else {
114 write!(f, " {} ", self.level)?;
116 if !self.spans.is_empty() {
117 for span in &self.spans {
118 let name = span.get("name").unwrap().as_str().unwrap();
119 write!(f, "{}", Color::Default.bold().paint(name))?;
120 write!(f, "{}", Color::Default.bold().paint("{"))?;
121 let mut first = true;
122 for (key, value) in span.iter().sorted_by_key(|(key, _)| <&String>::clone(key))
123 {
124 if key != "name" {
125 if !first {
126 write!(f, " ")?;
127 }
128 first = false;
129
130 write!(f, "{}", key)?;
131 write!(f, "{}", Color::Default.dimmed().paint("="))?;
132 write!(f, "{}", value)?;
133 }
134 }
135 write!(f, "{}", Color::Default.bold().paint("}"))?;
136 write!(f, "{}", Color::Default.dimmed().paint(":"))?;
137 }
138 write!(f, " ")?;
139 }
140
141 write!(
142 f,
143 "{}{}",
144 Color::Default.dimmed().paint(&self.target),
145 Color::Default.dimmed().paint(":"),
146 )?;
147
148 if !self.fields.message.is_empty() {
149 write!(f, " {}", self.fields.message)?;
150 }
151
152 for (key, value) in self
153 .fields
154 .fields
155 .iter()
156 .sorted_by_key(|(key, _)| <&String>::clone(key))
157 {
158 write!(f, " {}", Color::Default.italic().paint(key))?;
159 write!(f, "{}", Color::Default.dimmed().paint("="))?;
160 write!(f, "{}", QuotelessDisplay(value))?;
161 }
162 }
163
164 Ok(())
165 }
166}
167
168struct QuotelessDisplay<'a>(&'a JsonValue);
169
170impl Display for QuotelessDisplay<'_> {
171 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
172 match self {
173 QuotelessDisplay(JsonValue::String(str)) => write!(f, "{str}"),
174 QuotelessDisplay(value) => write!(f, "{value}"),
175 }
176 }
177}
178
179impl Event {
180 pub fn from_json_str(s: &str) -> Result<Self> {
182 serde_json::from_str(s).context(format!("Failed to parse json: {s}"))
183 }
184}