use arcbox_cli::client;
use anyhow::Result;
use clap::Args;
use tokio_util::sync::CancellationToken;
#[derive(Args)]
pub struct LogsArgs {
pub container: String,
#[arg(short, long)]
pub follow: bool,
#[arg(short, long)]
pub timestamps: bool,
#[arg(long, default_value = "all")]
pub tail: String,
#[arg(long)]
pub since: Option<String>,
#[arg(long)]
pub until: Option<String>,
#[arg(long)]
pub details: bool,
}
pub async fn execute(args: LogsArgs) -> Result<()> {
let daemon = client::get_client().await?;
let mut params = vec![
("stdout".to_string(), "true".to_string()),
("stderr".to_string(), "true".to_string()),
];
if args.follow {
params.push(("follow".to_string(), "true".to_string()));
}
if args.timestamps {
params.push(("timestamps".to_string(), "true".to_string()));
}
if args.tail != "all" {
params.push(("tail".to_string(), args.tail.clone()));
}
if let Some(ref since) = args.since {
params.push(("since".to_string(), since.clone()));
}
if let Some(ref until) = args.until {
params.push(("until".to_string(), until.clone()));
}
if args.details {
params.push(("details".to_string(), "true".to_string()));
}
let query = params
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<_>>()
.join("&");
let path = format!("/v1.43/containers/{}/logs?{}", args.container, query);
if args.follow {
let cancel_token = CancellationToken::new();
let cancel_clone = cancel_token.clone();
let ctrl_c_task = tokio::spawn(async move {
if tokio::signal::ctrl_c().await.is_ok() {
tracing::debug!("Received Ctrl+C, cancelling log stream");
cancel_clone.cancel();
}
});
let result = daemon
.stream_logs_with_cancel(&path, &mut print_log_frame, cancel_token)
.await;
ctrl_c_task.abort();
result?;
} else {
let logs = daemon.get_raw(&path).await?;
print_logs(&logs);
}
Ok(())
}
fn print_logs(data: &[u8]) {
let mut offset = 0;
while offset + 8 <= data.len() {
let size = u32::from_be_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]) as usize;
let end = offset + 8 + size;
if end > data.len() {
break;
}
let content = &data[offset + 8..end];
if let Ok(s) = std::str::from_utf8(content) {
print!("{}", s);
}
offset = end;
}
if offset == 0 && !data.is_empty() {
if let Ok(s) = std::str::from_utf8(data) {
print!("{}", s);
}
}
}
fn print_log_frame(data: &[u8]) {
if let Ok(s) = std::str::from_utf8(data) {
print!("{}", s);
}
}