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}