code wiki / _hdl_build / nx_metrics.nx
nx_metrics.nx source
↩ module page · 122 lines · 5510 B
1// nx_metrics.nx -- CLI over the sovereign metrics ring (nx_metrics_ring). Query daemon health HISTORY +
2// uptime SLO from the bounded binary TSDB. `put` injects a sample (used by the gate for deterministic
3// proof; the live writer is nx_metrics_sampler). nx_heal forks this for its MCP uptime/history verbs.
4// nx_metrics uptime <ring> <name> <window_sec> -> "<name> uptime <permille>/1000 over <w>s (n=<samples>)"
5// nx_metrics history <ring> <name> [n] -> recent samples: <ts> <verdict> fails=<f>
6// nx_metrics count <ring> -> total records stored
7// nx_metrics put <ring> <name> <verdict> <fails> <ts>-> append one sample (deterministic)
8// license_tier: ORIGINAL module: nishi-core.ops.metrics_cli
9import "nx_syscalls.nx"
10import "nx_metrics_ring.nx"
11
12func mc_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
13func mc_pn(v: i64) -> i64 {
14 let b: *u8 = sys_mmap(28)
15 var m: i64 = v
16 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
17 let t: *u8 = sys_mmap(28); var k: i64 = 0
18 if m == 0 { t[0] = 48 as u8; k = 1 }
19 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
20 var i: i64 = 0; while i < k { b[i] = t[k - 1 - i]; i = i + 1 }
21 sys_write(1, b, k); return 0
22}
23func mc_streq(a: *u8, b: *u8) -> i64 {
24 var i: i64 = 0
25 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
26 if b[i] != (0 as u8) { return 0 }
27 return 1
28}
29func mc_atoi(s: *u8) -> i64 {
30 var v: i64 = 0; var i: i64 = 0; var go: i64 = 1
31 while go == 1 { let c: i64 = s[i] as i64; if c < 48 { go = 0 } else { if c > 57 { go = 0 } else { v = v * 10 + (c - 48); i = i + 1 } } }
32 return v
33}
34func mc_vname(v: i64) -> *u8 {
35 if v == MR_V_SERVING { return "SERVING" as *u8 }
36 if v == MR_V_REFUSED { return "REFUSED" as *u8 }
37 if v == MR_V_HUNG { return "HUNG" as *u8 }
38 return "BADRESP" as *u8
39}
40
41func main(argc: i64, argv: *i64) -> i64 {
42 if argc < 3 { mc_p("usage: nx_metrics uptime|history|count|put <ring> ...\n" as *u8); return 2 }
43 let verb: *u8 = argv[1] as *u8
44 let ring: *u8 = argv[2] as *u8
45
46 if mc_streq(verb, "put" as *u8) == 1 {
47 if argc < 7 { mc_p("usage: nx_metrics put <ring> <name> <verdict> <fails> <ts>\n" as *u8); return 2 }
48 let h: i64 = mr_hash(argv[3] as *u8)
49 let rc: i64 = mr_append(ring, mc_atoi(argv[6] as *u8), h, mc_atoi(argv[4] as *u8), mc_atoi(argv[5] as *u8))
50 if rc != 0 { mc_p("put: append failed\n" as *u8); return 1 }
51 return 0
52 }
53 if mc_streq(verb, "count" as *u8) == 1 {
54 let fd: i64 = sys_openat_rd(ring)
55 if fd < 0 { mc_p("0\n" as *u8); return 0 }
56 let hdr: *u8 = sys_mmap(64)
57 mc_pn(mr_count(fd, hdr)); mc_p("\n" as *u8)
58 sys_close(fd)
59 return 0
60 }
61 if mc_streq(verb, "uptime" as *u8) == 1 {
62 if argc < 5 { mc_p("usage: nx_metrics uptime <ring> <name> <window_sec>\n" as *u8); return 2 }
63 let name: *u8 = argv[3] as *u8
64 let win: i64 = mc_atoi(argv[4] as *u8)
65 let now: i64 = sys_now_realtime_sec()
66 let h: i64 = mr_hash(name)
67 // count samples in window for the report
68 let fd: i64 = sys_openat_rd(ring)
69 var n: i64 = 0
70 if fd >= 0 {
71 let hdr: *u8 = sys_mmap(64)
72 let cnt: i64 = mr_count(fd, hdr)
73 let rec: *u8 = sys_mmap(32)
74 var i: i64 = 0
75 while i < cnt {
76 if mr_pread(fd, 64 + i * 32, rec, 32) == 32 { if mr_geti(rec, 8) == h { let ts: i64 = mr_geti(rec, 0); if ts >= now - win { if ts <= now { n = n + 1 } } } }
77 i = i + 1
78 }
79 sys_close(fd)
80 }
81 let up: i64 = mr_uptime(ring, h, win, now)
82 mc_p(name); mc_p(" uptime " as *u8)
83 if up < 0 { mc_p("NO-DATA" as *u8) } else { mc_pn(up); mc_p("/1000" as *u8) }
84 mc_p(" over " as *u8); mc_pn(win); mc_p("s (n=" as *u8); mc_pn(n); mc_p(" samples)\n" as *u8)
85 if up < 0 { return 1 }
86 return 0
87 }
88 if mc_streq(verb, "history" as *u8) == 1 {
89 if argc < 4 { mc_p("usage: nx_metrics history <ring> <name> [n]\n" as *u8); return 2 }
90 let name: *u8 = argv[3] as *u8
91 var want: i64 = 20
92 if argc >= 5 { want = mc_atoi(argv[4] as *u8) }
93 let h: i64 = mr_hash(name)
94 let fd: i64 = sys_openat_rd(ring)
95 if fd < 0 { mc_p("(no ring)\n" as *u8); return 1 }
96 let hdr: *u8 = sys_mmap(64)
97 let cnt: i64 = mr_count(fd, hdr)
98 let rec: *u8 = sys_mmap(32)
99 // walk newest-first: find matching indices from the end
100 var shown: i64 = 0
101 var i: i64 = cnt - 1
102 mc_p(name); mc_p(" history (newest first):\n" as *u8)
103 while i >= 0 {
104 if shown >= want { i = 0 - 1 }
105 else {
106 if mr_pread(fd, 64 + i * 32, rec, 32) == 32 { if mr_geti(rec, 8) == h {
107 mc_p(" ts=" as *u8); mc_pn(mr_geti(rec, 0))
108 mc_p(" " as *u8); mc_p(mc_vname(mr_geti(rec, 16)))
109 mc_p(" fails=" as *u8); mc_pn(mr_geti(rec, 24))
110 mc_p("\n" as *u8)
111 shown = shown + 1
112 } }
113 i = i - 1
114 }
115 }
116 sys_close(fd)
117 if shown == 0 { mc_p(" (no samples for this name)\n" as *u8) }
118 return 0
119 }
120 mc_p("usage: nx_metrics uptime|history|count|put <ring> ...\n" as *u8)
121 return 2
122}