flams_utils/logs.rs
1use std::fmt::Display;
2use std::num::NonZeroU64;
3use std::str::FromStr;
4
5use crate::time::Timestamp;
6use crate::vecmap::VecMap;
7
8#[derive(Clone,Debug)]
9#[cfg_attr(feature="serde", derive(serde::Serialize,serde::Deserialize))]
10pub enum LogFileLine {
11 SpanOpen {
12 name:String,
13 id:NonZeroU64,
14 timestamp:Timestamp,
15 target:Option<String>,
16 level:LogLevel,
17 args:VecMap<String, String>,
18 parent:Option<NonZeroU64>
19 },
20 SpanClose {
21 id:NonZeroU64,
22 timestamp:Timestamp
23 },
24 Message {
25 message:String,
26 timestamp:Timestamp,
27 target:Option<String>,
28 level:LogLevel,
29 args:VecMap<String, String>,
30 span: Option<NonZeroU64>
31 },
32}
33
34
35#[derive(Debug,Clone)]
36#[cfg_attr(feature="serde", derive(serde::Serialize,serde::Deserialize))]
37pub struct LogMessage {
38 pub message:String,
39 pub timestamp:Timestamp,
40 pub target:Option<String>,
41 pub level:LogLevel,
42 pub args:VecMap<String, String>,
43}
44
45#[derive(Debug,Clone)]
46#[cfg_attr(feature="serde", derive(serde::Serialize,serde::Deserialize))]
47pub struct LogSpan {
48 pub name:String,
49 pub timestamp:Timestamp,
50 pub target:Option<String>,
51 pub level:LogLevel,
52 pub args:VecMap<String, String>,
53 pub children:Vec<LogTreeElem>,
54 pub closed:Option<Timestamp>
55}
56
57#[derive(Debug,Clone)]
58#[cfg_attr(feature="serde", derive(serde::Serialize,serde::Deserialize))]
59pub enum LogTreeElem {
60 Span(LogSpan),
61 Message(LogMessage)
62}
63impl From<LogMessage> for LogTreeElem {
64 #[inline]
65 fn from(value: LogMessage) -> Self {
66 Self::Message(value)
67 }
68}
69impl From<LogSpan> for LogTreeElem {
70 #[inline]
71 fn from(value: LogSpan) -> Self {
72 Self::Span(value)
73 }
74}
75
76#[derive(Debug,Clone,Default)]
77#[cfg_attr(feature="serde", derive(serde::Serialize,serde::Deserialize))]
78pub struct LogTree {
79 pub children:Vec<LogTreeElem>,
80 pub open_span_paths:VecMap<NonZeroU64,Vec<usize>>
81}
82
83
84impl LogTree {
85 fn merge(&mut self,line:LogTreeElem, parent:Option<NonZeroU64>,is_span:Option<NonZeroU64>) {
86 let mut path = Vec::new();
87 let p = if let Some(p) = parent.as_ref().and_then(|p| self.open_span_paths.get(p)) {
88 if is_span.is_some() {path.clone_from(p);}
89 let mut ls = &mut self.children;
90 for i in p {
91 let LogTreeElem::Span(s) = &mut ls[*i] else {
92 unreachable!();
93 };
94 ls = &mut s.children;
95 }
96 ls
97 } else {
98 /*if parent.is_some() {
99 println!("Parent not found: {line:?}!");
100 }*/
101 &mut self.children
102 };
103 if let Some(id) = is_span {
104 path.push(p.len());
105 self.open_span_paths.insert(id,path);
106 }
107 p.push(line);
108 }
109 fn close(&mut self,id:NonZeroU64,timestamp:Timestamp) {
110 let e = if let Some(mut path) = self.open_span_paths.remove(&id) {
111 let mut ls = &mut self.children;
112 let last = path.pop().unwrap_or_else(|| unreachable!());
113 for i in path {
114 let LogTreeElem::Span(s) = &mut ls[i] else {
115 unreachable!();
116 };
117 ls = &mut s.children;
118 }
119 &mut ls[last]
120 } else { return };
121 let LogTreeElem::Span(e) = e else {unreachable!()};
122 e.closed = Some(timestamp);
123 }
124 pub fn add_line(&mut self,line:LogFileLine) {
125 match line {
126 LogFileLine::SpanOpen { name, id, timestamp, target, level, args, parent } => {
127 let span = LogSpan {
128 name,timestamp,target,level,args,
129 children: Vec::new(),
130 closed: None
131 };
132 self.merge(span.into(), parent,Some(id));
133 }
134 LogFileLine::Message { message, timestamp, target, level, args, span } => {
135 let message = LogMessage {
136 message, timestamp, target, level,args
137 };
138 self.merge(message.into(), span,None);
139 }
140 LogFileLine::SpanClose { timestamp,id, .. } => {
141 self.close(id,timestamp);
142 }
143 }
144 }
145}
146
147
148#[derive(Debug,Copy,Clone,PartialEq,Eq)]
149#[cfg_attr(feature="serde", derive(serde::Serialize,serde::Deserialize))]
150pub enum LogLevel { TRACE, DEBUG, INFO, WARN, ERROR }
151impl FromStr for LogLevel {
152 type Err = ();
153 fn from_str(s: &str) -> Result<Self, Self::Err> {
154 match s {
155 "TRACE" => Ok(Self::TRACE),
156 "DEBUG" => Ok(Self::DEBUG),
157 "INFO" => Ok(Self::INFO),
158 "WARN" => Ok(Self::WARN),
159 "ERROR" => Ok(Self::ERROR),
160 _ => Err(())
161 }
162 }
163}
164impl From<tracing::Level> for LogLevel {
165 fn from(l:tracing::Level) -> Self {
166 match l {
167 tracing::Level::TRACE => Self::TRACE,
168 tracing::Level::DEBUG => Self::DEBUG,
169 tracing::Level::INFO => Self::INFO,
170 tracing::Level::WARN => Self::WARN,
171 tracing::Level::ERROR => Self::ERROR
172 }
173 }
174}
175impl PartialOrd for LogLevel {
176 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
177 Some(self.cmp(other))
178 }
179}
180impl Ord for LogLevel {
181 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
182 match (self, other) {
183 (a,b) if a == b => std::cmp::Ordering::Equal,
184 (Self::TRACE, _) => std::cmp::Ordering::Less,
185 (_, Self::TRACE) => std::cmp::Ordering::Greater,
186 (Self::DEBUG, _) => std::cmp::Ordering::Less,
187 (_, Self::DEBUG) => std::cmp::Ordering::Greater,
188 (Self::INFO, _) => std::cmp::Ordering::Less,
189 (_, Self::INFO) => std::cmp::Ordering::Greater,
190 (Self::WARN, _) => std::cmp::Ordering::Less,
191 (_, Self::WARN) => std::cmp::Ordering::Greater,
192 _ => unreachable!()
193 }
194 }
195}
196impl Display for LogLevel {
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198 match self {
199 Self::TRACE => write!(f,"TRACE"),
200 Self::DEBUG => write!(f,"DEBUG"),
201 Self::INFO => write!(f,"INFO "),
202 Self::WARN => write!(f,"WARN "),
203 Self::ERROR => write!(f,"ERROR")
204 }
205 }
206}
207
208impl LogTreeElem {
209
210 #[must_use]
211 pub const fn timestamp(&self) -> Timestamp {
212 match self {
213 Self::Span(LogSpan {timestamp,..}) |
214 Self::Message(LogMessage {timestamp,..}) => *timestamp
215 }
216 }
217 #[must_use]
218 pub const fn level(&self) -> LogLevel {
219 match self {
220 Self::Span(LogSpan {level,..}) |
221 Self::Message(LogMessage {level,..}) => *level
222 }
223 }
224}
225
226/*
227
228 #[must_use]
229 pub fn target(&self) -> Option<&str> {
230 match self {
231 Self::Span(LogSpan {target,..}) |
232 Self::Message(LogMessage {target,..}) => target.as_deref()
233 }
234 }
235 #[must_use]
236 pub const fn args(&self) -> &VecMap<String, String> {
237 match self {
238 Self::Span(LogSpan {args,..}) |
239 Self::Message(LogMessage {args,..}) => args
240 }
241 }
242}
243
244#[derive(Debug,Clone)]
245#[cfg_attr(feature="serde", derive(serde::Serialize,serde::Deserialize))]
246pub struct LogMessage {
247 pub message:String,
248 pub timestamp:Timestamp,
249 pub target:Option<String>,
250 pub level:LogLevel,
251 pub args:VecMap<String, String>,
252}
253
254#[derive(Debug,Clone)]
255#[cfg_attr(feature="serde", derive(serde::Serialize,serde::Deserialize))]
256pub struct LogSpan {
257 pub name:String,
258 pub timestamp:Timestamp,
259 pub target:Option<String>,
260 pub level:LogLevel,
261 pub args:VecMap<String, String>,
262 pub children:Vec<LogTreeElem>,
263 pub closed:Option<Timestamp>
264}
265
266impl<S:ToString+PartialEq<String>,I:IntoIterator<Item = LogFileLine<S>>> From<I> for LogTree {
267 fn from(iter: I) -> Self {
268 let mut s = Self{children:Vec::new(),open_span_paths:VecMap::default()};
269 for e in iter {
270 s.add_line(e);
271 }
272 s
273 }
274}
275
276#[derive(Clone,Debug)]
277#[cfg_attr(feature="serde", derive(serde::Serialize,serde::Deserialize))]
278pub enum LogFileLine<S> {
279 SpanOpen {
280 name:S,
281 timestamp:Timestamp,
282 target:Option<S>,
283 level:LogLevel,
284 args:VecMap<S, S>,
285 parent:Option<String>
286 },
287 SpanClose {
288 id:String,
289 timestamp:Timestamp,
290 parent:Option<String>
291 },
292 Message {
293 message:S,
294 timestamp:Timestamp,
295 target:Option<S>,
296 level:LogLevel,
297 args:VecMap<S, S>,
298 span: Option<String>
299 },
300}
301impl<S:AsRef<str> + std::fmt::Debug + Hash> LogFileLine<S> {
302
303 #[must_use]
304 pub fn id_from(msg:&str,args:&VecMap<S,S>) -> String {
305 hashstr("", &(msg,args))
306 }
307 #[must_use]
308 pub fn id(&self) -> String {
309 match self {
310 Self::SpanOpen { name, args, .. } => {
311 Self::id_from(name.as_ref(),args)
312 }
313 Self::SpanClose { id, .. } => id.clone(),
314 Self::Message { message, args, .. } => {
315 Self::id_from(message.as_ref(),args)
316 }
317 }
318 }
319}
320
321impl<'a> LogFileLine<&'a str> {
322 fn read_string(parser:&mut ParseStr<'a,()>) -> Option<&'a str> {
323 if !parser.drop_prefix("\"") {return None}
324 let s = parser.read_until_escaped('\"','\\');
325 parser.pop_head();
326 Some(s)
327 }
328 fn read_elem(parser:&mut ParseStr<'a,()>) -> Option<&'a str> {
329 if parser.peek_head().is_some_and(|c| c == '\"') {
330 Self::read_string(parser)
331 } else if parser.peek_head().is_some_and(|c| c.is_ascii_digit()) {
332 Some(parser.read_while(|c| c.is_ascii_digit() || c == '.'))
333 } else {
334 None
335 }
336 }
337 fn read_span(parser:&mut ParseStr<'a,()>) -> Option<(&'a str,VecMap<&'a str,&'a str>)> {
338 let mut name = "";
339 if !parser.drop_prefix("{") {return None}
340 let mut vec = VecMap::default();
341 while parser.drop_prefix("\"") {
342 let key = parser.read_until(|c| c == '"');
343 if !parser.drop_prefix("\":") {return None}
344 if key == "name" {
345 name = Self::read_string(parser)?;
346 } else {
347 vec.insert(key,Self::read_elem(parser)?);
348 }
349 parser.drop_prefix(",");
350 }
351 if !parser.drop_prefix("}") {return None}
352 Some((name,vec))
353 }
354
355 #[must_use]
356 pub fn parse(s: &'a str) -> Option<Self> {
357 let mut parser = ParseStr::<()>::new(s);
358 if !parser.drop_prefix("{\"timestamp\":") {return None}
359 let ts = Self::read_string(&mut parser)?;
360 let timestamp =ts.parse().ok()?;
361 if !parser.drop_prefix(",\"level\":") {return None}
362 let level = Self::read_string(&mut parser)?;
363 let level : LogLevel = level.parse().ok()?;
364 if !parser.drop_prefix(",\"message\":") {return None}
365 let message = Self::read_string(&mut parser)?;
366 let mut target = None;
367 let mut span = None;
368 let mut spans = Vec::new();
369 let mut args = VecMap::default();
370 while parser.drop_prefix(",\"") {
371 let key = parser.read_until(|c| c == '"');
372 if !parser.drop_prefix("\":") {return None}
373 match key {
374 "target" => {
375 target = Some(Self::read_string(&mut parser)?);
376 }
377 "span" => span = Some(Self::read_span(&mut parser)?),
378 "spans" => {
379 if !parser.drop_prefix("[") {return None}
380 if parser.peek_head() == Some('{') {
381 spans.push(Self::read_span(&mut parser)?);
382 while parser.drop_prefix(",") {
383 spans.push(Self::read_span(&mut parser)?);
384 }
385 }
386 if !parser.drop_prefix("]") {return None}
387 }
388 "time.busy" | "time.idle" => {
389 Self::read_string(&mut parser)?;
390 }
391 k => {
392 args.insert(k,Self::read_elem(&mut parser)?);
393 }
394 }
395 }
396 if !parser.drop_prefix("}") {return None}
397 if message == "new" {
398 let (name,args) = span?;
399 Some(LogFileLine::SpanOpen {
400 name,
401 timestamp,
402 target,
403 level,
404 args,
405 parent:spans.into_iter().map(|(name,args)| hashstr("",&(name,args))).next_back()
406 })
407 } else if message == "close" {
408 let (name,args) = span?;
409 let id = LogFileLine::id_from(name,&args);
410 Some(LogFileLine::SpanClose {
411 id,
412 timestamp,
413 parent:spans.into_iter().map(|(name,args)| hashstr("",&(name,args))).next_back()
414 })
415 } else {
416 Some(LogFileLine::Message {
417 message,
418 timestamp,
419 target,
420 level,
421 args,
422 span:spans.into_iter().map(|(name,args)| hashstr("",&(name,args))).next_back()
423 })
424 }
425 }
426
427 #[must_use]
428 pub fn to_owned(self) -> LogFileLine<String> {
429 match self {
430 LogFileLine::SpanOpen { name, timestamp, target, level, args, parent } => LogFileLine::SpanOpen {
431 name: name.into(),
432 timestamp,
433 target: target.map(Into::into),
434 level,
435 args: args.into_iter().map(|(k,v)| (k.into(),v.into())).collect(),
436 parent
437 },
438 LogFileLine::SpanClose { id, timestamp, parent } => LogFileLine::SpanClose {
439 id, timestamp,
440 parent
441 },
442 LogFileLine::Message { message, timestamp, target, level, args, span } => LogFileLine::Message {
443 message: message.into(),
444 timestamp,
445 target: target.map(Into::into),
446 level,
447 args: args.into_iter().map(|(k,v)| (k.into(),v.into())).collect(),
448 span
449 }
450 }
451 }
452}
453
454
455 */