Skip to main content

agent_exec/
tail.rs

1//! Implementation of the `tail` sub-command.
2
3use anyhow::Result;
4
5use crate::jobstore::{JobDir, resolve_root};
6use crate::schema::{Response, TailData};
7
8/// Options for the `tail` sub-command.
9#[derive(Debug)]
10pub struct TailOpts<'a> {
11    pub job_id: &'a str,
12    pub root: Option<&'a str>,
13    /// Number of lines to show from the end of each log.
14    pub tail_lines: u64,
15    /// Maximum bytes to read from the end of each log.
16    pub max_bytes: u64,
17}
18
19impl<'a> Default for TailOpts<'a> {
20    fn default() -> Self {
21        TailOpts {
22            job_id: "",
23            root: None,
24            tail_lines: 50,
25            max_bytes: 65536,
26        }
27    }
28}
29
30/// Execute `tail`: read log tails and emit JSON.
31pub fn execute(opts: TailOpts) -> Result<()> {
32    let root = resolve_root(opts.root);
33    let job_dir = JobDir::open(&root, opts.job_id)?;
34
35    let stdout_log_path = job_dir.stdout_path();
36    let stderr_log_path = job_dir.stderr_path();
37
38    // Use the shared helper so that byte metric calculation is in one place.
39    let stdout = job_dir.read_tail_metrics("stdout.log", opts.tail_lines, opts.max_bytes);
40    let stderr = job_dir.read_tail_metrics("stderr.log", opts.tail_lines, opts.max_bytes);
41
42    let response = Response::new(
43        "tail",
44        TailData {
45            job_id: opts.job_id.to_string(),
46            stdout_tail: stdout.tail,
47            stderr_tail: stderr.tail,
48            truncated: stdout.truncated || stderr.truncated,
49            encoding: "utf-8-lossy".to_string(),
50            stdout_log_path: stdout_log_path.display().to_string(),
51            stderr_log_path: stderr_log_path.display().to_string(),
52            stdout_observed_bytes: stdout.observed_bytes,
53            stderr_observed_bytes: stderr.observed_bytes,
54            stdout_included_bytes: stdout.included_bytes,
55            stderr_included_bytes: stderr.included_bytes,
56        },
57    );
58    response.print();
59    Ok(())
60}