Skip to main content

kanade_shared/wire/
logs.rs

1//! Wire types for the on-demand log fetch path
2//! (`logs.fetch.<pc_id>` NATS request/reply).
3//!
4//! Operator → agent: `LogsRequest`. Agent → operator: raw UTF-8 log
5//! bytes (the tail of the most-recent rolling file). Replies are
6//! capped well under the 1 MB NATS payload limit by the requested
7//! line count; for full-file pulls a future variant will fall back
8//! to JetStream Object Store.
9
10use serde::{Deserialize, Serialize};
11
12/// Payload sent on `logs.fetch.<pc_id>` to request the tail of an
13/// agent's log file. Unknown / missing fields fall back to sensible
14/// defaults so the CLI / Web UI can grow the shape later without
15/// retraining every fleet host.
16#[derive(Serialize, Deserialize, Debug, Clone)]
17#[serde(default)]
18pub struct LogsRequest {
19    /// Number of trailing lines to return from the most recent log
20    /// file. Defaults to 500 — fits comfortably in a single NATS
21    /// reply.
22    pub tail_lines: u32,
23}
24
25impl Default for LogsRequest {
26    fn default() -> Self {
27        Self { tail_lines: 500 }
28    }
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34
35    #[test]
36    fn defaults_to_500_lines() {
37        assert_eq!(LogsRequest::default().tail_lines, 500);
38    }
39
40    #[test]
41    fn missing_field_round_trips_to_default() {
42        let req: LogsRequest = serde_json::from_str("{}").unwrap();
43        assert_eq!(req.tail_lines, 500);
44    }
45
46    #[test]
47    fn explicit_field_overrides_default() {
48        let req: LogsRequest = serde_json::from_str(r#"{"tail_lines": 100}"#).unwrap();
49        assert_eq!(req.tail_lines, 100);
50    }
51}