{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"c-trace-10","type":"registry:block","title":"Distributed trace spans","description":"Request spans nested under their parent with their durations, each opening to its attributes, the slowest one marked.","dependencies":[],"registryDependencies":["@reui/badge","trace"],"files":[{"path":"c-trace-10.tsx","type":"registry:block","content":"import { Badge } from \"@/components/reui/badge\"\n\nimport {\n  Trace,\n  TraceCollapsible,\n  TraceCollapsibleContent,\n  TraceCollapsibleTrigger,\n  TraceDescription,\n  TraceIcon,\n  TraceLine,\n  TraceTitle,\n} from \"@/components/ui/trace\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\ntype Span = {\n  id: string\n  name: string\n  // Picks the row's glyph only; the OpenTelemetry span kind is not shown.\n  icon: \"server\" | \"database\" | \"http\" | \"code\"\n  ms: number\n  error?: boolean\n  attributes?: Record<string, string>\n  children?: Span[]\n}\n\n// Attribute keys follow the current OpenTelemetry semantic conventions.\nconst ROOT: Span = {\n  id: \"root\",\n  name: \"POST /api/checkout\",\n  icon: \"server\",\n  ms: 842,\n  children: [\n    {\n      id: \"cart\",\n      name: \"SELECT cart_items\",\n      icon: \"database\",\n      ms: 96,\n      attributes: {\n        \"db.system.name\": \"postgresql\",\n        \"db.response.returned_rows\": \"7\",\n      },\n    },\n    {\n      id: \"reserve-1\",\n      name: \"POST /inventory/reserve\",\n      icon: \"http\",\n      ms: 34,\n      error: true,\n      attributes: {\n        \"http.response.status_code\": \"409\",\n        \"error.type\": \"409\",\n      },\n    },\n    {\n      id: \"reserve-2\",\n      name: \"POST /inventory/reserve\",\n      icon: \"http\",\n      ms: 30,\n      attributes: {\n        \"http.response.status_code\": \"200\",\n        \"http.request.resend_count\": \"1\",\n      },\n    },\n    {\n      id: \"payment\",\n      name: \"payments.createIntent\",\n      icon: \"code\",\n      ms: 610,\n      children: [\n        {\n          id: \"payment-http\",\n          name: \"POST /v1/payment_intents\",\n          icon: \"http\",\n          ms: 598,\n          attributes: {\n            \"server.address\": \"api.payments.example\",\n            \"http.response.status_code\": \"200\",\n          },\n        },\n      ],\n    },\n  ],\n}\n\n// Self time is a span's duration minus its children's: the span that spent\n// the time itself, not a parent that only waited on it.\nfunction slowest(span: Span): { span: Span; self: number } {\n  const own = {\n    span,\n    self: span.ms - (span.children ?? []).reduce((sum, c) => sum + c.ms, 0),\n  }\n  return (span.children ?? [])\n    .map(slowest)\n    .reduce((best, next) => (next.self > best.self ? next : best), own)\n}\n\nconst HOT = slowest(ROOT).span\n\n// Child spans nest inside their parent's row, and the path to the slowest span\n// starts open.\nexport function Pattern() {\n  return (\n    <div className=\"w-full max-w-md text-sm\">\n      <Trace>\n        <SpanRow span={ROOT} />\n      </Trace>\n    </div>\n  )\n}\n\nfunction contains(span: Span, id: string): boolean {\n  return span.id === id || (span.children ?? []).some((c) => contains(c, id))\n}\n\nfunction SpanRow({ span }: { span: Span }) {\n  const share = Math.round((span.ms / ROOT.ms) * 100)\n\n  return (\n    <TraceCollapsible\n      defaultOpen={Boolean(span.children) && contains(span, HOT.id)}\n    >\n      <TraceCollapsibleTrigger>\n        <TraceLine status={span.error ? \"error\" : undefined}>\n          <TraceIcon>\n            <SpanGlyph icon={span.icon} />\n          </TraceIcon>\n          <TraceTitle>\n            <code className=\"text-xs\">{span.name}</code>\n            {span.error ? \" failed\" : null}\n          </TraceTitle>\n          <TraceDescription>{span.ms} ms</TraceDescription>\n          {span.id === HOT.id ? (\n            <Badge variant=\"warning-light\">\n              {share}%<span className=\"sr-only\"> of the request time</span>\n            </Badge>\n          ) : null}\n        </TraceLine>\n      </TraceCollapsibleTrigger>\n      <TraceCollapsibleContent keepMounted>\n        {span.children ? (\n          <Trace>\n            {span.children.map((child) => (\n              <SpanRow key={child.id} span={child} />\n            ))}\n          </Trace>\n        ) : (\n          <dl className=\"flex flex-col gap-1 font-mono text-xs\">\n            {Object.entries(span.attributes ?? {}).map(([key, value]) => (\n              <div key={key} className=\"flex gap-3\">\n                <dt className=\"min-w-0 truncate\">{key}</dt>\n                <dd className=\"text-foreground ml-auto shrink-0\">{value}</dd>\n              </div>\n            ))}\n          </dl>\n        )}\n      </TraceCollapsibleContent>\n    </TraceCollapsible>\n  )\n}\n\nfunction SpanGlyph({ icon }: { icon: Span[\"icon\"] }) {\n  switch (icon) {\n    case \"server\":\n      return (\n        <IconPlaceholder\n          lucide=\"ServerIcon\"\n          tabler=\"IconServer\"\n          hugeicons=\"ServerStack01Icon\"\n          phosphor=\"HardDrivesIcon\"\n          remixicon=\"RiServerLine\"\n        />\n      )\n    case \"database\":\n      return (\n        <IconPlaceholder\n          lucide=\"DatabaseIcon\"\n          tabler=\"IconDatabase\"\n          hugeicons=\"Database02Icon\"\n          phosphor=\"DatabaseIcon\"\n          remixicon=\"RiDatabase2Line\"\n        />\n      )\n    case \"http\":\n      return (\n        <IconPlaceholder\n          lucide=\"GlobeIcon\"\n          tabler=\"IconWorld\"\n          hugeicons=\"Globe02Icon\"\n          phosphor=\"GlobeIcon\"\n          remixicon=\"RiGlobalLine\"\n        />\n      )\n    case \"code\":\n      return (\n        <IconPlaceholder\n          lucide=\"CodeIcon\"\n          tabler=\"IconCode\"\n          hugeicons=\"SourceCodeIcon\"\n          phosphor=\"CodeIcon\"\n          remixicon=\"RiCodeSSlashLine\"\n        />\n      )\n  }\n}","target":"components/examples/c-trace-10.tsx"}],"meta":{"order":10,"gridSize":2,"label":"Request Spans"}}