Menu
Public repository / 44903b72f606a648c72f939ee5299c590717c83f
AkurAI-Monitor / src/tailer.rs
Latest change c39a9a924feb8021a7355a7299f92502eb8d3e76 - Initial commit: rust-monitor v0.1.0 by Olafur Bui
use std::collections::HashMap;
use std::io::{Read, Seek, SeekFrom};
pub struct LogTailer {
positions: HashMap<String, u64>,
}
impl LogTailer {
pub fn new(paths: &[String]) -> Self {
let mut positions = HashMap::new();
for path in paths {
// Seek to end on startup — only capture new lines
if let Ok(meta) = std::fs::metadata(path) {
positions.insert(path.clone(), meta.len());
}
}
LogTailer { positions }
}
pub fn read_new_lines(&mut self) -> Vec<(String, String)> {
let mut entries = Vec::new();
let paths: Vec<String> = self.positions.keys().cloned().collect();
for path in &paths {
let pos = *self.positions.get(path).unwrap_or(&0);
let Ok(mut file) = std::fs::File::open(path) else {
continue;
};
let Ok(meta) = file.metadata() else {
continue;
};
// File was truncated/rotated — reset to start
let current_len = meta.len();
let seek_pos = if current_len < pos { 0 } else { pos };
if file.seek(SeekFrom::Start(seek_pos)).is_err() {
continue;
}
let mut buf = String::new();
if file.read_to_string(&mut buf).is_err() {
continue;
}
for line in buf.lines() {
let line = line.trim();
if !line.is_empty() {
entries.push((path.clone(), line.to_string()));
}
}
self.positions.insert(path.clone(), current_len);
}
entries
}
}