traceErrors
uses tcmts.cli.main.versionsOf, which uses tcmts.share.NameTrees, which uses tcmts.store.unpacked, which uses node:zlib
The errors since since (the server's and pages', not a call as the page saw it, which is the server's again), by
the definition that threw them as named now (each version of it alike) and what they said, alike, the most often
first: an error each span it went up through says is one error, once a trace. Each with when it was first and last
seen, the newest trace it was in, the versions it was thrown by (the one it began with first, and the history entry
that made that one), what it went up through, whether it was thrown in the browser, and its traces, newest first
(the latest 20: when, the version that threw it, the span it was first said in).
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { errorKey, versionsOf } from "#codebase/tcmts/cli/main"; import { spanOf } from "#codebase/tcmts/cli/traced"; import type { Of, Version } from "#codebase/tcmts/cli/traced"; import type { DatabaseSync } from "node:sqlite"; export function traceErrors( db: DatabaseSync, cb: Codebase, since: number, limit: number, ): { name?: string; error: string; times: number; first: number; last: number; newest: string; versions: string[]; began?: Version; through: Of[]; throughKinds: string[]; inBrowser: boolean; traces: { trace: string; start: number; thrown?: string; kind: string }[]; }[] { const errors = db .prepare( `SELECT trace, start, error, kind, name, hash, json_extract(attrs, '$."tcm.thrown_in"') AS thrown FROM spans WHERE error IS NOT NULL AND kind != 'client' AND start >= ? ORDER BY start`, ) .all(since) as { trace: string; start: number; error: string; kind: string; name: string; hash: string | null; thrown: string | null; }[]; const groups = new Map< string, { name?: string; error: string; traces: Map<string, { trace: string; start: number; thrown?: string; kind: string }>; first: number; last: number; newest: string; versions: string[]; through: Map<string, { of: Of; kind: string }>; inBrowser: boolean; } >(); for (const e of errors) { // (as named now: an earlier version by the definition that replaced it) const name = e.thrown ? spanOf(cb, { hash: e.thrown, name: `#${e.thrown.slice(0, 8)}` }).label : undefined; const id = `${name ?? ""}\n${errorKey(e.error)}`; let g = groups.get(id); if (!g) groups.set( id, (g = { ...(name ? { name } : {}), error: e.error.split("\n")[0]!, traces: new Map(), first: e.start, last: e.start, newest: e.trace, versions: [], through: new Map(), inBrowser: false, }), ); if (!g.traces.has(e.trace)) g.traces.set(e.trace, { trace: e.trace, start: e.start, ...(e.thrown ? { thrown: e.thrown } : {}), kind: e.kind, }); g.last = e.start; g.newest = e.trace; if (e.thrown && !g.versions.includes(e.thrown)) g.versions.push(e.thrown); if (e.kind === "error") g.inBrowser = true; else if (e.kind !== "sql" && e.kind !== "transaction") { const of = spanOf(cb, e); g.through.set(`${of.label} ${e.kind}`, { of, kind: e.kind }); } } return [...groups.values()] .sort((a, b) => b.traces.size - a.traces.size) .slice(0, limit) .map((g) => { const began = g.versions[0]; const v = began && g.name ? versionsOf(cb, g.name).find((x) => x.hash === began) : undefined; const through = [...g.through.values()]; return { ...(g.name ? { name: g.name } : {}), error: g.error, times: g.traces.size, first: g.first, last: g.last, newest: g.newest, versions: g.versions, ...(v ? { began: v } : {}), through: through.map((t) => t.of), throughKinds: through.map((t) => t.kind), inBrowser: g.inBrowser, traces: [...g.traces.values()].reverse().slice(0, 20), }; }); }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.main.errorKey
- tcmts.cli.main.versionsOf
- tcmts.cli.traced.Of
- tcmts.cli.traced.Version
- tcmts.cli.traced.spanOf