nx_log_gate.nx source
↩ module page · 116 lines · 5417 B
1// nx_log_gate.nx -- the gate for the structured logger 32 modules import.
2// WHY: nx_gensota gen-3 worklist. The module's header makes a STRONG, testable promise --
3// "deterministic by default: monotonic sequence counter, not wall-clock. Tests can snapshot
4// logs BYTE-FOR-BYTE" -- and a byte-for-byte snapshot claim is only true if the format is
5// fixed-width and the counter has no holes.
6// ★THE TOOTH THAT MATTERS (T4): A FILTERED EVENT MUST NOT ADVANCE THE SEQUENCE. If a dropped
7// event consumed a sequence number, every snapshot would depend on the CURRENT LOG LEVEL --
8// two runs at different verbosity could never be diffed, which is the whole point of the design.
9// Nothing else in the system would notice; the lines still look perfectly well-formed.
10// The context takes an out_fd directly, so this gate writes to a real file and READS THE BYTES
11// BACK -- a writer is only tested by a read-back.
12// ⚠DELIBERATELY NOT TESTED: nx_log_fatal, which exits the process by contract (NX_LOG_EXIT=200).
13// A gate that called it would kill its own harness; its exit path is proven by inspection here
14// and would need a fork to test honestly -- declared, not silently skipped.
15// license_tier: ORIGINAL expect_exit: 0 No hw writes (Rule 26).
16import "nx_syscalls.nx"
17import "nx_gate_verdict.nx"
18import "nx_log.nx"
19
20const NLG_MODE: i64 = 0x1a4
21const NLG_CAP: i64 = 4096
22
23func nlg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
24func nlg_eqn(a: *u8, b: *u8, n: i64) -> i64 {
25 var i: i64 = 0
26 while i < n { if a[i] != b[i] { return 0 } i = i + 1 }
27 return 1
28}
29// read whole file into buf; returns length or -1
30func nlg_slurp(path: *u8, buf: *u8) -> i64 {
31 let lenp: *i64 = sys_mmap(16) as *i64
32 lenp[0] = 0
33 let src: *u8 = sys_read_file(path, lenp)
34 if (src as i64) == 0 { return 0 - 1 }
35 var n: i64 = lenp[0]
36 if n > NLG_CAP - 1 { n = NLG_CAP - 1 }
37 var i: i64 = 0
38 while i < n { buf[i] = src[i]; i = i + 1 }
39 buf[n] = 0 as u8
40 return n
41}
42
43func main() -> i64 {
44 let ctr: *i64 = gv_ctr()
45 gv_head("NX-LOG-GATE -- the byte-for-byte snapshot promise, made testable" as *u8)
46 let path: *u8 = "knowledge/status/log_gate_capture.tmp\x00" as *u8
47 let buf: *u8 = sys_mmap(NLG_CAP)
48
49 var t1: i64 = 1
50 if nlg_eqn(nx_log_level_name(NX_LOG_TRACE), "TRACE" as *u8, 5) == 0 { t1 = 0 }
51 if nlg_eqn(nx_log_level_name(NX_LOG_ERROR), "ERROR" as *u8, 5) == 0 { t1 = 0 }
52 if nlg_eqn(nx_log_level_name(NX_LOG_FATAL), "FATAL" as *u8, 5) == 0 { t1 = 0 }
53 if nlg_eqn(nx_log_level_name(99), "?????" as *u8, 5) == 0 { t1 = 0 }
54 gv_check("T1 level names are FIXED-WIDTH 5 and an unknown level is ????? not a crash" as *u8, t1, ctr)
55
56 var t2: i64 = 1
57 if NX_LOG_TRACE >= NX_LOG_DEBUG { t2 = 0 }
58 if NX_LOG_DEBUG >= NX_LOG_INFO { t2 = 0 }
59 if NX_LOG_INFO >= NX_LOG_WARN { t2 = 0 }
60 if NX_LOG_WARN >= NX_LOG_ERROR { t2 = 0 }
61 if NX_LOG_ERROR >= NX_LOG_FATAL { t2 = 0 }
62 gv_check("T2 levels STRICTLY ORDERED, so a >= filter is meaningful" as *u8, t2, ctr)
63
64 let fd: i64 = sys_openat_wr(path, NLG_MODE)
65 var t3: i64 = 0
66 if fd >= 0 { t3 = 1 }
67 gv_check("T3 capture file opened (instrument is able to observe)" as *u8, t3, ctr)
68
69 let c: *NxLogContext = nx_log_new(NX_LOG_INFO, fd)
70 nx_log_emit(c, NX_LOG_INFO, "gate.tag" as *u8, "first" as *u8)
71 nx_log_emit(c, NX_LOG_DEBUG, "gate.tag" as *u8, "dropped-below-min" as *u8)
72 nx_log_emit(c, NX_LOG_TRACE, "gate.tag" as *u8, "also-dropped" as *u8)
73 nx_log_emit(c, NX_LOG_ERROR, "gate.tag" as *u8, "second" as *u8)
74 sys_close(fd)
75
76 // ★THE LOAD-BEARING TOOTH
77 var t4: i64 = 0
78 if c.seq == 2 { if c.drop_cnt == 2 { t4 = 1 } }
79 gv_check("T4 a FILTERED event does NOT advance seq (snapshot stays level-independent)" as *u8, t4, ctr)
80
81 let n: i64 = nlg_slurp(path, buf)
82 var t5: i64 = 0
83 if n > 0 { t5 = 1 }
84 gv_check("T5 read-back returned bytes (not an empty-file false pass)" as *u8, t5, ctr)
85
86 // '00000000 INFO gate.tag first\n' -- 8-digit zero-padded seq, then a space
87 var t6: i64 = 0
88 if n > 9 {
89 if nlg_eqn(buf, "00000000 " as *u8, 9) == 1 { t6 = 1 }
90 }
91 gv_check("T6 seq is 8-digit ZERO-PADDED and starts at 0 for a fresh context" as *u8, t6, ctr)
92
93 var t7: i64 = 0
94 if n > 14 { if nlg_eqn(buf, "00000000 INFO " as *u8, 14) == 1 { t7 = 1 } }
95 gv_check("T7 line shape: <seq> <LEVEL-padded> ... exactly as documented" as *u8, t7, ctr)
96
97 // the SECOND emitted line must be seq 1 -- the dropped pair left no hole
98 var t8: i64 = 0
99 var i: i64 = 0
100 var nl: i64 = 0 - 1
101 while i < n { if buf[i] == (10 as u8) { if nl < 0 { nl = i } } i = i + 1 }
102 if nl > 0 { if n > nl + 9 {
103 let p2: *u8 = ((buf as i64) + nl + 1) as *u8
104 if nlg_eqn(p2, "00000001 " as *u8, 9) == 1 { t8 = 1 }
105 } }
106 gv_check("T8 the next EMITTED line is seq 00000001 -- the drops left NO HOLE" as *u8, t8, ctr)
107
108 // NEGATIVE CONTROL: the byte comparator must be able to fail.
109 var t9: i64 = 0
110 if nlg_eqn(buf, "00000001 " as *u8, 9) == 0 { if nlg_eqn(nx_log_level_name(NX_LOG_INFO), "WARN " as *u8, 5) == 0 { t9 = 1 } }
111 gv_check("T9 NEG-CONTROL: wrong seq and wrong level name are REJECTED" as *u8, t9, ctr)
112
113 let rc: i64 = gv_verdict("LOG-GATE" as *u8, ctr, "filter does not perturb the sequence; format read back" as *u8)
114 sys_exit(rc)
115 return rc
116}