code wiki / _hdl_build / nx_apm_dashboard.nx

nx_apm_dashboard.nx source

↩ module page · 228 lines · 13402 B

1// nx_apm_dashboard.nx -- SOVEREIGN "New-Relic-style" status board: renders the APM time-series 2// (knowledge/status/apm_collect.tsv, written by nx_apm_collect) into a self-contained HTML page. 3// 4// TTFB-honest by construction: the verdict is recomputed from the row's stats (downs/timeouts/stalls), 5// and `slowclose` (keep-alive/idle-hold = fast first byte, slow close) is shown as an INFO badge, NEVER 6// as a failure -- so the board never reproduces the read-until-close "20s wedge" artifact. Zero 3rd-party 7// JS/CDN (sovereignty invariant): inline CSS only. Groups rows by daemon label, latest sample wins, 8// renders a colour-coded card grid + an incidents list. 9// 10// in: knowledge/status/apm_collect.tsv (argv[1] overrides) 11// out: knowledge/status/apm_dashboard.html (argv[2] overrides) 12// TSV cols: epoch(0) label(1) port(2) path(3) conc(4) oks(5) nres(6) min(7) p50(8) p95(9) max(10) 13// stalls(11) timeouts(12) downs(13) maxtotal(14) slowclose(15) 14// license_tier: ORIGINAL 15import "nx_syscalls.nx" 16const AD_MAGIC_65536: i64 = 65536 17const AD_MAGIC_4096: i64 = 4096 18const AD_MAGIC_262144: i64 = 262144 19 20const AD_TSV: *u8 = "knowledge/status/apm_collect.tsv" 21const AD_HTML: *u8 = "knowledge/status/apm_dashboard.html" 22const AD_MAX_LABELS: i64 = 64 23const AD_LBL_W: i64 = 40 // bytes per stored label 24 25// verdict codes (recomputed from the row -- ttfb-honest) 26const AD_GREEN: i64 = 0 27const AD_AMBER: i64 = 1 28const AD_RED: i64 = 2 29const AD_DOWN: i64 = 3 30 31func ad_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 32func ad_w(s: *u8) -> i64 { return sys_write(1, s, ad_slen(s)) } 33func ad_wn(v: i64) -> i64 { 34 let t: *u8 = sys_mmap(24); var m: i64 = v; var k: i64 = 0 35 if m == 0 { t[0] = 48 as u8; k = 1 } 36 while m > 0 { t[k] = (48 + (m - (m/10)*10)) as u8; m = m/10; k = k + 1 } 37 let o: *u8 = sys_mmap(24); var i: i64 = 0; while i < k { o[i] = t[k-1-i]; i = i + 1 } 38 return sys_write(1, o, k) 39} 40func ad_atoi(s: *u8) -> i64 { var v: i64 = 0; var i: i64 = 0; while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v*10 + (c-48) } } i = i + 1 } return v } 41func ad_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] != (0 as u8) { return 0 } return 1 } 42 43// bounded file read (NOT sys_read_file -- that mmaps 4 GiB/call) 44func ad_read_small(path: *u8, buf: *u8, cap: i64) -> i64 { 45 let fd: i64 = sys_openat_rd(path) 46 if fd < 0 { return 0 - 1 } 47 var off: i64 = 0; var go: i64 = 1 48 while go == 1 { if off >= cap { go = 0 } else { let r: i64 = sys_read(fd, buf + off, cap - off); if r <= 0 { go = 0 } else { off = off + r } } } 49 sys_close(fd); return off 50} 51// copy one tab/newline-delimited field; stop[0]=0 tab,1 newline,2 eof; returns next pos. 52func ad_field(buf: *u8, n: i64, pos: i64, out: *u8, outcap: i64, stop: *i64) -> i64 { 53 var i: i64 = pos; var w: i64 = 0; var done: i64 = 0 54 while done == 0 { 55 if i >= n { stop[0] = 2; done = 1 } 56 else { let c: i64 = buf[i] as i64 57 if c == 9 { stop[0] = 0; i = i + 1; done = 1 } 58 else { if c == 10 { stop[0] = 1; i = i + 1; done = 1 } 59 else { if w < outcap - 1 { out[w] = buf[i]; w = w + 1 } i = i + 1 } } } 60 } 61 out[w] = 0 as u8; return i 62} 63 64// append C-string into html buf at off, return new off 65func ad_bs(buf: *u8, off: i64, s: *u8) -> i64 { var o: i64 = off; var i: i64 = 0; while s[i] != (0 as u8) { buf[o] = s[i]; o = o + 1; i = i + 1 } return o } 66// append decimal into html buf at off, return new off 67func ad_bn(buf: *u8, off: i64, v: i64) -> i64 { 68 var o: i64 = off; var m: i64 = v 69 if m < 0 { buf[o] = 45 as u8; o = o + 1; m = 0 - m } 70 let t: *u8 = sys_mmap(24); var k: i64 = 0 71 if m == 0 { t[0] = 48 as u8; k = 1 } 72 while m > 0 { t[k] = (48 + (m - (m/10)*10)) as u8; m = m/10; k = k + 1 } 73 var i: i64 = 0; while i < k { buf[o + i] = t[k - 1 - i]; i = i + 1 } 74 return o + k 75} 76 77// verdict from a row's stats (ttfb-honest: slowclose is NOT a factor) 78func ad_verdict(conc: i64, oks: i64, stalls: i64, timeouts: i64, downs: i64) -> i64 { 79 if conc > 0 { if downs >= conc { return AD_DOWN } } 80 if timeouts > 0 { return AD_RED } 81 if downs > 0 { return AD_RED } 82 if stalls > 0 { return AD_AMBER } 83 return AD_GREEN 84} 85func ad_vname(v: i64) -> *u8 { 86 if v == AD_GREEN { return "GREEN" as *u8 } 87 if v == AD_AMBER { return "AMBER" as *u8 } 88 if v == AD_RED { return "RED" as *u8 } 89 return "DOWN" as *u8 90} 91func ad_vcolor(v: i64) -> *u8 { 92 if v == AD_GREEN { return "#1a7f37" as *u8 } 93 if v == AD_AMBER { return "#bf8700" as *u8 } 94 if v == AD_RED { return "#cf222e" as *u8 } 95 return "#6e7781" as *u8 96} 97 98// find label in store, or add it; returns index (or -1 if full) 99func ad_find_or_add(store: *u8, np: *i64, name: *u8) -> i64 { 100 var i: i64 = 0 101 while i < np[0] { if ad_streq(store + i*AD_LBL_W, name) == 1 { return i } i = i + 1 } 102 if np[0] >= AD_MAX_LABELS { return 0 - 1 } 103 let idx: i64 = np[0] 104 let dst: *u8 = store + idx*AD_LBL_W 105 var w: i64 = 0; while name[w] != (0 as u8) { if w < AD_LBL_W - 1 { dst[w] = name[w] } w = w + 1 } 106 dst[w] = 0 as u8 107 np[0] = idx + 1 108 return idx 109} 110 111// seg-store key:value helpers (NO tsv): find "key=<int>" on its own line -> the int. 112func ad_starts(buf: *u8, ls: i64, end: i64, key: *u8) -> i64 { 113 var m: i64 = 0 114 while key[m] != (0 as u8) { if ls+m >= end { return 0 } if buf[ls+m] != key[m] { return 0 } m = m + 1 } 115 return m 116} 117func ad_kv(buf: *u8, n: i64, key: *u8) -> i64 { 118 var ls: i64 = 0; var i: i64 = 0 119 while i <= n { 120 var nl: i64 = 0; if i >= n { nl = 1 } else { if buf[i] == (10 as u8) { nl = 1 } } 121 if nl == 1 { 122 let kl: i64 = ad_starts(buf, ls, i, key) 123 if kl > 0 { var v: i64 = 0; var p: i64 = ls + kl; while p < i { let c: i64 = buf[p] as i64; if c >= 48 { if c <= 57 { v = v*10 + (c-48) } } p = p + 1 } return v } 124 ls = i + 1 125 } 126 i = i + 1 127 } 128 return 0 129} 130 131func main(argc: i64, argv: *i64) -> i64 { 132 var storedir: *u8 = "knowledge/store" as *u8; if argc >= 2 { storedir = argv[1] as *u8 } 133 var html: *u8 = AD_HTML; if argc >= 3 { html = argv[2] as *u8 } 134 135 // per-label latest stats (parallel arrays) 136 let store: *u8 = sys_mmap(AD_MAX_LABELS * AD_LBL_W) 137 let np: *i64 = sys_mmap(8) as *i64; np[0] = 0 138 let s_port: *i64 = sys_mmap(AD_MAX_LABELS*8) as *i64 139 let s_conc: *i64 = sys_mmap(AD_MAX_LABELS*8) as *i64 140 let s_oks: *i64 = sys_mmap(AD_MAX_LABELS*8) as *i64 141 let s_p50: *i64 = sys_mmap(AD_MAX_LABELS*8) as *i64 142 let s_max: *i64 = sys_mmap(AD_MAX_LABELS*8) as *i64 143 let s_stall:*i64 = sys_mmap(AD_MAX_LABELS*8) as *i64 144 let s_to: *i64 = sys_mmap(AD_MAX_LABELS*8) as *i64 145 let s_down: *i64 = sys_mmap(AD_MAX_LABELS*8) as *i64 146 let s_slow: *i64 = sys_mmap(AD_MAX_LABELS*8) as *i64 147 let s_epoch:*i64 = sys_mmap(AD_MAX_LABELS*8) as *i64 148 let s_samp: *i64 = sys_mmap(AD_MAX_LABELS*8) as *i64 149 var rows: i64 = 0 150 151 // read the SOVEREIGN seg-store (NO tsv): apm-ids index -> per-label key:value records 152 let idxp: *u8 = sys_mmap(512); var ipo: i64 = ad_bs(idxp, 0, storedir); idxp[ipo]=47 as u8; ipo=ipo+1; ipo=ad_bs(idxp, ipo, "apm-ids" as *u8); idxp[ipo]=0 as u8 153 let idxb: *u8 = sys_mmap(AD_MAGIC_65536); let idn: i64 = ad_read_small(idxp, idxb, AD_MAGIC_65536) 154 if idn <= 0 { ad_w("nx_apm_dashboard: no seg-store data at " as *u8); ad_w(idxp); ad_w("\n" as *u8); return 1 } 155 let lbl: *u8 = sys_mmap(64); let recp: *u8 = sys_mmap(512); let rec: *u8 = sys_mmap(AD_MAGIC_4096) 156 var ls: i64 = 0; var i: i64 = 0 157 while i <= idn { 158 var nl: i64 = 0; if i >= idn { nl = 1 } else { if idxb[i] == (10 as u8) { nl = 1 } } 159 if nl == 1 { 160 if i > ls { 161 var w: i64 = 0; var k: i64 = ls; while k < i { if w < 63 { lbl[w] = idxb[k]; w = w + 1 } k = k + 1 } lbl[w] = 0 as u8 162 var po: i64 = ad_bs(recp, 0, storedir); recp[po]=47 as u8; po=po+1; po=ad_bs(recp, po, "apm-" as *u8); po = ad_bs(recp, po, lbl); recp[po] = 0 as u8 163 let rn: i64 = ad_read_small(recp, rec, AD_MAGIC_4096) 164 if rn > 0 { 165 let idx: i64 = ad_find_or_add(store, np, lbl) 166 if idx >= 0 { 167 s_port[idx]=ad_kv(rec,rn,"port=" as *u8); s_conc[idx]=ad_kv(rec,rn,"conc=" as *u8); s_oks[idx]=ad_kv(rec,rn,"oks=" as *u8) 168 s_p50[idx]=ad_kv(rec,rn,"ttfb_p50=" as *u8); s_max[idx]=ad_kv(rec,rn,"ttfb_max=" as *u8) 169 s_stall[idx]=ad_kv(rec,rn,"stalls=" as *u8); s_to[idx]=ad_kv(rec,rn,"timeouts=" as *u8); s_down[idx]=ad_kv(rec,rn,"downs=" as *u8) 170 s_slow[idx]=ad_kv(rec,rn,"slowclose=" as *u8); s_epoch[idx]=ad_kv(rec,rn,"epoch=" as *u8); s_samp[idx]=1; rows=rows+1 171 } 172 } 173 } 174 ls = i + 1 175 } 176 i = i + 1 177 } 178 179 // ---- emit HTML ---- 180 let hb: *u8 = sys_mmap(AD_MAGIC_262144); var o: i64 = 0 181 o = ad_bs(hb, o, "<!doctype html><html><head><meta charset=utf-8><meta name=viewport content='width=device-width,initial-scale=1'><title>Nishi APM</title><style>" as *u8) 182 o = ad_bs(hb, o, "body{background:#0d1117;color:#e6edf3;font:14px system-ui,sans-serif;margin:0;padding:24px}h1{font-size:20px;margin:0 0 4px}.sub{color:#8b949e;font-size:12px;margin-bottom:18px}" as *u8) 183 o = ad_bs(hb, o, ".grid{display:flex;flex-wrap:wrap;gap:12px}.card{background:#161b22;border:1px solid #30363d;border-left-width:5px;border-radius:8px;padding:12px 14px;min-width:190px}" as *u8) 184 o = ad_bs(hb, o, ".nm{font-weight:600;font-size:15px}.pt{color:#8b949e;font-size:12px}.v{font-weight:700;float:right}.lat{font-size:22px;margin:6px 0 2px}.mut{color:#8b949e;font-size:12px}.badge{display:inline-block;background:#1f2937;color:#9da7b3;border-radius:4px;padding:1px 6px;font-size:11px;margin-top:6px}" as *u8) 185 o = ad_bs(hb, o, ".inc{margin-top:22px}.inc h2{font-size:15px}.ok{color:#1a7f37}</style></head><body>" as *u8) 186 o = ad_bs(hb, o, "<h1>Nishi APM &mdash; daemon health</h1><div class=sub>latency = time-to-first-byte (TTFB); slowclose = keep-alive idle-hold (informational, not a wedge). " as *u8) 187 o = ad_bn(hb, o, np[0]); o = ad_bs(hb, o, " daemons, " as *u8); o = ad_bn(hb, o, rows); o = ad_bs(hb, o, " samples.</div><div class=grid>" as *u8) 188 189 var k: i64 = 0; var nbad: i64 = 0 190 while k < np[0] { 191 let v: i64 = ad_verdict(s_conc[k], s_oks[k], s_stall[k], s_to[k], s_down[k]) 192 if v != AD_GREEN { nbad = nbad + 1 } 193 o = ad_bs(hb, o, "<div class=card style='border-left-color:" as *u8); o = ad_bs(hb, o, ad_vcolor(v)); o = ad_bs(hb, o, "'>" as *u8) 194 o = ad_bs(hb, o, "<span class=v style='color:" as *u8); o = ad_bs(hb, o, ad_vcolor(v)); o = ad_bs(hb, o, "'>" as *u8); o = ad_bs(hb, o, ad_vname(v)); o = ad_bs(hb, o, "</span>" as *u8) 195 o = ad_bs(hb, o, "<div class=nm>" as *u8); o = ad_bs(hb, o, store + k*AD_LBL_W); o = ad_bs(hb, o, "</div><div class=pt>:" as *u8); o = ad_bn(hb, o, s_port[k]); o = ad_bs(hb, o, "</div>" as *u8) 196 o = ad_bs(hb, o, "<div class=lat>" as *u8); o = ad_bn(hb, o, s_p50[k]); o = ad_bs(hb, o, "<span class=mut> ms p50</span></div>" as *u8) 197 o = ad_bs(hb, o, "<div class=mut>max " as *u8); o = ad_bn(hb, o, s_max[k]); o = ad_bs(hb, o, "ms &middot; ok " as *u8); o = ad_bn(hb, o, s_oks[k]); o = ad_bs(hb, o, "/" as *u8); o = ad_bn(hb, o, s_conc[k]); o = ad_bs(hb, o, "</div>" as *u8) 198 if s_slow[k] > 0 { o = ad_bs(hb, o, "<div class=badge>slowclose " as *u8); o = ad_bn(hb, o, s_slow[k]); o = ad_bs(hb, o, " (keep-alive)</div>" as *u8) } 199 o = ad_bs(hb, o, "</div>" as *u8) 200 k = k + 1 201 } 202 o = ad_bs(hb, o, "</div>" as *u8) 203 204 // incidents (non-GREEN) 205 o = ad_bs(hb, o, "<div class=inc><h2>Incidents</h2>" as *u8) 206 if nbad == 0 { o = ad_bs(hb, o, "<div class=ok>All daemons healthy (no stalls, timeouts, or down).</div>" as *u8) } 207 else { 208 o = ad_bs(hb, o, "<ul>" as *u8) 209 var j: i64 = 0 210 while j < np[0] { 211 let v: i64 = ad_verdict(s_conc[j], s_oks[j], s_stall[j], s_to[j], s_down[j]) 212 if v != AD_GREEN { 213 o = ad_bs(hb, o, "<li><b>" as *u8); o = ad_bs(hb, o, store + j*AD_LBL_W); o = ad_bs(hb, o, "</b> :" as *u8); o = ad_bn(hb, o, s_port[j]) 214 o = ad_bs(hb, o, " &mdash; " as *u8); o = ad_bs(hb, o, ad_vname(v)) 215 o = ad_bs(hb, o, " (p50 " as *u8); o = ad_bn(hb, o, s_p50[j]); o = ad_bs(hb, o, "ms, stalls " as *u8); o = ad_bn(hb, o, s_stall[j]); o = ad_bs(hb, o, ", timeouts " as *u8); o = ad_bn(hb, o, s_to[j]); o = ad_bs(hb, o, ", down " as *u8); o = ad_bn(hb, o, s_down[j]); o = ad_bs(hb, o, ")</li>" as *u8) 216 } 217 j = j + 1 218 } 219 o = ad_bs(hb, o, "</ul>" as *u8) 220 } 221 o = ad_bs(hb, o, "</div></body></html>" as *u8) 222 223 let fd: i64 = sys_openat_wr(html, 0x1a4) 224 if fd < 0 { ad_w("nx_apm_dashboard: cannot write " as *u8); ad_w(html); ad_w("\n" as *u8); return 1 } 225 sys_write(fd, hb, o); sys_close(fd) 226 ad_w("nx_apm_dashboard: wrote " as *u8); ad_w(html); ad_w(" (" as *u8); ad_wn(np[0]); ad_w(" daemons, " as *u8); ad_wn(rows); ad_w(" samples, " as *u8); ad_wn(nbad); ad_w(" incidents)\n" as *u8) 227 return 0 228}