logger: per-process log files under /var/log/<boot-stamp>/
The logger service drains the tagged ring every 250 ms and demultiplexes
it into one file per process on the flash volume:
/mnt/usb/var/log/2026-07-21T150434Z/system/services/fat.log
[ 0.214] mounted FAT (fat32, 128992 clusters, partition lba 0)
The boot stamp is the wall-clock anchor from klog_status (FAT-safe, no
colons); the kernel's records go to kernel.log; each line carries the
record's monotonic timestamp and level. Storage is best-effort and
late — the ring buffers a whole boot until the mount appears, then the
first drain writes the backlog, storage-stack records included. Lost
records surface as '-- N records lost --' from sequence gaps. Files
close (= fat's device cache flush) after a 2 s quiet period, bounding
data-at-risk without per-record flush thrash. The logger announces
itself once — a periodic status line would feed the stream it drains.
init spawns the logger last, so the reverse-order shutdown stops it
first and its final drain runs over a live storage chain; log-flush and
init's own DANOS.LOG shutdown flush retire (superseded). runtime.fs
makePath treats components at or above a mount point as router names —
create is best-effort per prefix, the final verdict is exists(path).
This commit is contained in:
@@ -22,11 +22,6 @@ const runtime = @import("runtime");
|
||||
const power = runtime.power_protocol;
|
||||
const build_options = @import("build_options");
|
||||
|
||||
/// Where the kernel boot log is persisted on the USB FAT volume — an 8.3 name at
|
||||
/// the mount root (see system/services/log-flush). init writes it at shutdown;
|
||||
/// the log-flush one-shot writes it once at boot.
|
||||
const log_path = "/mnt/usb/DANOS.LOG";
|
||||
|
||||
/// The system services init brings up at boot, in order, by binary path. This is
|
||||
/// init's policy — the microkernel keeps such choices in user space, not the
|
||||
/// kernel. Drivers are absent on purpose: the device manager owns those. (A
|
||||
@@ -39,6 +34,10 @@ const boot_services = [_][]const u8{
|
||||
"/system/services/fat",
|
||||
"/system/services/display",
|
||||
"/system/services/display-demo",
|
||||
// Last: at shutdown children stop in reverse order, so the logger goes down
|
||||
// FIRST — its final drain still has the fat server (and the whole storage
|
||||
// chain) alive underneath it.
|
||||
"/system/services/logger",
|
||||
};
|
||||
|
||||
/// The live process id of each boot service (0 = not running), indexed by its position
|
||||
@@ -86,14 +85,6 @@ pub fn main() void {
|
||||
if (runtime.system.spawnSupervised(service, &.{}, supervision_endpoint)) |id| child_ids[i] = id;
|
||||
}
|
||||
|
||||
// Once the storage stack is up, a one-shot copies the boot log to the USB
|
||||
// volume (/mnt/usb/DANOS.LOG) so it can be read on another machine — the only
|
||||
// way to see it on a headless/real board with no host capturing serial. Fire
|
||||
// and forget: it polls for the mount itself, and is deliberately NOT one of
|
||||
// init's supervised children (a transient one-shot must not be stopped-and-
|
||||
// waited-for during shutdown).
|
||||
_ = runtime.system.spawn("/system/services/log-flush");
|
||||
|
||||
// Subscribe to power events (retry: the power service registers well after
|
||||
// init starts). Best-effort — without it, a `terminate` signal still
|
||||
// triggers the same shutdown path.
|
||||
@@ -178,30 +169,6 @@ fn subscribePower() void {
|
||||
_ = runtime.ipc.callCap(h, std.mem.asBytes(&request), &reply, supervision_endpoint) catch {};
|
||||
}
|
||||
|
||||
/// Copy the whole kernel log to /mnt/usb/DANOS.LOG (the same file log-flush
|
||||
/// writes at boot), so a poweroff captures the fullest log. Best-effort: if the
|
||||
/// USB volume is not mounted, the open fails and it does nothing. Must run while
|
||||
/// the storage services are still alive (see shutDown).
|
||||
fn flushKernelLog() void {
|
||||
// Truncate on open so this fuller flush replaces the boot-time one cleanly.
|
||||
var file = runtime.fs.open(log_path, .{ .create = true, .truncate = true }) orelse return; // no USB volume
|
||||
defer file.close();
|
||||
var chunk: [4096]u8 = undefined;
|
||||
// Interim (until the logger service in M-E): the stream is now framed
|
||||
// records, so the file holds raw frames rather than plain text. Start at
|
||||
// the ring's tail — offset 0 is gone once the ring has wrapped.
|
||||
var offset: u64 = if (runtime.system.klogStatus()) |s| s.tail else 0;
|
||||
const start = offset;
|
||||
while (true) {
|
||||
const got = runtime.system.klogRead(offset, &chunk) orelse break; // cursor lost
|
||||
if (got == 0) break; // caught up
|
||||
if (file.writeAll(chunk[0..got]) == null) break; // storage went away
|
||||
offset += got;
|
||||
}
|
||||
var line: [96]u8 = undefined;
|
||||
_ = runtime.system.write(std.fmt.bufPrint(&line, "/system/services/init: flushed log to {s} ({d} bytes)\n", .{ log_path, offset - start }) catch "");
|
||||
}
|
||||
|
||||
/// The stop sequence: persist the log while storage is still up, then terminate
|
||||
/// each child in reverse spawn order (vfs last — other services may flush through
|
||||
/// it), waiting up to a deadline for each to exit before killing it, then ask the
|
||||
@@ -209,10 +176,9 @@ fn flushKernelLog() void {
|
||||
fn shutDown() void {
|
||||
shutting_down = true; // the stop loop below kills children — those deaths aren't crashes
|
||||
_ = runtime.system.write("/system/services/init: shutting down\n");
|
||||
// Persist the fullest log to the USB volume BEFORE tearing anything down: the
|
||||
// reverse-order stop loop below kills the fat server first, so /mnt/usb must be
|
||||
// written while it is still mounted.
|
||||
flushKernelLog();
|
||||
// Log persistence is the logger service's job: it is the LAST boot service,
|
||||
// so the reverse-order stop below terminates it first and its final drain
|
||||
// runs while the whole storage chain is still alive.
|
||||
var i = boot_services.len;
|
||||
while (i > 0) {
|
||||
i -= 1;
|
||||
|
||||
Reference in New Issue
Block a user