1use std::collections::HashMap;
2
3#[derive(Default)]
4pub struct LogLine {
5 pub(crate) message: String,
6 pub(crate) severity: Option<Severity>,
7 pub(crate) target: Option<String>,
8 pub(crate) file: Option<String>,
9 pub(crate) line: Option<u32>,
10 pub(crate) labels: HashMap<LabelName, LabelValue>,
11}
12
13impl LogLine {
14 pub fn new<S>(
15 severity: Option<Severity>,
16 message: S,
17 target: Option<String>,
18 file: Option<String>,
19 line: Option<u32>,
20 ) -> Self
21 where
22 S: Into<String>,
23 {
24 Self {
25 message: message.into(),
26 severity,
27 target,
28 file,
29 line,
30 labels: HashMap::new(),
31 }
32 }
33
34 pub fn add_label<K, V>(&mut self, name: K, value: V)
35 where
36 K: Into<LabelName>,
37 V: Into<LabelValue>,
38 {
39 _ = self.labels.insert(name.into(), value.into());
40 }
41
42 #[cfg(feature = "tracing")]
44 fn add_property<K, V>(&mut self, key: K, value: V)
45 where
46 K: Into<LabelName>,
47 V: Into<LabelValue>,
48 {
49 let key = key.into();
50 let value = value.into();
51
52 if key == "message" {
53 self.message = if let LabelValue::String(message) = value {
54 message
55 } else {
56 "".into()
57 };
58 } else {
59 self.add_label(key, value);
60 }
61 }
62}
63
64#[cfg(feature = "tracing")]
65impl From<&tracing_core::Event<'_>> for LogLine {
66 fn from(event: &tracing_core::Event) -> Self {
67 let metadata = event.metadata();
68 let log_level = metadata.level().into();
69
70 let mut this = Self {
71 severity: Some(log_level),
72 target: Some(metadata.target().into()),
73 file: metadata.file().map(String::from),
74 line: metadata.line(),
75 ..Default::default()
76 };
77 event.record(&mut this);
78
79 this
80 }
81}
82
83#[cfg(feature = "tracing")]
84impl tracing::field::Visit for LogLine {
85 fn record_f64(&mut self, field: &tracing_core::Field, value: f64) {
86 self.add_property(field, value);
87 }
88
89 fn record_i64(&mut self, field: &tracing_core::Field, value: i64) {
90 self.add_property(field, value);
91 }
92
93 fn record_u64(&mut self, field: &tracing_core::Field, value: u64) {
94 self.add_property(field, value);
95 }
96
97 fn record_str(&mut self, field: &tracing_core::Field, value: &str) {
98 self.add_property(field, value);
99 }
100
101 fn record_i128(&mut self, field: &tracing_core::Field, value: i128) {
102 self.add_property(field, value);
103 }
104
105 fn record_u128(&mut self, field: &tracing_core::Field, value: u128) {
106 self.add_property(field, value);
107 }
108
109 fn record_bool(&mut self, field: &tracing_core::Field, value: bool) {
110 self.add_property(field, value);
111 }
112
113 fn record_error(
116 &mut self,
117 field: &tracing_core::Field,
118 value: &(dyn core::error::Error + 'static),
119 ) {
120 self.add_property(field, format!("{}", value));
121 }
122
123 fn record_debug(&mut self, field: &tracing_core::Field, value: &dyn std::fmt::Debug) {
124 self.add_property(field, format!("{:?}", value));
125 }
126}
127
128#[derive(Eq, PartialEq, Hash)]
129pub struct LabelName(String);
130
131impl From<LabelName> for String {
132 fn from(value: LabelName) -> Self {
133 value.0
134 }
135}
136
137impl PartialEq<&str> for LabelName {
138 fn eq(&self, other: &&str) -> bool {
139 self.0 == *other
140 }
141}
142
143#[cfg(feature = "tracing")]
144impl From<&tracing_core::Field> for LabelName {
145 fn from(value: &tracing_core::Field) -> Self {
146 Self(value.name().into())
147 }
148}
149
150impl From<String> for LabelName {
151 fn from(value: String) -> Self {
152 Self(value)
153 }
154}
155
156impl From<&str> for LabelName {
157 fn from(value: &str) -> Self {
158 Self(value.into())
159 }
160}
161
162#[allow(non_camel_case_types)]
163pub enum LabelValue {
164 f64(f64),
165 i64(i64),
166 u64(u64),
167 i128(i128),
168 u128(u128),
169 bool(bool),
170 String(String),
171}
172
173macro_rules! impl_from_for_prop_value {
174 [$($type:ident),+] => {
175 $(
176 impl From<$type> for LabelValue {
177 fn from(value: $type) -> Self {
178 Self::$type(value)
179 }
180 }
181 )+
182 };
183}
184
185impl From<&str> for LabelValue {
186 fn from(value: &str) -> Self {
187 Self::String(value.to_string())
188 }
189}
190
191impl_from_for_prop_value![f64, i64, u64, i128, u128, bool, String];
192
193#[derive(Clone, Copy)]
194pub enum Severity {
195 _Critical,
196 Error,
197 Warning,
198 Info,
199 Debug,
200}
201
202#[cfg(feature = "tracing")]
203impl From<&tracing::Level> for Severity {
204 fn from(value: &tracing::Level) -> Self {
205 use tracing::Level;
206
207 match *value {
209 Level::TRACE => Self::Debug,
210 Level::DEBUG => Self::Debug,
211 Level::INFO => Self::Info,
212 Level::WARN => Self::Warning,
213 Level::ERROR => Self::Error,
214 }
215 }
216}