flams_utils/
logs.rs

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