1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
//! Rotation-aware multi-file log streaming.
//!
//! Two public entry points keyed by sandbox name:
//!
//! - [`read_logs`] returns a snapshot `Vec<LogEntry>` filtered with
//! [`LogOptions`]. Reads everything currently on disk, sorts by
//! timestamp, and returns.
//! - [`log_stream`] returns a [`futures::Stream`] over the same
//! files, suitable for live-tailing or replaying a fixed range.
//! Uses filesystem change notifications (the `notify` crate) for
//! live updates with a fallback poll, and stamps each entry with
//! an opaque [`LogCursor`] for exact per-source resume.
//!
//! # Files read
//!
//! - `exec.log` + rotated siblings (`exec.log.1` ... `exec.log.4`):
//! JSON Lines, captured stdout / stderr / pty output written by
//! the runtime relay tap. Rotates at 10 MiB per file, retains up
//! to four historical files on disk (~40 MiB ceiling).
//! - `runtime.log`: plain text, runtime diagnostics. Only read when
//! `System` is in the requested sources. Does not rotate.
//! - `kernel.log`: plain text, guest kernel console. Only read when
//! `System` is in the requested sources. Does not rotate.
//!
//! Adding a new log file type is one entry in `LOG_FILES`.
//!
//! # Ordering contract
//!
//! - [`read_logs`] returns entries in strict chronological order
//! (it sorts by timestamp before returning).
//! - [`log_stream`] preserves chronological order **within each
//! source** but emits **across sources** in "as parsed" order —
//! a `runtime.log` entry timestamped slightly earlier than an
//! `exec.log` entry may be yielded after it if the `exec.log`
//! read landed first. Use [`read_logs`] if you need strict
//! global ordering.
//!
//! # Keeping up
//!
//! [`log_stream`] holds an open file descriptor on each file it is
//! reading. Because rotation is a `rename` (not a delete), the FD
//! remains valid across rotations: the stream can drain whatever
//! the producer wrote to the now-rotated file before transitioning
//! to the new active file.
//!
//! However, the producer caps disk retention at four rotated files
//! (~40 MiB). If a consumer falls behind enough that the inode it
//! was reading rotates past that retention window before the
//! stream catches up, the file is overwritten and lost. When that
//! happens, the stream yields
//! [`crate::MicrosandboxError::MissedRotation`]
//! and ends. Hard-fail by design — restart from
//! [`LogStreamStart::Beginning`], [`LogStreamStart::Since`] with
//! the current time, or [`LogStreamStart::From`] with the cursor
//! of the last entry successfully consumed.
pub use ;
pub use ;
pub use ;
use PathBuf;
use Stream;
use ;
use crate::;
//--------------------------------------------------------------------------------------------------
// LOG_FILES
//--------------------------------------------------------------------------------------------------
/// The set of log files microsandbox produces. Add a new file
/// type by adding an entry here — the [`LogEngine`] opens a
/// reader for any entry whose `produces` list intersects the
/// caller's requested sources.
const LOG_FILES: & = &;
//--------------------------------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------------------------------
/// Chronologically sorted log snapshot plus the cursor at the end
/// of the drained on-disk content.
/// Compute the on-disk log directory for a sandbox name.
/// Read all matching log entries for the named sandbox.
///
/// Sandbox names are limited to 128 UTF-8 bytes.
///
/// Returns entries sorted by timestamp (strict chronological order
/// across all sources). Returns
/// [`MicrosandboxError::SandboxNotFound`] if the sandbox's log
/// directory doesn't exist.
///
/// Implemented as a drain of [`log_stream`] with `follow: false`,
/// sorted post-collect; `until` and `tail` are applied
/// post-collect because the stream's per-source ordering doesn't
/// match snapshot's "filter after sort" contract.
pub async
/// Read all matching log entries and return the snapshot end cursor.
///
/// This is useful when handing a bounded historical read to
/// [`log_stream`] with [`LogStreamStart::From`] without losing log
/// lines written between the snapshot drain and follow startup.
pub async
/// Stream log entries for the named sandbox.
///
/// Sandbox names are limited to 128 UTF-8 bytes.
///
/// Returns [`MicrosandboxError::SandboxNotFound`] if the sandbox's
/// log directory doesn't exist. Within each source, entries are
/// chronological; across sources, ordering is "as parsed."
pub async