#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Duration;
use tokio::time::interval;
use tracing::Level;
use tracing_cache::SpanCache;
use tracing_console_host::serve;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut args = std::env::args().skip(1);
let dir = match args.next() {
Some(s) => PathBuf::from(s),
None => {
eprintln!("usage: fs_listing_api <directory> [bind-addr]");
std::process::exit(2);
}
};
let addr: std::net::SocketAddr = args
.next()
.unwrap_or_else(|| "127.0.0.1:7777".to_string())
.parse()?;
let level = tracing_cache::LevelPredicate::with_filter(tracing::metadata::LevelFilter::OFF);
let level_handle = level.handle();
let predicate = tracing_cache::ChancePredicate::new(level, 100.0);
let chance_handle = predicate.handle();
let config = tracing_cache::CacheConfig {
pending_batch: 2,
..tracing_cache::CacheConfig::default()
};
let (cache, driver) = SpanCache::with_predicate_and_config(16384, predicate, config);
let cache = Arc::new(cache);
tracing::subscriber::set_global_default(Arc::clone(&cache))?;
tokio::spawn(driver.run());
let serve_cache = Arc::clone(&cache);
let serve_level = level_handle.clone();
let serve_chance = chance_handle.clone();
tokio::spawn(async move {
if let Err(e) = serve(serve_cache, serve_level, serve_chance, addr).await {
eprintln!("serve: {e}");
}
});
eprintln!(
"walking {} every second; serving console RPC at {addr}",
dir.display()
);
let mut tick = interval(Duration::from_secs(1));
loop {
tick.tick().await;
walk_root(&dir);
}
}
fn walk_root(path: &Path) {
let span = tracing::span!(
parent: None,
Level::INFO,
"list_directory",
path = %path.display(),
root = true,
);
let _g = span.enter();
walk_body(path);
}
fn walk_recursive(path: &Path) {
let span = tracing::span!(
Level::INFO,
"list_directory",
path = %path.display(),
root = false,
);
let _g = span.enter();
walk_body(path);
}
fn walk_body(path: &Path) {
let entries = match std::fs::read_dir(path) {
Ok(e) => e,
Err(e) => {
tracing::event!(Level::WARN, error = %e, "read_dir failed");
return;
}
};
for entry in entries {
let Ok(entry) = entry else { continue };
let p = entry.path();
let Ok(file_type) = entry.file_type() else {
continue;
};
if file_type.is_dir() {
walk_recursive(&p);
} else if file_type.is_file() {
fetch_size(&p);
}
}
}
fn fetch_size(path: &Path) {
match std::fs::metadata(path) {
Ok(m) => tracing::event!(Level::DEBUG, bytes = m.len(), path = %path.display(), "size"),
Err(e) => {
tracing::event!(Level::WARN, error = %e, path = %path.display(), "metadata failed")
}
}
}