use std::time::Duration;
use zygo_core::pool::{LogEntry, LogKind};
use zygo_core::supervisor::client::Client;
use zygo_core::supervisor::{Request, Response};
use crate::cli::Cli;
use crate::output::Style;
const FOLLOW_INTERVAL: Duration = Duration::from_millis(500);
pub fn run(cli: &Cli, name: &str, follow: bool, tail: u32, failed: bool) -> anyhow::Result<u8> {
let paths = super::paths(cli);
let mut client = Client::connect(&paths)?;
let style = Style::stdout();
let mut after = 0;
let mut limit = tail;
loop {
let response = client.send(&Request::Logs {
name: name.to_string(),
after,
limit,
failed,
tenant: None,
})?;
let (entries, next) = match response {
Response::Logs { entries, next, .. } => (entries, next),
other => return super::supervisor::report_failure(cli, &other),
};
for entry in &entries {
if cli.json {
println!("{}", serde_json::to_string(entry)?);
} else {
print(&style, entry);
}
}
if !follow {
return Ok(0);
}
after = next;
limit = u32::MAX;
std::thread::sleep(FOLLOW_INTERVAL);
}
}
fn print(style: &Style, entry: &LogEntry) {
let at = timestamp(entry.at_ms);
match &entry.kind {
LogKind::Zygote => {
println!(
"{} {} {}",
style.dim(&at),
style.dim("zygote "),
entry.text
);
}
LogKind::Request {
id,
exit_code,
wall_ms,
timed_out,
error,
stderr,
} => {
let status = if *exit_code == 0 && error.is_none() {
style.green(&format!("exit {exit_code}"))
} else if *timed_out {
style.red(&format!("exit {exit_code} (deadline)"))
} else {
style.red(&format!("exit {exit_code}"))
};
println!(
"{} {} {status} {}",
style.dim(&at),
style.dim(&format!("req {id}")),
style.dim(&format!("{wall_ms:.1} ms"))
);
for line in entry.text.lines() {
println!(" {line}");
}
for line in stderr.lines() {
println!(" {}", style.yellow(line));
}
if let Some(error) = error {
for line in error.lines() {
println!(" {}", style.red(line));
}
}
}
}
}
fn timestamp(at_ms: u64) -> String {
let secs = at_ms / 1000;
let ms = at_ms % 1000;
let day_secs = secs % 86_400;
format!(
"{:02}:{:02}:{:02}.{:03}",
day_secs / 3600,
(day_secs % 3600) / 60,
day_secs % 60,
ms
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn timestamps_are_wall_clock_to_the_millisecond() {
assert_eq!(timestamp(0), "00:00:00.000");
assert_eq!(timestamp(1_700_000_000_500), "22:13:20.500");
}
}