more log subcommands
This commit is contained in:
133
src/bin/dkl.rs
133
src/bin/dkl.rs
@ -24,21 +24,25 @@ enum Command {
|
||||
prefix: String,
|
||||
},
|
||||
Logger {
|
||||
#[arg(long, short = 'p', default_value = "/var/log")]
|
||||
/// Path where the logs are stored
|
||||
#[arg(long, short = 'p', default_value = "/var/log", env = "DKL_LOG_PATH")]
|
||||
log_path: String,
|
||||
/// Name of the log instead of the command's basename
|
||||
#[arg(long, short = 'n')]
|
||||
log_name: Option<String>,
|
||||
/// prefix log lines with time & stream
|
||||
#[arg(long)]
|
||||
with_prefix: bool,
|
||||
command: String,
|
||||
args: Vec<String>,
|
||||
},
|
||||
Log {
|
||||
#[arg(long, short = 'p', default_value = "/var/log")]
|
||||
/// Path where the logs are stored
|
||||
#[arg(long, short = 'p', default_value = "/var/log", env = "DKL_LOG_PATH")]
|
||||
log_path: String,
|
||||
log_name: String,
|
||||
since: Option<String>,
|
||||
until: Option<String>,
|
||||
#[command(subcommand)]
|
||||
op: LogOp,
|
||||
},
|
||||
}
|
||||
|
||||
@ -84,30 +88,100 @@ async fn main() -> Result<()> {
|
||||
C::Log {
|
||||
log_path,
|
||||
log_name,
|
||||
since,
|
||||
until,
|
||||
} => {
|
||||
let since = parse_ts_arg(since)?;
|
||||
let until = parse_ts_arg(until)?;
|
||||
op,
|
||||
} => op.run(&log_path, &log_name).await,
|
||||
}
|
||||
}
|
||||
|
||||
let mut files = dkl::logger::log_files(&log_path, &log_name).await?;
|
||||
files.sort();
|
||||
async fn apply_config(config_file: &str, filters: &[glob::Pattern], chroot: &str) -> Result<()> {
|
||||
let config = fs::read_to_string(config_file).await?;
|
||||
let config: dkl::Config = serde_yaml::from_str(&config)?;
|
||||
|
||||
let mut out = tokio::io::stdout();
|
||||
let files = if filters.is_empty() {
|
||||
config.files
|
||||
} else {
|
||||
(config.files.into_iter())
|
||||
.filter(|f| filters.iter().any(|filter| filter.matches(&f.path)))
|
||||
.collect()
|
||||
};
|
||||
|
||||
for f in files {
|
||||
if !since.is_none_or(|since| f.timestamp >= since) {
|
||||
continue;
|
||||
dkl::apply::files(&files, chroot).await
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum LogOp {
|
||||
Ls {
|
||||
#[arg(short = 'l', long)]
|
||||
detail: bool,
|
||||
},
|
||||
Cleanup {
|
||||
/// days of log to keep
|
||||
days: u64,
|
||||
},
|
||||
Cat {
|
||||
/// print logs >= since
|
||||
since: Option<String>,
|
||||
/// print logs <= until
|
||||
until: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl LogOp {
|
||||
async fn run(self, log_path: &str, log_name: &str) -> Result<()> {
|
||||
let mut files = dkl::logger::log_files(&log_path, &log_name).await?;
|
||||
files.sort();
|
||||
|
||||
use LogOp as Op;
|
||||
match self {
|
||||
Op::Ls { detail } => {
|
||||
for f in files {
|
||||
let path = f.path.to_string_lossy();
|
||||
if detail {
|
||||
println!("{ts} {path}", ts = f.timestamp);
|
||||
} else {
|
||||
println!("{path}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Op::Cleanup { days } => {
|
||||
let deadline = chrono::Utc::now() - chrono::Days::new(days);
|
||||
let deadline = dkl::logger::trunc_ts(deadline);
|
||||
debug!("cleanup {log_name} logs < {deadline}");
|
||||
|
||||
for f in files {
|
||||
if f.timestamp < deadline {
|
||||
debug!("removing {}", f.path.to_string_lossy());
|
||||
fs::remove_file(f.path).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Op::Cat { since, until } => {
|
||||
let since = parse_ts_arg(since)?;
|
||||
let until = parse_ts_arg(until)?;
|
||||
|
||||
let mut out = tokio::io::stdout();
|
||||
|
||||
for f in files {
|
||||
if !since.is_none_or(|since| f.timestamp >= since) {
|
||||
continue;
|
||||
}
|
||||
if !until.is_none_or(|until| f.timestamp <= until) {
|
||||
continue;
|
||||
}
|
||||
|
||||
debug!(
|
||||
"cat {path} (timestamp={ts}, compressed={comp})",
|
||||
path = f.path.to_string_lossy(),
|
||||
ts = f.timestamp.to_rfc3339(),
|
||||
comp = f.compressed
|
||||
);
|
||||
if let Err(e) = f.copy_to(&mut out).await {
|
||||
error!("{file}: {e}", file = f.path.to_string_lossy());
|
||||
}
|
||||
}
|
||||
if !until.is_none_or(|until| f.timestamp <= until) {
|
||||
continue;
|
||||
}
|
||||
|
||||
debug!("{f:?}");
|
||||
f.copy_to(&mut out).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,21 +200,6 @@ fn basename(path: &str) -> &str {
|
||||
path.rsplit_once('/').map_or(path, |split| split.1)
|
||||
}
|
||||
|
||||
async fn apply_config(config_file: &str, filters: &[glob::Pattern], chroot: &str) -> Result<()> {
|
||||
let config = fs::read_to_string(config_file).await?;
|
||||
let config: dkl::Config = serde_yaml::from_str(&config)?;
|
||||
|
||||
let files = if filters.is_empty() {
|
||||
config.files
|
||||
} else {
|
||||
(config.files.into_iter())
|
||||
.filter(|f| filters.iter().any(|filter| filter.matches(&f.path)))
|
||||
.collect()
|
||||
};
|
||||
|
||||
dkl::apply::files(&files, chroot).await
|
||||
}
|
||||
|
||||
fn parse_globs(filters: &[String]) -> Result<Vec<glob::Pattern>> {
|
||||
let mut errors = false;
|
||||
let filters = (filters.iter())
|
||||
|
Reference in New Issue
Block a user