use anyhow::Result;
use kerf::{Config, Kerf, Match, MatcherSet};
use std::env;
use std::io::{BufRead, BufReader};
use std::process::{Command, Stdio};
use std::sync::Arc;
use tokio::sync::Mutex;
use tracing::{debug, error, info, trace, warn};
async fn run_as_child() -> Result<()> {
let filter_set = MatcherSet::from_matcher(Match::trace().all_modules());
let config = Config::from_tab(("Child", filter_set));
let tracer = Kerf::init(config)?;
tracer
.set_callback(move |event, _tab_names| {
println!("C|{}", event.format());
})?
.await??;
for i in 0..10 {
trace!("Child trace message {}", i);
debug!("Child debug message {}", i);
info!("Child info message {}", i);
if i % 3 == 0 {
warn!("Child warning message {}", i);
}
if i % 5 == 0 {
error!("Child error message {}", i);
}
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
}
Ok(())
}
async fn run_as_parent() -> Result<()> {
let filter_set = MatcherSet::from_matcher(Match::trace().all_modules());
let config = Config::from_tab(("Parent", filter_set));
let tracer = Kerf::init(config)?;
let log_count = Arc::new(Mutex::new((0, 0))); let log_count_clone = log_count.clone();
tracer
.set_callback(move |event, _tab_names| {
let count_clone = log_count_clone.clone();
tokio::spawn(async move {
let mut counts = count_clone.lock().await;
counts.0 += 1; println!("P|{}", event.format());
});
})?
.await??;
let child = Command::new(env::current_exe()?)
.arg("--child")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
info!("Parent started child process with PID: {}", child.id());
let stdout = child.stdout.expect("Failed to capture child stdout");
let stderr = child.stderr.expect("Failed to capture child stderr");
let log_count_clone = log_count.clone();
tokio::spawn(async move {
let reader = BufReader::new(stdout);
for line in reader.lines() {
match line {
Ok(line) => {
let mut counts = log_count_clone.lock().await;
counts.1 += 1; println!("{line}"); }
Err(e) => eprintln!("Error reading from child stdout: {e}"),
}
}
});
tokio::spawn(async move {
let reader = BufReader::new(stderr);
for line in reader.lines() {
match line {
Ok(line) => {
eprintln!("Child stderr: {line}");
}
Err(e) => eprintln!("Error reading from child stderr: {e}"),
}
}
});
for i in 0..15 {
trace!("Parent trace message {}", i);
debug!("Parent debug message {}", i);
info!("Parent info message {}", i);
if i % 4 == 0 {
warn!("Parent warning message {}", i);
}
if i % 7 == 0 {
error!("Parent error message {}", i);
}
tokio::time::sleep(tokio::time::Duration::from_millis(700)).await;
}
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
let counts = log_count.lock().await;
info!("Log stats - Parent: {}, Child: {}", counts.0, counts.1);
Ok(())
}
#[tokio::main]
async fn main() -> Result<()> {
let args: Vec<String> = env::args().collect();
let is_child = args.len() > 1 && args[1] == "--child";
if is_child {
println!("Starting as child process");
run_as_child().await?;
} else {
println!("Starting as parent process");
run_as_parent().await?;
}
Ok(())
}