From 3b069106f3408abf0dc74115bc16f35e0a72b265 Mon Sep 17 00:00:00 2001 From: Dominik Werder Date: Wed, 18 Jun 2025 13:07:36 +0200 Subject: [PATCH] Add timestamp and origin to LogItem --- Cargo.toml | 1 + src/streamitem.rs | 153 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 126 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2b58179..2c32ab7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,5 +18,6 @@ bytes = "1.10.0" futures-util = "0.3.24" autoerr = "0.0" chrono = { version = "0.4.19", features = ["serde"] } +time = { version = "0.3.41", features = ["formatting", "parsing", "macros"] } netpod = { path = "../daqbuf-netpod", package = "daqbuf-netpod" } daqbuf-err = { path = "../daqbuf-err" } diff --git a/src/streamitem.rs b/src/streamitem.rs index 3bad87c..9377bd8 100644 --- a/src/streamitem.rs +++ b/src/streamitem.rs @@ -5,6 +5,7 @@ use netpod::EventDataReadStats; use netpod::RangeFilterStats; use serde::Deserialize; use serde::Serialize; +use std::fmt; pub const TERM_FRAME_TYPE_ID: u32 = 0xaa0001; pub const ERROR_FRAME_TYPE_ID: u32 = 0xaa0002; @@ -65,16 +66,37 @@ impl StreamItem { #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct LogItem { - pub node_ix: u32, + #[serde(with = "timestamp")] + ts: time::UtcDateTime, + origin: String, #[serde(with = "levelserde")] - pub level: Level, - pub msg: String, + level: Level, + msg: String, } impl LogItem { - pub fn from_node(node_ix: usize, level: Level, msg: String) -> Self { + pub fn from_node(level: Level, msg: String) -> Self { Self { - node_ix: node_ix as _, + ts: time::UtcDateTime::now(), + level, + msg, + origin: String::new(), + } + } + + pub fn level_msg(level: Level, msg: String) -> Self { + Self { + ts: time::UtcDateTime::now(), + level, + msg, + origin: String::new(), + } + } + + pub fn origin_level_msg(origin: String, level: Level, msg: String) -> Self { + Self { + ts: time::UtcDateTime::now(), + origin, level, msg, } @@ -82,11 +104,31 @@ impl LogItem { pub fn info(msg: String) -> Self { Self { - node_ix: 0, + ts: time::UtcDateTime::now(), level: Level::INFO, msg, + origin: String::new(), } } + + pub fn level(&self) -> Level { + self.level + } + + pub fn display_log_file(&self) -> LogItemDisplayLogfile { + LogItemDisplayLogfile { item: self } + } +} + +pub struct LogItemDisplayLogfile<'a> { + item: &'a LogItem, +} + +impl<'a> fmt::Display for LogItemDisplayLogfile<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let me = &self.item; + write!(fmt, "{} {} {} {}", me.ts, me.level, me.origin, me.msg) + } } pub type SitemErrTy = err::Error; @@ -193,6 +235,56 @@ where err::Error::from_string(x) } +mod timestamp { + use serde::Deserializer; + use serde::Serializer; + use serde::de; + use serde::de::Visitor; + use std::fmt; + + pub fn serialize(val: &time::UtcDateTime, se: S) -> Result + where + S: Serializer, + { + let g = val + .format(time::macros::format_description!( + "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:6]" + )) + .unwrap(); + se.serialize_str(&g) + } + + struct Visit; + + impl<'de> Visitor<'de> for Visit { + type Value = time::UtcDateTime; + + fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "expect utc timestamp") + } + + fn visit_str(self, val: &str) -> Result + where + E: de::Error, + { + let ret = time::UtcDateTime::parse( + val, + time::macros::format_description!( + "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:6]" + ), + ); + ret.map_err(|e| E::custom(format!("{e}"))) + } + } + + pub fn deserialize<'de, D>(de: D) -> Result + where + D: Deserializer<'de>, + { + de.deserialize_str(Visit) + } +} + mod levelserde { use super::Level; use serde::de::{self, Visitor}; @@ -204,30 +296,17 @@ mod levelserde { S: Serializer, { let g = match *t { - Level::ERROR => 1, - Level::WARN => 2, - Level::INFO => 3, - Level::DEBUG => 4, - Level::TRACE => 5, + Level::ERROR => "ERROR", + Level::WARN => "WARN", + Level::INFO => "INFO", + Level::DEBUG => "DEBUG", + Level::TRACE => "TRACE", }; - se.serialize_u32(g) + se.serialize_str(g) } struct VisitLevel; - impl VisitLevel { - fn from_u32(x: u32) -> Level { - match x { - 1 => Level::ERROR, - 2 => Level::WARN, - 3 => Level::INFO, - 4 => Level::DEBUG, - 5 => Level::TRACE, - _ => Level::TRACE, - } - } - } - impl<'de> Visitor<'de> for VisitLevel { type Value = Level; @@ -235,11 +314,19 @@ mod levelserde { write!(fmt, "expect Level code") } - fn visit_u64(self, val: u64) -> Result + fn visit_str(self, val: &str) -> Result where E: de::Error, { - Ok(VisitLevel::from_u32(val as _)) + let level = match val { + "ERROR" => Level::ERROR, + "WARN" => Level::WARN, + "INFO" => Level::INFO, + "DEBUG" => Level::DEBUG, + "TRACE" => Level::TRACE, + _ => return Err(E::custom("unknown level value")), + }; + Ok(level) } } @@ -247,7 +334,17 @@ mod levelserde { where D: Deserializer<'de>, { - de.deserialize_u32(VisitLevel) + de.deserialize_str(VisitLevel) + } +} + +#[test] +fn serde_level() { + use Level::{DEBUG, ERROR, INFO, TRACE, WARN}; + for v1 in [ERROR, WARN, INFO, DEBUG, TRACE] { + let s = serde_json::to_string(&v1).unwrap(); + let v2 = serde_json::from_str(&s).unwrap(); + assert_eq!(v1, v2); } }