TracesPage

tcmts.studio.TracesPage · #iqeljg4v · term · runs on the server

is an async component

What the app did on this machine (tcm dev and tcm start record it), or from production (its server's traces, fetched: fetchProduction, set up here, ProductionForm), as tcm traces says it: what runs worse than its version before and the change that did it, then what it spent its time on by definition as named now (show "errors": the errors instead, grouped by the definition that threw them), since long ago (1h, 24h, 7d).

import { shownMs, sinceMs } from "#codebase/tcmts/cli/main";
import { traceErrors, traceTotals, traceWorse } from "#codebase/tcmts/cli/traced";
import type { Of } from "#codebase/tcmts/cli/traced";
import { shortHash } from "#codebase/tcmts/core/hash";
import { ago, at, codeHref, Fold, NavTabs, servedCodebase, shareNow } from "#codebase/tcmts/share";
import {
  FetchProductionButton, production, ProductionForm, StudioFrame, studioShare, traceOfHref, tracesFrom,
} from "#codebase/tcmts/studio";

export async function TracesPage({
  since,
  show,
  from: source,
}: {
  since: string;
  show: string;
  from: string;
}) {
  studioShare();
  const s = shareNow();
  if (!s.signedIn) return <StudioFrame tab="traces" title="Sign in">{null}</StudioFrame>;
  const ago_ = ["1h", "24h", "7d"].includes(since) ? since : "24h";
  const from = Date.now() - sinceMs(ago_);
  const errorsShown = show === "errors";
  const prod = source === "production";
  const known = prod ? production(servedCodebase().cb) : undefined;
  const toTrace = (id: string) => at(`/trace/${id}${prod ? "?from=production" : ""}`);
  const t = tracesFrom(s.cb, source)((db) => {
    const rows = traceTotals(db, s.cb, from, 40, { byNameNow: true });
    return {
      rows,
      worse: traceWorse(db, s.cb, from, rows),
      errors: traceErrors(db, s.cb, from, 40),
    };
  });
  const of = (o: Of & { versions?: number }) => (
    <span className="lbl">
      {o.label}
      {o.hash && o.gone && o.earlier ? (
        <span className="muted"> ({(o.versions ?? 1) > 1 ? `${o.versions} earlier versions` : "an earlier version"})</span>
      ) : o.hash && (o.versions ?? 1) > 1 ? (
        <span className="muted"> ({o.versions} versions)</span>
      ) : o.hash && o.gone ? (
        <span className="muted"> ({shortHash(o.hash)}, no longer named)</span>
      ) : null}
    </span>
  );
  const link = (p: { since?: string; show?: string }) => {
    const q = new URLSearchParams();
    const sn = p.since ?? ago_;
    const sh = p.show ?? show;
    if (sn !== "24h") q.set("since", sn);
    if (sh) q.set("show", sh);
    if (prod) q.set("from", "production");
    return at(`/traces${q.size ? `?${q}` : ""}`);
  };
  const most = Math.max(1, ...(t?.rows ?? []).map((r) => r.total));
  const failed = (t?.errors ?? []).reduce((n, e) => n + e.times, 0);
  return (
    <StudioFrame tab="traces" title="Traces">
      <div className="pagehead">
        <h2 className="title">Traces</h2>
        <span className="muted">
          {prod ? "what the app did in production" : "what the app did on this machine"}, by definition as named now
        </span>
        <span className="right seg">
          <a className="btn small" href={at("/traces")} {...(prod ? {} : { "data-selected": "" })}>
            This machine
          </a>
          <a className="btn small" href={at("/traces?from=production")} {...(prod ? { "data-selected": "" } : {})}>
            Production
          </a>
        </span>
        <span className="seg">
          {["1h", "24h", "7d"].map((x) => (
            <a key={x} className="btn small" href={link({ since: x })} {...(x === ago_ ? { "data-selected": "" } : {})}>
              {x}
            </a>
          ))}
        </span>
      </div>
      <div className="subtabs" style={{ marginBottom: 16, ...(prod && !known ? { display: "none" } : {}) }}>
        <NavTabs
          label="Traces"
          selected={errorsShown ? "errors" : "summary"}
          tabs={[
            { id: "summary", label: "Where the time goes", href: link({ show: "" }) },
            { id: "errors", label: "Errors", href: link({ show: "errors" }), ...(failed ? { count: failed } : {}) },
          ]}
        />
      </div>
      {prod && known && (
        <div className="notice" style={{ marginBottom: 16 }}>
          {`From ${known.url}, ${known.fetched ? `fetched ${ago(known.fetched)}` : "not fetched yet"}. `}
          <FetchProductionButton label={known.fetched ? "Fetch again" : "Fetch"} />
        </div>
      )}
      {prod && !known ? (
        <div className="box">
          <div className="boxhead">
            <b>Production</b>
          </div>
          <div className="boxbody">
            <p className="muted" style={{ marginTop: 0 }}>
              The server <code>tcm start</code> runs the app on hands its traces to whoever holds its traces token: set{" "}
              <code>TCM_TRACES_TOKEN</code> there to a secret of your own, and give it here with where it runs. Its
              traces are fetched into the studio, which reads them as it reads this machine's.
            </p>
            <ProductionForm url="" />
          </div>
        </div>
      ) : !t ? (
        <div className="box">
          <div className="empty">
            {prod ? (
              "No traces from production yet."
            ) : (
              <>
                No traces yet: <code>tcm dev</code> and <code>tcm start</code> record them as the app is used.
              </>
            )}
          </div>
        </div>
      ) : (
        <>
          {t.worse.map((w) => (
            <div className="notice" key={`${w.name} ${w.kind}`} style={{ borderColor: "var(--warn)", background: "var(--warnbg)" }}>
              <b className="warn">
                <a href={traceOfHref(w.name, w.kind, source)} style={{ color: "inherit" }}>{w.name}</a> ({w.kind}) runs worse than its version before: {w.said}.
              </b>{" "}
              {w.changed.length ? <>What changed: {w.changed.join(", ")}. </> : null}
              {w.since && <a href={at(`/history/${w.since.causal}`)}>“{w.since.title}”</a>}
            </div>
          ))}
          {errorsShown ? (
            t.errors.length ? (
              <>
                <p className="muted" style={{ margin: "0 0 12px" }}>
                  By the definition that threw them, as named now: alike ones together, once a trace.
                </p>
                {t.errors.map((e) => (
                  <div className="box" key={`${e.name ?? ""} ${e.error}`}>
                    <div className="boxhead">
                      {e.name ? (
                        <a className="mono" href={codeHref(e.name)}>{e.name}</a>
                      ) : (
                        <b>Not thrown in the codebase's code</b>
                      )}
                      {e.inBrowser && <span className="muted">in the browser</span>}
                      <span className="right muted">
                        {e.times === 1 ? "once" : `${e.times} times`}, last {ago(e.last)}
                      </span>
                    </div>
                    <div className="boxbody errbody">
                      <pre className="code errmsg">{e.error}</pre>
                      {(e.began || e.through.length > 0) && (
                        <div className="muted errwhere">
                          {e.began && (
                            <>
                              Began with {shortHash(e.versions[0]!)}, in{" "}
                              <a href={at(`/history/${e.began.causal}`)}>{e.began.title}</a>
                              {e.through.length > 0 ? ". " : ""}
                            </>
                          )}
                          {e.through.length > 0 && (
                            <>
                              Went up through{" "}
                              {e.through
                                .slice(0, 4)
                                .map((o, i) => (o.label === e.throughKinds[i] ? o.label : `${o.label} (${e.throughKinds[i]})`))
                                .join(", ")}
                              .
                            </>
                          )}
                        </div>
                      )}
                    </div>
                    {(() => {
                    const kinds = new Set(e.traces.map((y) => y.kind));
                    const row = (x: (typeof e.traces)[number]) => {
                      return (
                        <a className="lrow tracerow" key={x.trace} href={toTrace(x.trace)}>
                          <div>
                            <span className="mono">trace {x.trace.slice(0, 8)}</span>
                            {kinds.size > 1 && <span className="muted"> · in a {x.kind === "error" ? "page" : x.kind}</span>}
                            {e.versions.length > 1 && x.thrown && (
                              <span className="muted"> · thrown by {shortHash(x.thrown)}</span>
                            )}
                          </div>
                          <span className="muted">{ago(x.start)}</span>
                        </a>
                      );
                    };
                    const rest = e.times - 5;
                    return (
                      <>
                        {e.traces.slice(0, 5).map(row)}
                        {rest > 0 && (
                          <div className="tracerest">
                            <Fold head={<span className="muted">{rest} earlier</span>}>
                              {e.traces.slice(5).map(row)}
                              {e.times > e.traces.length && (
                                <div className="lrow muted" style={{ fontSize: 12.5 }}>
                                  <div>and {e.times - e.traces.length} before those, not listed</div>
                                </div>
                              )}
                            </Fold>
                          </div>
                        )}
                      </>
                    );
                    })()}
                  </div>
                ))}
              </>
            ) : (
              <div className="box">
                <div className="empty">No errors in the last {ago_}.</div>
              </div>
            )
          ) : (
            <div className="box">
              {t.rows.length ? (
                <div className="trows">
                  <div className="tr th">
                    <span>definition</span>
                    <span>kind</span>
                    <span className="num">calls</span>
                    <span className="num">errors</span>
                    <span className="num">p50</span>
                    <span className="num">p95</span>
                    <span>time, all told</span>
                  </div>
                    {t.rows.map((r) => (
                      <a className="tr" key={`${r.hash ?? r.name} ${r.kind}`} href={traceOfHref(r.label, r.kind, source)}>
                        {of(r)}
                        <span className="muted">{r.kind === "client" ? "from the browser" : r.kind}</span>
                        <span className="num">{r.n}</span>
                        <span className={`num${r.errors ? " bad" : ""}`}>{r.errors}</span>
                        <span className="num">{shownMs(r.p50)}</span>
                        <span className="num">{shownMs(r.p95)}</span>
                        <span title={shownMs(r.total)}>
                          <span className="sr-only">{shownMs(r.total)}</span>
                          <span className="meter">
                            <span
                              className="fill"
                              style={{
                                width: `${Math.max(2, (r.total / most) * 100)}%`,
                                background: ["client", "view", "navigate", "error"].includes(r.kind)
                                  ? "var(--brw)"
                                  : ["sql", "transaction"].includes(r.kind)
                                    ? "var(--any)"
                                    : "var(--srv)",
                              }}
                            />
                          </span>
                        </span>
                      </a>
                    ))}
                </div>
              ) : (
                <div className="empty">Nothing recorded in the last {ago_}.</div>
              )}
            </div>
          )}
        </>
      )}
    </StudioFrame>
  );
}

uses

used by