mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 18:14:11 +02:00
allow using custom date format
This commit is contained in:
parent
95dbe0171e
commit
1f8cbbf2b8
3 changed files with 44 additions and 1 deletions
13
README.md
13
README.md
|
|
@ -18,6 +18,19 @@ logsmash ./logfile.log
|
||||||
Logsmash supports both loading plain log files, compressed log files (`.gz`, `.bz2`, `.xz` or `.zst`), or archives
|
Logsmash supports both loading plain log files, compressed log files (`.gz`, `.bz2`, `.xz` or `.zst`), or archives
|
||||||
containing log files (`.zip` or (compressed) `.tar`).
|
containing log files (`.zip` or (compressed) `.tar`).
|
||||||
|
|
||||||
|
### Date formats
|
||||||
|
|
||||||
|
Since not all log files use the same date format, logsmash tries to parse each data with a number of different log
|
||||||
|
formats.
|
||||||
|
|
||||||
|
If the log file you're opening is using an unsupported log format, you can specify a custom date format with the
|
||||||
|
`--date-format` option.
|
||||||
|
|
||||||
|
The data format can either be in [the strftime format](https://man7.org/linux/man-pages/man3/strftime.3.html) or
|
||||||
|
in [the time crate format (version 2)](https://time-rs.github.io/book/api/format-description.html).
|
||||||
|
|
||||||
|
For example: `[day].[month].[year] - [hour]:[minute]:[second]`.
|
||||||
|
|
||||||
## Log sources
|
## Log sources
|
||||||
|
|
||||||
Logsmash is built around matching log line to their source, either a call to a logging function or an exception being
|
Logsmash is built around matching log line to their source, either a call to a logging function or an exception being
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@ use serde_json::Value;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
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::{Config, EncodedConfig, TimePrecision};
|
||||||
use time::format_description::well_known::Iso8601;
|
use time::format_description::well_known::Iso8601;
|
||||||
|
use time::format_description::OwnedFormatItem;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use tinystr::TinyAsciiStr;
|
use tinystr::TinyAsciiStr;
|
||||||
|
|
||||||
|
|
@ -17,6 +19,9 @@ pub const TIME_FORMAT: EncodedConfig = Config::DEFAULT
|
||||||
})
|
})
|
||||||
.encode();
|
.encode();
|
||||||
|
|
||||||
|
// ugly global because passing state to serde is hard
|
||||||
|
pub static CUSTOM_TIME_FORMAT: OnceLock<Option<OwnedFormatItem>> = OnceLock::new();
|
||||||
|
|
||||||
#[derive(Deserialize, Clone)]
|
#[derive(Deserialize, Clone)]
|
||||||
pub struct LogLine<'a> {
|
pub struct LogLine<'a> {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -38,6 +43,7 @@ pub struct LogLine<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod date {
|
mod date {
|
||||||
|
use crate::logline::CUSTOM_TIME_FORMAT;
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use time::format_description::well_known::Iso8601;
|
use time::format_description::well_known::Iso8601;
|
||||||
|
|
@ -70,6 +76,12 @@ mod date {
|
||||||
) -> Result<OffsetDateTime, D::Error> {
|
) -> Result<OffsetDateTime, D::Error> {
|
||||||
let str = <&str>::deserialize(deserializer)?;
|
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) {
|
if let Some(date) = try_format(str, &Iso8601::DATE_TIME_OFFSET) {
|
||||||
return Ok(date);
|
return Ok(date);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
src/main.rs
20
src/main.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::app::{App, LogMatch};
|
use crate::app::{App, LogMatch};
|
||||||
use crate::error::LogError;
|
use crate::error::LogError;
|
||||||
use crate::logfile::LogFile;
|
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::matcher::{MatchResult, Matcher};
|
||||||
use crate::ui::run_ui;
|
use crate::ui::run_ui;
|
||||||
use base64::prelude::*;
|
use base64::prelude::*;
|
||||||
|
|
@ -26,6 +26,7 @@ mod ui;
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
use tikv_jemallocator::Jemalloc;
|
use tikv_jemallocator::Jemalloc;
|
||||||
|
use time::format_description::{parse_owned, parse_strftime_owned};
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
|
|
@ -37,11 +38,28 @@ struct Args {
|
||||||
/// Collect data and exit, intended for profiling
|
/// Collect data and exit, intended for profiling
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
profile: bool,
|
profile: bool,
|
||||||
|
/// Date format to use when parsing log lines
|
||||||
|
#[arg(long)]
|
||||||
|
date_format: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> MainResult {
|
fn main() -> MainResult {
|
||||||
let args = Args::parse();
|
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 = File::open(&args.file)?;
|
||||||
let file = BufReader::new(file);
|
let file = BufReader::new(file);
|
||||||
let log_file = LogFile::open(&args.file, file).map_err(|err| LogError::Read {
|
let log_file = LogFile::open(&args.file, file).map_err(|err| LogError::Read {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue