Skip to content

Commit

Permalink
Merge pull request #121 from pamburus/feature/tail
Browse files Browse the repository at this point in the history
new: Added `--tail` parameter for `--follow` mode
  • Loading branch information
pamburus authored Jan 24, 2024
2 parents 2967077 + 2c530a2 commit de652ab
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ categories = ["command-line-utilities"]
description = "Utility for viewing json-formatted log files."
keywords = ["cli", "human", "log"]
name = "hl"
version = "0.24.2"
version = "0.25.0"
edition = "2021"
build = "build.rs"

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ Options:
--list-themes List available themes and exit
-s, --sort Sort messages chronologically
-F, --follow Follow input streams and sort messages chronologically during time frame set by --sync-interval-ms option
--tail <TAIL> Number of last messages to preload from each file in --follow mode [default: 10]
--sync-interval-ms <SYNC_INTERVAL_MS> Synchronization interval for live streaming mode enabled by --follow option [default: 100]
-o, --output <OUTPUT> Output file
--dump-index Dump index metadata and exit
Expand Down
3 changes: 2 additions & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub struct Options {
pub input_info: Option<InputInfo>,
pub dump_index: bool,
pub app_dirs: Option<AppDirs>,
pub tail: u64,
}

impl Options {
Expand Down Expand Up @@ -396,7 +397,7 @@ impl App {
if let InputReference::File(filename) = &input_ref {
meta = Some(fs::metadata(filename)?);
}
let mut input = Some(input_ref.open()?);
let mut input = Some(input_ref.open_tail(self.options.tail)?);
let is_file = |meta: &Option<fs::Metadata>| meta.as_ref().map(|m|m.is_file()).unwrap_or(false);
let process = |input: &mut Option<Input>, is_file: bool| {
if let Some(input) = input {
Expand Down
45 changes: 45 additions & 0 deletions src/input.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// std imports
use std::cmp::min;
use std::convert::TryInto;
use std::fs::File;
use std::io::{self, stdin, BufReader, Read, Seek, SeekFrom};
Expand Down Expand Up @@ -61,12 +62,56 @@ impl InputReference {
self.hold()?.open()
}

pub fn open_tail(&self, n: u64) -> io::Result<Input> {
match self {
Self::Stdin => self.open(),
Self::File(path) => {
let mut file = File::open(path)
.map_err(|e| io::Error::new(e.kind(), format!("failed to open {}: {}", self.description(), e)))?;
Self::seek_tail(&mut file, n).ok();
Ok(Input::new(self.clone(), Box::new(file)))
}
}
}

pub fn description(&self) -> String {
match self {
Self::Stdin => "<stdin>".into(),
Self::File(filename) => format!("file '{}'", Color::Yellow.paint(filename.to_string_lossy())),
}
}

fn seek_tail(file: &mut File, lines: u64) -> io::Result<()> {
const BUF_SIZE: usize = 64 * 1024;
let mut scratch = [0; BUF_SIZE];
let mut count: u64 = 0;
let mut prev_pos = file.seek(SeekFrom::End(0))?;
let mut pos = prev_pos;
while pos > 0 {
pos -= min(BUF_SIZE as u64, pos);
pos = file.seek(SeekFrom::Start(pos))?;
if pos == prev_pos {
break;
}
let bn = min(BUF_SIZE, (prev_pos - pos) as usize);
let buf = scratch[..bn].as_mut();

file.read_exact(buf)?;

for i in (0..bn).rev() {
if buf[i] == b'\n' {
if count == lines {
file.seek(SeekFrom::Start(pos + i as u64 + 1))?;
return Ok(());
}
count += 1;
}
}

prev_pos = pos;
}
Ok(())
}
}

// ---
Expand Down
5 changes: 5 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ struct Opt {
#[arg(long, short = 'F')]
follow: bool,

/// Number of last messages to preload from each file in --follow mode.
#[arg(long, default_value = "10")]
tail: u64,

/// Synchronization interval for live streaming mode enabled by --follow option.
#[arg(long, default_value = "100")]
sync_interval_ms: u64,
Expand Down Expand Up @@ -402,6 +406,7 @@ fn run() -> Result<()> {
},
dump_index: opt.dump_index,
app_dirs: Some(app_dirs),
tail: opt.tail,
});

// Configure input.
Expand Down

0 comments on commit de652ab

Please sign in to comment.