//! The per-process logger: std.log wired to the tagged kernel log ring. //! //! A program just calls `std.log.info("mounted {s}", .{path})` (or a scoped //! logger); this backend formats the line into a fixed buffer and emits ONE //! `debug_write` record carrying the level. The kernel stamps the record with //! the sender's pid and task name (its binary path) — the process does NOT put //! its own name in the payload; attribution is the kernel's, structural and //! unforgeable. Serial shows the kernel-rendered `: message` line, and //! the logger service demultiplexes the ring into one file per process. //! //! Installed for every user binary by the root shim (library/runtime/root.zig) //! via `std_options`; a program can override by declaring its own //! `pub const std_options`. const std = @import("std"); const abi = @import("abi"); const sc = @import("system-call"); // --- the tagged log ring: raw wrappers + record types, formerly in the system.zig dump --- /// A log record's level and the ring's framing types (re-exported from the shared ABI so /// callers and the logger service don't import `abi` themselves). pub const KlogLevel = abi.KlogLevel; pub const KlogStatus = abi.KlogStatus; pub const KlogRecordHeader = abi.KlogRecordHeader; pub const klog_record_header_size = abi.klog_record_header_size; pub const klog_record_alignment = abi.klog_record_alignment; pub const klog_record_magic = abi.klog_record_magic; pub const klog_flag_truncated = abi.klog_flag_truncated; pub const klog_maximum_message = abi.klog_maximum_message; pub const maximum_process_name = abi.maximum_process_name; /// Write raw bytes to the kernel log (bring-up/panic diagnostics; ordinary output goes /// through std.log -> writeRecord). The kernel stamps the record with this process's id /// and name. Returns the byte count, or a wrapped -1. pub fn write(message: []const u8) usize { return writeRecord(.raw, message); } /// Emit one leveled record into the tagged kernel log ring. The kernel stamps /// pid/name/sequence/timestamp; the payload should be a single line. pub fn writeRecord(level: KlogLevel, message: []const u8) usize { return sc.systemCall3(.debug_write, @intFromPtr(message.ptr), message.len, @intFromEnum(level)); } /// Copy framed records out of the tagged kernel log ring starting at stream `offset` into /// `out`. Returns the byte count (0 = caught up), or null when `offset` fell behind the /// ring's tail or lies past its head (re-sync via `klogStatus`). pub fn klogRead(offset: u64, out: []u8) ?usize { const r = sc.systemCall3(.klog_read, offset, @intFromPtr(out.ptr), out.len); if (@as(isize, @bitCast(r)) < 0) return null; return r; } /// The log ring's live cursors (oldest retained offset, end of stream, next sequence) /// plus the wall-clock time of boot — how a log reader starts, detects loss, and names a /// per-boot log directory. pub fn klogStatus() ?KlogStatus { var status: KlogStatus = undefined; if (@as(isize, @bitCast(sc.systemCall1(.klog_status, @intFromPtr(&status)))) != 0) return null; return status; } fn levelOf(comptime level: std.log.Level) KlogLevel { return switch (level) { .err => .err, .warn => .warn, .info => .info, .debug => .debug, }; } pub fn logFn( comptime level: std.log.Level, comptime scope: @EnumLiteral(), comptime format: []const u8, args: anytype, ) void { // One record = one line = at most klog_maximum_message bytes of payload. // On overflow keep what fits and end with "~" so the record is still a // whole line (the kernel would split an embedded rest anyway). var buffer: [256]u8 = undefined; const prefix = if (scope == .default) "" else "(" ++ @tagName(scope) ++ ") "; const line = std.fmt.bufPrint(&buffer, prefix ++ format, args) catch truncated: { buffer[buffer.len - 1] = '~'; break :truncated buffer[0..]; }; _ = writeRecord(levelOf(level), line); } /// The std.Options the root shim installs unless the program overrides it. /// Debug level: filtering is the log *reader's* job here — the ring is cheap, /// serial is a dev convenience, and the logger service keeps everything. pub const default_options: std.Options = .{ .log_level = .debug, .logFn = logFn, };