milestone 19: hygiene sweep - dead ecs surface, single-source constants, tls classification, frontend state hazards, docker smoke network fix
CI / test (push) Successful in 1m46s
CI / test-aarch64 (push) Successful in 5m30s
CI / frontend (push) Successful in 46s
CI / cross (push) Successful in 8m12s
CI / docker (push) Successful in 3m46s

This commit is contained in:
2026-08-07 20:39:27 +02:00
parent 6f67940995
commit 6c507992e4
59 changed files with 1020 additions and 382 deletions
+69 -2
View File
@@ -55,10 +55,19 @@ pub const max_rotated_path_bytes = std.Io.Dir.max_path_bytes + 4;
pub const Stats = struct {
lines_written: u64 = 0,
lines_deduped: u64 = 0,
/// Messages that did not fit `max_message_bytes` and carry the
/// `truncation_marker` in place of their last bytes. Counted where the
/// truncation happens, so a truncated message the dedup window then
/// swallows counts here and under `lines_deduped`.
lines_truncated: u64 = 0,
rotations: u64 = 0,
sink_errors: u64 = 0,
};
/// What a truncated message ends in, the same three bytes `safe_url.zig` uses
/// so both truncations read alike to an operator.
pub const truncation_marker = "...";
// ---------------------------------------------------------------------------
// Level
// ---------------------------------------------------------------------------
@@ -287,8 +296,18 @@ pub fn logFn(
var message_buf: [max_message_bytes]u8 = undefined;
var mw: std.Io.Writer = .fixed(&message_buf);
mw.print(format, args) catch {};
const message = mw.buffered();
// A message longer than the buffer is truncated rather than dropped, but a
// silently cut line reads as a complete one: the marker says the sink cut
// it, and the counter says how often that happens.
const message = if (mw.print(format, args)) mw.buffered() else |_| blk: {
state.stats.lines_truncated += 1;
// `@min` rather than a plain subtraction: a `print` that fails on its
// first chunk leaves fewer bytes buffered than the marker is long, and
// the marker still has to fit.
const kept = @min(mw.buffered().len, message_buf.len - truncation_marker.len);
@memcpy(message_buf[kept..][0..truncation_marker.len], truncation_marker);
break :blk message_buf[0 .. kept + truncation_marker.len];
};
if (comptime isDedupScope(scope) and enabled(message_level, .warn)) {
comptime std.debug.assert(@tagName(scope).len <= max_scope_name_bytes);
@@ -1032,3 +1051,51 @@ test "rotation covers max_files - 1 generations" {
try testing.expectEqualStrings("nxdns.log.1", names[0]);
try testing.expectEqualStrings("nxdns.log.4", names[3]);
}
test "a message over the buffer is marked and counted" {
var threaded: std.Io.Threaded = .init(testing.allocator, .{});
defer threaded.deinit();
var tmp = testing.tmpDir(.{});
defer tmp.cleanup();
var path_buf: [128]u8 = undefined;
const p = try std.fmt.bufPrint(&path_buf, ".zig-cache/tmp/{s}/nxdns.log", .{tmp.sub_path});
var stderr_buf: [64]u8 = undefined;
_ = std.debug.lockStderr(&stderr_buf);
const saved = state;
defer {
closeFileLocked();
state = saved;
std.debug.unlockStderr();
}
state.stats = .{};
state.io = threaded.io();
state.threshold = .info;
state.output = .file;
state.path_len = p.len;
@memcpy(state.path_buf[0..p.len], p);
state.max_files = 0;
state.max_bytes = 0;
state.file = null;
state.file_pos = 0;
state.rotate_pending = false;
state.installed = true;
const oversized: [max_message_bytes + 100]u8 = @splat('a');
logFn(.info, .default, "{s}", .{&oversized});
try testing.expectEqual(@as(u64, 1), state.stats.lines_truncated);
try testing.expectEqual(@as(u64, 1), state.stats.lines_written);
try testing.expectEqual(@as(u64, 0), state.stats.sink_errors);
closeFileLocked();
var read_buf: [max_message_bytes * 2]u8 = undefined;
const written = try std.Io.Dir.cwd().readFile(state.io, p, &read_buf);
try testing.expect(std.mem.endsWith(u8, written, truncation_marker ++ "\n"));
// The marker replaces the last bytes of the message rather than extending
// it, so the record still fits what the buffer held.
const colon = std.mem.indexOf(u8, written, ": ").?;
try testing.expectEqual(max_message_bytes, written[colon + 2 ..].len - 1);
}