allow loading multiple files
All checks were successful
CI / build (push) Successful in 49s
CI / checks (push) Successful in 1m3s
CI / build-nixpkgs (push) Successful in 37s

This commit is contained in:
Robin Appelman 2025-09-26 16:24:12 +02:00
commit e52630a487
2 changed files with 37 additions and 18 deletions

View file

@ -31,6 +31,7 @@ impl From<&usize> for LogLineNumber {
} }
} }
#[derive(Default)]
pub struct LogFile { pub struct LogFile {
content: String, content: String,
} }
@ -108,6 +109,20 @@ impl LogFile {
} }
} }
pub fn merge<I: IntoIterator<Item = LogFile>>(files: I) -> LogFile {
let mut files = files.into_iter();
let mut merged = files.next().unwrap_or_default();
for file in files {
if !merged.content.ends_with('\n') {
merged.content.push('\n');
}
merged.content.push_str(&file.content);
}
merged
}
pub fn iter(&self) -> impl Iterator<Item = &str> + Send + '_ { pub fn iter(&self) -> impl Iterator<Item = &str> + Send + '_ {
self.content.lines() self.content.lines()
} }

View file

@ -6,6 +6,7 @@ use crate::matcher::{MatchResult, Matcher};
use crate::ui::run_ui; use crate::ui::run_ui;
use base64::prelude::*; use base64::prelude::*;
use clap::Parser; use clap::Parser;
use either::Either;
use indicatif::{ParallelProgressIterator, ProgressStyle}; use indicatif::{ParallelProgressIterator, ProgressStyle};
use logfile::logline::{Exception, FullException, FullLogLine, LogLine, CUSTOM_TIME_FORMAT}; use logfile::logline::{Exception, FullException, FullLogLine, LogLine, CUSTOM_TIME_FORMAT};
use logsmash_data::{default_apps, get_statements, SourceDefinition}; use logsmash_data::{default_apps, get_statements, SourceDefinition};
@ -41,8 +42,8 @@ static GLOBAL: Jemalloc = Jemalloc;
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
struct Args { struct Args {
/// File to read from, or "-" for stdin /// Files to read from, or "-" for stdin
file: String, files: Vec<String>,
/// Collect data and exit, intended for profiling /// Collect data and exit, intended for profiling
#[arg(long)] #[arg(long)]
profile: bool, profile: bool,
@ -68,20 +69,23 @@ fn main() -> MainResult {
.expect("Set only once"); .expect("Set only once");
} }
let log_file = if args.file == "-" { let mut log_files = Vec::new();
for path in args.files {
let log_file = if path == "-" {
let content = read_to_string(stdin()).map_err(|err| LogError::Read { let content = read_to_string(stdin()).map_err(|err| LogError::Read {
err: ReadError::Io(err), err: ReadError::Io(err),
path: args.file, path,
})?; })?;
LogFile::from_string(content) LogFile::from_string(content)
} else { } else {
let file = File::open(&args.file)?; let file = File::open(&path)?;
let file = BufReader::new(file); let file = BufReader::new(file);
LogFile::open(&args.file, file).map_err(|err| LogError::Read { LogFile::open(&path, file).map_err(|err| LogError::Read { err, path })?
err,
path: args.file,
})?
}; };
log_files.push(log_file);
}
let log_file = LogFile::merge(log_files);
let lines: Vec<_> = log_file let lines: Vec<_> = log_file
.iter() .iter()
@ -127,8 +131,8 @@ fn main() -> MainResult {
.collect(); .collect();
results.sort_by_key(|res| match res { results.sort_by_key(|res| match res {
Ok(line) => line.line_number, Ok(line) => Either::Left(line.time),
Err((line_number, _)) => *line_number, Err((line_number, _)) => Either::Right(*line_number),
}); });
let parsed_log: ParsedLogs = results.into_iter().collect(); let parsed_log: ParsedLogs = results.into_iter().collect();