traceErrors

tcmts.cli.traced.traceErrors · #syvcti7t · term · runs on the server

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

used by