use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use clap::Args;
use serde_json::json;
use crate::api::bindings as api_bindings;
use crate::api::editor::list_bindings;
use crate::cli::commands::shared;
use crate::cli::GlobalArgs;
use crate::error::OlError;
use crate::ui::output::OutputConfig;
#[derive(Args, Debug)]
pub struct TailArgs {
#[arg(long, value_name = "ID")]
pub binding: Option<String>,
#[arg(long, default_value_t = 0, value_name = "N")]
pub since: u32,
#[arg(long, default_value_t = 2, value_name = "SECS")]
pub interval: u64,
}
pub async fn run(g: &GlobalArgs, args: TailArgs) -> Result<(), OlError> {
let _ = args.since; let out = OutputConfig::resolve(g);
let client = shared::make_client().await?;
let binding_ids: Vec<String> = match args.binding.clone() {
Some(id) => vec![id],
None => list_bindings(&client)
.await?
.into_iter()
.map(|r| r.id)
.collect(),
};
if binding_ids.is_empty() {
out.print_step("No bindings to tail.");
return Ok(());
}
let stop = Arc::new(AtomicBool::new(false));
install_ctrlc(stop.clone());
let mut tick: u64 = 0;
while !stop.load(Ordering::Relaxed) {
for id in &binding_ids {
match api_bindings::metrics(&client, id).await {
Ok(m) => {
if out.is_machine() {
out.print_json(&json!({
"tick": tick,
"binding_id": id,
"metrics": m,
}));
} else {
let p95 = m.latency_ms.as_ref().and_then(|s| s.p95);
let success = m.success_rate_24h;
let score = m.score;
let parts = vec![
p95.map(|n| format!("p95={}ms", n)),
success.map(|n| format!("success={:.1}%", n * 100.0)),
score.map(|n| format!("score={:.1}", n)),
];
let summary = parts.into_iter().flatten().collect::<Vec<_>>().join(" ");
let timestamp = chrono::Utc::now().to_rfc3339();
println!("{timestamp} {id} {summary}");
}
}
Err(e) if e.code.code == "OL-4234" => {
out.print_substep(&format!("binding {id} no longer exists; skipping"));
}
Err(e) => {
out.print_error(&e);
}
}
}
tick += 1;
let interval = Duration::from_secs(args.interval.max(1));
for _ in 0..interval.as_millis() / 100 {
if stop.load(Ordering::Relaxed) {
break;
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
out.print_step(&format!("Stopped ({tick} ticks)."));
Ok(())
}
fn install_ctrlc(stop: Arc<AtomicBool>) {
let s = stop.clone();
let _ = ctrlc::set_handler(move || {
s.store(true, Ordering::Relaxed);
});
}