allow using custom date format

This commit is contained in:
Robin Appelman 2025-02-20 23:26:17 +01:00
commit 1f8cbbf2b8
3 changed files with 44 additions and 1 deletions

View file

@ -6,8 +6,10 @@ use serde_json::Value;
use std::borrow::Cow;
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
use std::sync::OnceLock;
use time::format_description::well_known::iso8601::{Config, EncodedConfig, TimePrecision};
use time::format_description::well_known::Iso8601;
use time::format_description::OwnedFormatItem;
use time::OffsetDateTime;
use tinystr::TinyAsciiStr;
@ -17,6 +19,9 @@ pub const TIME_FORMAT: EncodedConfig = Config::DEFAULT
})
.encode();
// ugly global because passing state to serde is hard
pub static CUSTOM_TIME_FORMAT: OnceLock<Option<OwnedFormatItem>> = OnceLock::new();
#[derive(Deserialize, Clone)]
pub struct LogLine<'a> {
#[serde(default)]
@ -38,6 +43,7 @@ pub struct LogLine<'a> {
}
mod date {
use crate::logline::CUSTOM_TIME_FORMAT;
use serde::de::Error;
use serde::{Deserialize, Deserializer};
use time::format_description::well_known::Iso8601;
@ -70,6 +76,12 @@ mod date {
) -> Result<OffsetDateTime, D::Error> {
let str = <&str>::deserialize(deserializer)?;
if let Some(Some(format)) = CUSTOM_TIME_FORMAT.get() {
if let Some(date) = try_format(str, format) {
return Ok(date);
}
}
if let Some(date) = try_format(str, &Iso8601::DATE_TIME_OFFSET) {
return Ok(date);
}

View file

@ -1,7 +1,7 @@
use crate::app::{App, LogMatch};
use crate::error::LogError;
use crate::logfile::LogFile;
use crate::logline::{Exception, FullException, FullLogLine, LogLine};
use crate::logline::{Exception, FullException, FullLogLine, LogLine, CUSTOM_TIME_FORMAT};
use crate::matcher::{MatchResult, Matcher};
use crate::ui::run_ui;
use base64::prelude::*;
@ -26,6 +26,7 @@ mod ui;
#[cfg(not(target_os = "windows"))]
use tikv_jemallocator::Jemalloc;
use time::format_description::{parse_owned, parse_strftime_owned};
#[cfg(not(target_os = "windows"))]
#[global_allocator]
@ -37,11 +38,28 @@ struct Args {
/// Collect data and exit, intended for profiling
#[arg(long)]
profile: bool,
/// Date format to use when parsing log lines
#[arg(long)]
date_format: Option<String>,
}
fn main() -> MainResult {
let args = Args::parse();
if let Some(date_format) = args.date_format.as_deref() {
let date_format = if date_format.contains('%') {
parse_strftime_owned(date_format)
.inspect_err(|_| eprintln!("Invalid strftime format: {date_format}"))?
} else {
parse_owned::<2>(date_format)
.inspect_err(|_| eprintln!("Invalid date format: {date_format}"))?
};
CUSTOM_TIME_FORMAT
.set(Some(date_format))
.expect("Set only once");
}
let file = File::open(&args.file)?;
let file = BufReader::new(file);
let log_file = LogFile::open(&args.file, file).map_err(|err| LogError::Read {