try more date formats when parsing

This commit is contained in:
Robin Appelman 2024-08-07 16:57:00 +02:00
commit 6213aff07e
3 changed files with 59 additions and 3 deletions

View file

@ -17,7 +17,7 @@ zip = "2.1.5"
itertools = "0.13.0" itertools = "0.13.0"
ratatui = "0.27.0" ratatui = "0.27.0"
tinystr = { version = "0.7.6", features = ["serde"] } tinystr = { version = "0.7.6", features = ["serde"] }
time = { version = "0.3.36", features = ["serde", "serde-well-known"] } time = { version = "0.3.36", features = ["serde", "serde-well-known", "parsing", "macros"] }
hdrhistogram = "7.5.4" hdrhistogram = "7.5.4"
ahash = "0.8.11" ahash = "0.8.11"
base64 = "0.21.7" base64 = "0.21.7"

View file

@ -24,10 +24,65 @@ pub struct LogLine {
pub message: String, pub message: String,
pub exception: Option<Exception>, pub exception: Option<Exception>,
pub app: TinyAsciiStr<32>, pub app: TinyAsciiStr<32>,
#[serde(with = "time::serde::iso8601")] #[serde(with = "date")]
pub time: OffsetDateTime, pub time: OffsetDateTime,
} }
mod date {
use serde::de::Error;
use serde::{Deserialize, Deserializer};
use time::format_description::well_known::Iso8601;
use time::format_description::well_known::Rfc2822;
use time::format_description::well_known::Rfc3339;
use time::format_description::BorrowedFormatItem;
use time::macros::format_description;
use time::parsing::Parsable;
use time::{OffsetDateTime, PrimitiveDateTime};
const FORMATS: &[&[BorrowedFormatItem]] = &[format_description!(
"[year]-[month]-[day] [hour]:[minute]:[second]"
)];
fn try_format(str: &str, format: &(impl Parsable + ?Sized)) -> Option<OffsetDateTime> {
if let Ok(date) = OffsetDateTime::parse(str, format) {
Some(date)
} else if let Ok(date) = PrimitiveDateTime::parse(str, format) {
Some(date.assume_utc())
} else {
None
}
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<OffsetDateTime, D::Error> {
let str = <&str>::deserialize(deserializer)?;
if let Some(date) = try_format(str, &Iso8601::DATE_TIME_OFFSET) {
return Ok(date);
}
if let Some(date) = try_format(str, &Iso8601::DATE_TIME) {
return Ok(date);
}
if let Some(date) = try_format(str, &Rfc3339) {
return Ok(date);
}
if let Some(date) = try_format(str, &Rfc2822) {
return Ok(date);
}
for format in FORMATS {
if let Some(date) = try_format(str, format) {
return Ok(date);
}
}
return Err(D::Error::custom(format_args!(
"Failed to parse date: {}",
str
)));
}
}
impl LogLine { impl LogLine {
pub fn identity(&self) -> u64 { pub fn identity(&self) -> u64 {
let mut hasher = AHasher::default(); let mut hasher = AHasher::default();
@ -93,7 +148,7 @@ pub struct FullLogLine {
#[serde(rename = "reqId")] #[serde(rename = "reqId")]
pub request_id: TinyAsciiStr<32>, pub request_id: TinyAsciiStr<32>,
pub level: LogLevel, pub level: LogLevel,
#[serde(with = "time::serde::iso8601")] #[serde(with = "date")]
pub time: OffsetDateTime, pub time: OffsetDateTime,
#[serde(rename = "remoteAddr")] #[serde(rename = "remoteAddr")]
pub remote_address: String, pub remote_address: String,

View file

@ -52,6 +52,7 @@ fn main() -> MainResult {
Ok(first_parsed) => first_parsed, Ok(first_parsed) => first_parsed,
Err(e) => { Err(e) => {
eprintln!("Failed to parse the first line in the log: {:#}", e); eprintln!("Failed to parse the first line in the log: {:#}", e);
eprintln!("{first}");
return Ok(()); return Ok(());
} }
}; };