autotel-terminal 17.0.3 → 17.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +561 -254
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +561 -254
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +534 -249
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +534 -249
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +284 -217
- package/src/lib/dashboard-keymap.test.ts +18 -0
- package/src/lib/dashboard-keymap.ts +14 -1
- package/src/lib/error-model.test.ts +1 -0
- package/src/lib/export-model.test.ts +1 -0
- package/src/lib/format.test.ts +31 -1
- package/src/lib/format.ts +26 -3
- package/src/lib/service-colors.test.ts +22 -0
- package/src/lib/service-colors.ts +32 -0
- package/src/lib/topology-render.test.ts +319 -0
- package/src/lib/topology-render.ts +96 -0
- package/src/lib/trace-model.ts +9 -0
- package/src/otlp-http-json.test.ts +131 -0
- package/src/otlp-http-json.ts +52 -7
- package/src/span-stream.ts +57 -1
package/dist/cli.cjs
CHANGED
|
@@ -111,6 +111,25 @@ function createTerminalSpanStream(processor) {
|
|
|
111
111
|
const startTime = timeToMs(span.startTime);
|
|
112
112
|
const endTime = timeToMs(span.endTime);
|
|
113
113
|
const durationMs = endTime - startTime;
|
|
114
|
+
const resourceAttrs = span.resource?.attributes ?? {};
|
|
115
|
+
const spanAttrs = span.attributes;
|
|
116
|
+
const mergedAttrs = {};
|
|
117
|
+
for (const [k, v] of Object.entries(resourceAttrs)) {
|
|
118
|
+
mergedAttrs[k] = v;
|
|
119
|
+
}
|
|
120
|
+
for (const [k, v] of Object.entries(spanAttrs)) {
|
|
121
|
+
mergedAttrs[k] = v;
|
|
122
|
+
}
|
|
123
|
+
const spanEvents = span.events?.length ? span.events.map((e) => ({
|
|
124
|
+
name: e.name,
|
|
125
|
+
timeMs: timeToMs(e.time),
|
|
126
|
+
attributes: e.attributes
|
|
127
|
+
})) : void 0;
|
|
128
|
+
const spanLinks = span.links?.length ? span.links.map((l) => ({
|
|
129
|
+
traceId: l.context.traceId,
|
|
130
|
+
spanId: l.context.spanId,
|
|
131
|
+
attributes: l.attributes
|
|
132
|
+
})) : void 0;
|
|
114
133
|
const event = {
|
|
115
134
|
name: span.name,
|
|
116
135
|
spanId: spanContext.spanId,
|
|
@@ -121,7 +140,9 @@ function createTerminalSpanStream(processor) {
|
|
|
121
140
|
durationMs,
|
|
122
141
|
status: mapStatus(span.status.code),
|
|
123
142
|
kind: mapKind(span.kind),
|
|
124
|
-
attributes:
|
|
143
|
+
attributes: mergedAttrs,
|
|
144
|
+
...spanEvents ? { events: spanEvents } : {},
|
|
145
|
+
...spanLinks ? { links: spanLinks } : {}
|
|
125
146
|
};
|
|
126
147
|
callback(event);
|
|
127
148
|
});
|
|
@@ -130,6 +151,10 @@ function createTerminalSpanStream(processor) {
|
|
|
130
151
|
}
|
|
131
152
|
|
|
132
153
|
// src/lib/trace-model.ts
|
|
154
|
+
function spanServiceName(span) {
|
|
155
|
+
const svc = span.attributes?.["service.name"];
|
|
156
|
+
return typeof svc === "string" ? svc : "unknown";
|
|
157
|
+
}
|
|
133
158
|
function buildTraceMap(spans, maxTraces = 50) {
|
|
134
159
|
const byTrace = /* @__PURE__ */ new Map();
|
|
135
160
|
for (const s of spans) {
|
|
@@ -153,6 +178,7 @@ function buildTraceSummaries(traceMap) {
|
|
|
153
178
|
const durationMs = root ? root.durationMs : 0;
|
|
154
179
|
const hasError = traceSpans.some((s) => s.status === "ERROR");
|
|
155
180
|
const lastEndTime = Math.max(...traceSpans.map((s) => s.endTime));
|
|
181
|
+
const services = [...new Set(traceSpans.map((s) => spanServiceName(s)))];
|
|
156
182
|
summaries.push({
|
|
157
183
|
traceId,
|
|
158
184
|
rootName: root?.name ?? "unknown",
|
|
@@ -160,7 +186,8 @@ function buildTraceSummaries(traceMap) {
|
|
|
160
186
|
hasError,
|
|
161
187
|
spanCount: traceSpans.length,
|
|
162
188
|
lastEndTime,
|
|
163
|
-
spans: traceSpans
|
|
189
|
+
spans: traceSpans,
|
|
190
|
+
services
|
|
164
191
|
});
|
|
165
192
|
}
|
|
166
193
|
return summaries;
|
|
@@ -276,6 +303,45 @@ function buildWaterfallBar(spanStart, spanDuration, traceStart, traceDuration, w
|
|
|
276
303
|
const trailing = Math.max(0, width - clampedStart - clampedLen);
|
|
277
304
|
return " ".repeat(clampedStart) + "\u2588".repeat(clampedLen) + " ".repeat(trailing);
|
|
278
305
|
}
|
|
306
|
+
function buildTimeRuler(totalMs, width) {
|
|
307
|
+
const left = "0ms";
|
|
308
|
+
const right = formatDurationMs(totalMs);
|
|
309
|
+
const mid = formatDurationMs(totalMs / 2);
|
|
310
|
+
const midPos = Math.floor(width / 2) - Math.floor(mid.length / 2);
|
|
311
|
+
const rightPos = width - right.length;
|
|
312
|
+
const chars = Array.from({ length: width }).fill(" ");
|
|
313
|
+
for (let i = 0; i < left.length && i < width; i++) chars[i] = left[i];
|
|
314
|
+
if (midPos > left.length + 1 && midPos + mid.length < rightPos - 1) {
|
|
315
|
+
for (let i = 0; i < mid.length; i++) chars[midPos + i] = mid[i];
|
|
316
|
+
}
|
|
317
|
+
for (let i = 0; i < right.length; i++) chars[rightPos + i] = right[i];
|
|
318
|
+
return chars.join("");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/lib/service-colors.ts
|
|
322
|
+
var SERVICE_COLORS = [
|
|
323
|
+
"cyan",
|
|
324
|
+
"magenta",
|
|
325
|
+
"blue",
|
|
326
|
+
"yellow",
|
|
327
|
+
"green",
|
|
328
|
+
"redBright",
|
|
329
|
+
"cyanBright",
|
|
330
|
+
"magentaBright",
|
|
331
|
+
"blueBright",
|
|
332
|
+
"yellowBright",
|
|
333
|
+
"greenBright"
|
|
334
|
+
];
|
|
335
|
+
function hashString(s) {
|
|
336
|
+
let hash = 0;
|
|
337
|
+
for (let i = 0; i < s.length; i++) {
|
|
338
|
+
hash = Math.trunc((hash << 5) - hash + s.codePointAt(i));
|
|
339
|
+
}
|
|
340
|
+
return Math.abs(hash);
|
|
341
|
+
}
|
|
342
|
+
function getServiceColor(serviceName) {
|
|
343
|
+
return SERVICE_COLORS[hashString(serviceName) % SERVICE_COLORS.length];
|
|
344
|
+
}
|
|
279
345
|
|
|
280
346
|
// src/lib/log-model.ts
|
|
281
347
|
function filterLogsBySearch(logs, searchQuery, minLevel) {
|
|
@@ -516,6 +582,123 @@ function buildErrorSummaries(traceSummaries) {
|
|
|
516
582
|
return out;
|
|
517
583
|
}
|
|
518
584
|
|
|
585
|
+
// src/lib/topology-model.ts
|
|
586
|
+
function getServiceName2(span) {
|
|
587
|
+
const attrs = span.attributes ?? {};
|
|
588
|
+
const serviceName = attrs["service.name"] ?? attrs["resource.service.name"];
|
|
589
|
+
return serviceName || "unknown";
|
|
590
|
+
}
|
|
591
|
+
function getPeerService(span) {
|
|
592
|
+
const attrs = span.attributes ?? {};
|
|
593
|
+
const peerService = attrs["peer.service"] ?? attrs["db.system"] ?? attrs["messaging.system"] ?? attrs["http.host"];
|
|
594
|
+
return peerService ?? null;
|
|
595
|
+
}
|
|
596
|
+
function buildServiceGraph(spans) {
|
|
597
|
+
const byService = /* @__PURE__ */ new Map();
|
|
598
|
+
for (const span of spans) {
|
|
599
|
+
const svc = getServiceName2(span);
|
|
600
|
+
const entry = byService.get(svc) ?? {
|
|
601
|
+
durations: [],
|
|
602
|
+
spanCount: 0,
|
|
603
|
+
errorCount: 0
|
|
604
|
+
};
|
|
605
|
+
entry.spanCount += 1;
|
|
606
|
+
entry.durations.push(span.durationMs);
|
|
607
|
+
if (span.status === "ERROR") entry.errorCount += 1;
|
|
608
|
+
byService.set(svc, entry);
|
|
609
|
+
}
|
|
610
|
+
const services = [];
|
|
611
|
+
for (const [serviceName, { durations, spanCount, errorCount }] of byService) {
|
|
612
|
+
if (spanCount === 0) continue;
|
|
613
|
+
const avgDurationMs = durations.reduce((acc, d) => acc + d, 0) / durations.length;
|
|
614
|
+
const sorted = durations.toSorted((a, b) => a - b);
|
|
615
|
+
const p95Index = Math.floor(sorted.length * 0.95);
|
|
616
|
+
const p95DurationMs = sorted[p95Index] ?? sorted.at(-1) ?? 0;
|
|
617
|
+
services.push({
|
|
618
|
+
serviceName,
|
|
619
|
+
spanCount,
|
|
620
|
+
errorCount,
|
|
621
|
+
avgDurationMs,
|
|
622
|
+
p95DurationMs
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
const byEdge = /* @__PURE__ */ new Map();
|
|
626
|
+
for (const span of spans) {
|
|
627
|
+
const to = getPeerService(span);
|
|
628
|
+
if (!to) continue;
|
|
629
|
+
const from = getServiceName2(span);
|
|
630
|
+
const key = `${from}\u2192${to}`;
|
|
631
|
+
const entry = byEdge.get(key) ?? { spanCount: 0, errorCount: 0 };
|
|
632
|
+
entry.spanCount += 1;
|
|
633
|
+
if (span.status === "ERROR") entry.errorCount += 1;
|
|
634
|
+
byEdge.set(key, entry);
|
|
635
|
+
}
|
|
636
|
+
const edges = [];
|
|
637
|
+
for (const [key, { spanCount, errorCount }] of byEdge) {
|
|
638
|
+
const [fromService, toService] = key.split("\u2192");
|
|
639
|
+
edges.push({ fromService, toService, spanCount, errorCount });
|
|
640
|
+
}
|
|
641
|
+
services.sort((a, b) => b.spanCount - a.spanCount);
|
|
642
|
+
edges.sort((a, b) => b.spanCount - a.spanCount);
|
|
643
|
+
return { services, edges };
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// src/lib/topology-render.ts
|
|
647
|
+
function renderTopologyAscii(graph) {
|
|
648
|
+
if (graph.services.length === 0) {
|
|
649
|
+
return [" No services detected yet."];
|
|
650
|
+
}
|
|
651
|
+
const targetServices = new Set(graph.edges.map((e) => e.toService));
|
|
652
|
+
const roots = graph.services.filter(
|
|
653
|
+
(s) => !targetServices.has(s.serviceName)
|
|
654
|
+
);
|
|
655
|
+
const rootList = roots.length > 0 ? roots : [...graph.services];
|
|
656
|
+
const lines = [];
|
|
657
|
+
for (const root of rootList) {
|
|
658
|
+
renderNode(graph, root.serviceName, "", true, lines, /* @__PURE__ */ new Set());
|
|
659
|
+
}
|
|
660
|
+
return lines;
|
|
661
|
+
}
|
|
662
|
+
function renderNode(graph, serviceName, prefix, isRoot, lines, ancestors) {
|
|
663
|
+
const svc = graph.services.find((s) => s.serviceName === serviceName);
|
|
664
|
+
const label = svc ? formatServiceLine(svc) : `[${serviceName}]`;
|
|
665
|
+
if (isRoot) {
|
|
666
|
+
lines.push(label);
|
|
667
|
+
}
|
|
668
|
+
if (ancestors.has(serviceName)) return;
|
|
669
|
+
const pathAncestors = new Set(ancestors);
|
|
670
|
+
pathAncestors.add(serviceName);
|
|
671
|
+
const outgoing = graph.edges.filter((e) => e.fromService === serviceName);
|
|
672
|
+
for (let i = 0; i < outgoing.length; i++) {
|
|
673
|
+
const edge = outgoing[i];
|
|
674
|
+
const isLast = i === outgoing.length - 1;
|
|
675
|
+
const connector = isLast ? "\u2514" : "\u251C";
|
|
676
|
+
const childPrefix = isLast ? " " : "\u2502 ";
|
|
677
|
+
const downstream = graph.services.find(
|
|
678
|
+
(s) => s.serviceName === edge.toService
|
|
679
|
+
);
|
|
680
|
+
const edgeLabel = formatEdgeLabel(edge);
|
|
681
|
+
const downstreamLabel = downstream ? formatServiceLine(downstream) : `[${edge.toService}]`;
|
|
682
|
+
lines.push(`${prefix} ${connector}\u2500\u2500${edgeLabel}\u2500\u2500\u2192 ${downstreamLabel}`);
|
|
683
|
+
renderNode(
|
|
684
|
+
graph,
|
|
685
|
+
edge.toService,
|
|
686
|
+
prefix + " " + childPrefix,
|
|
687
|
+
false,
|
|
688
|
+
lines,
|
|
689
|
+
pathAncestors
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
function formatServiceLine(svc) {
|
|
694
|
+
const errPart = svc.errorCount > 0 ? ` \xB7 ${svc.errorCount} err` : "";
|
|
695
|
+
return `[${svc.serviceName}] ${svc.spanCount} spans${errPart} \xB7 p95 ${formatDurationMs(svc.p95DurationMs)}`;
|
|
696
|
+
}
|
|
697
|
+
function formatEdgeLabel(edge) {
|
|
698
|
+
const errPart = edge.errorCount > 0 ? `, ${edge.errorCount} err` : "";
|
|
699
|
+
return `(${edge.spanCount}${errPart})`;
|
|
700
|
+
}
|
|
701
|
+
|
|
519
702
|
// src/lib/export-model.ts
|
|
520
703
|
function exportTraceToJson(trace, logs) {
|
|
521
704
|
const exported = {
|
|
@@ -1033,6 +1216,8 @@ function Dashboard({
|
|
|
1033
1216
|
[spansForSelectedService]
|
|
1034
1217
|
);
|
|
1035
1218
|
const selectedTraceSummary = drilldownTraceId == null ? filteredSummaries[selected] ?? null : filteredSummaries.find((t2) => t2.traceId === drilldownTraceId) ?? null;
|
|
1219
|
+
const serviceGraph = react.useMemo(() => buildServiceGraph(spans), [spans]);
|
|
1220
|
+
const topologyLines = react.useMemo(() => renderTopologyAscii(serviceGraph), [serviceGraph]);
|
|
1036
1221
|
const errorSummaries = react.useMemo(
|
|
1037
1222
|
() => buildErrorSummaries(traceSummaries),
|
|
1038
1223
|
[traceSummaries]
|
|
@@ -1439,6 +1624,13 @@ Currently viewing trace ${drilldownTraceId}. This trace has ${drilldownSpans.len
|
|
|
1439
1624
|
setDrilldownSelectedIndex(0);
|
|
1440
1625
|
setDrilldownScrollOffset(0);
|
|
1441
1626
|
}
|
|
1627
|
+
if (input === "G") {
|
|
1628
|
+
setViewMode((m) => m === "topology" ? "trace" : "topology");
|
|
1629
|
+
setSelected(0);
|
|
1630
|
+
setDrilldownTraceId(null);
|
|
1631
|
+
setDrilldownSelectedIndex(0);
|
|
1632
|
+
setDrilldownScrollOffset(0);
|
|
1633
|
+
}
|
|
1442
1634
|
if (input === "c") {
|
|
1443
1635
|
setSpans([]);
|
|
1444
1636
|
setLogs([]);
|
|
@@ -1580,20 +1772,31 @@ ${json}
|
|
|
1580
1772
|
{ isActive: isRawModeSupported }
|
|
1581
1773
|
);
|
|
1582
1774
|
const headerRight = recording ? "[Recording]" : paused ? "[Paused]" : "[Live]";
|
|
1583
|
-
const headerModeLabel = viewMode === "trace" ? "traces" : viewMode === "span" ? "spans" : viewMode === "log" ? "logs" : viewMode === "service-summary" ? "services" : "errors";
|
|
1775
|
+
const headerModeLabel = viewMode === "trace" ? "traces" : viewMode === "span" ? "spans" : viewMode === "log" ? "logs" : viewMode === "service-summary" ? "services" : viewMode === "topology" ? "topology" : "errors";
|
|
1584
1776
|
const showNewError = newErrorCount > 0;
|
|
1585
1777
|
function renderTreeRow(node, index) {
|
|
1586
1778
|
const isSel = drilldownTraceId != null && index === drilldownSelectedIndex;
|
|
1587
1779
|
const prefix = node.depth === 0 ? "" : " ".repeat(node.depth) + (node.children.length > 0 ? "\u251C\u2500\u2500 " : "\u2514\u2500\u2500 ");
|
|
1780
|
+
const svcName = spanServiceName(node.span);
|
|
1781
|
+
const svcColor = getServiceColor(svcName);
|
|
1588
1782
|
const statusColor = node.span.status === "ERROR" ? "red" : node.span.durationMs > 500 ? "yellow" : "green";
|
|
1589
1783
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1590
1784
|
ink.Box,
|
|
1591
1785
|
{
|
|
1592
1786
|
flexDirection: "row",
|
|
1593
1787
|
children: [
|
|
1594
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: isSel ? "
|
|
1788
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { backgroundColor: isSel ? "blue" : void 0, color: isSel ? "white" : void 0, children: isSel ? "\u25B8 " : " " }),
|
|
1789
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: node.span.status === "ERROR" ? "red" : void 0, children: node.span.status === "ERROR" ? "\u2717" : " " }),
|
|
1595
1790
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: prefix }),
|
|
1596
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: colors ? statusColor : void 0, children: truncate(node.span.name,
|
|
1791
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: colors ? statusColor : void 0, children: truncate(node.span.name, 23) }),
|
|
1792
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: svcColor, children: [
|
|
1793
|
+
" ",
|
|
1794
|
+
truncate(svcName, 10)
|
|
1795
|
+
] }),
|
|
1796
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
1797
|
+
" ",
|
|
1798
|
+
node.span.kind ?? ""
|
|
1799
|
+
] }),
|
|
1597
1800
|
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
1598
1801
|
" ",
|
|
1599
1802
|
formatDurationMs(node.span.durationMs)
|
|
@@ -1651,18 +1854,323 @@ ${json}
|
|
|
1651
1854
|
] })
|
|
1652
1855
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", justifyContent: "space-between", children: [
|
|
1653
1856
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: drilldownTraceId == null ? "\u2191/\u2193 select \u2022 Enter open \u2022 Tab cycle tabs \u2022 Esc back \u2022 T trace \u2022 L logs \u2022 a AI \u2022 ? help" : "\u2191/\u2193 select \u2022 Tab cycle tabs \u2022 Esc back \u2022 T trace \u2022 L logs \u2022 a AI \u2022 ? help" }),
|
|
1654
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: viewMode === "trace" ? `traces ${filteredSummaries.length}/${traceSummaries.length}` : viewMode === "span" ? `spans ${filteredSpans.length}/${spans.length}` : viewMode === "service-summary" ? `services ${serviceStats.length}` : viewMode === "errors" ? `errors ${filteredErrorSummaries.length}/${errorSummaries.length}` : `logs ${filteredLogs.length}/${logs.length}` })
|
|
1857
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: viewMode === "trace" ? `traces ${filteredSummaries.length}/${traceSummaries.length}` : viewMode === "span" ? `spans ${filteredSpans.length}/${spans.length}` : viewMode === "service-summary" ? `services ${serviceStats.length}` : viewMode === "errors" ? `errors ${filteredErrorSummaries.length}/${errorSummaries.length}` : viewMode === "topology" ? `services ${serviceGraph.services.length} \xB7 edges ${serviceGraph.edges.length}` : `logs ${filteredLogs.length}/${logs.length}` })
|
|
1655
1858
|
] }),
|
|
1656
|
-
showHelp && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Views: t/l/v/E \u2022 Search: / \u2022 Filters: e/S/R/H/f/x \u2022 Capture: p/r/J \u2022 AI: a \u2022 Clear: c" })
|
|
1859
|
+
showHelp && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Views: t/l/v/E/G \u2022 Search: / \u2022 Filters: e/S/R/H/f/x \u2022 Capture: p/r/J \u2022 AI: a \u2022 Clear: c" })
|
|
1657
1860
|
] }),
|
|
1658
1861
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginBottom: 0, children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: spanFilters.serviceName || spanFilters.route || spanFilters.statusGroup !== "all" || spanFilters.traceId ? `filters:${spanFilters.serviceName ? ` service=${spanFilters.serviceName}` : ""}${spanFilters.route ? ` route=${spanFilters.route}` : ""}${spanFilters.statusGroup && spanFilters.statusGroup !== "all" ? ` status=${spanFilters.statusGroup}` : ""}${spanFilters.traceId ? ` trace=${spanFilters.traceId.slice(0, 8)}\u2026` : ""}` : "filters: none" }) }),
|
|
1659
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "
|
|
1862
|
+
viewMode === "topology" && /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
|
|
1863
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Service Topology" }),
|
|
1864
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Press G to toggle \xB7 Shows service dependencies from span data" }),
|
|
1865
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { flexDirection: "column", marginTop: 1, children: topologyLines.map((line, i) => {
|
|
1866
|
+
const hasErr = line.includes(" err");
|
|
1867
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: hasErr ? "red" : void 0, children: line }, `topo-${i}`);
|
|
1868
|
+
}) })
|
|
1869
|
+
] }),
|
|
1870
|
+
viewMode !== "topology" && (drilldownTraceId != null ? /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, paddingY: 0, children: [
|
|
1871
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 0, flexDirection: "column", children: [
|
|
1872
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { bold: true, children: [
|
|
1873
|
+
"Trace ",
|
|
1874
|
+
drilldownTraceId.slice(0, 16),
|
|
1875
|
+
"\u2026"
|
|
1876
|
+
] }),
|
|
1877
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", gap: 1, children: [
|
|
1878
|
+
drilldownSummary?.services?.map((svc) => /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: getServiceColor(svc), children: svc }, svc)),
|
|
1879
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "\xB7" }),
|
|
1880
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: drilldownSummary?.rootName ?? "unknown" }),
|
|
1881
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "\xB7" }),
|
|
1882
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
1883
|
+
drilldownSpans.length,
|
|
1884
|
+
" spans"
|
|
1885
|
+
] }),
|
|
1886
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "\xB7" }),
|
|
1887
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
1888
|
+
drilldownSummary?.services?.length ?? 0,
|
|
1889
|
+
" services"
|
|
1890
|
+
] }),
|
|
1891
|
+
drilldownSummary?.hasError && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: "red", children: [
|
|
1892
|
+
" ",
|
|
1893
|
+
"ERROR"
|
|
1894
|
+
] })
|
|
1895
|
+
] }),
|
|
1896
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", gap: 2, children: [
|
|
1897
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Duration: " }),
|
|
1898
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "green", children: formatDurationMs(drilldownSummary?.durationMs ?? 0) })
|
|
1899
|
+
] })
|
|
1900
|
+
] }),
|
|
1901
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 0, flexDirection: "row", gap: 2, children: [
|
|
1902
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1903
|
+
ink.Text,
|
|
1904
|
+
{
|
|
1905
|
+
color: drilldownTab === "timeline" ? "blue" : void 0,
|
|
1906
|
+
dimColor: drilldownTab !== "timeline",
|
|
1907
|
+
bold: drilldownTab === "timeline",
|
|
1908
|
+
children: "Timeline"
|
|
1909
|
+
}
|
|
1910
|
+
),
|
|
1911
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1912
|
+
ink.Text,
|
|
1913
|
+
{
|
|
1914
|
+
color: drilldownTab === "spans" ? "blue" : void 0,
|
|
1915
|
+
dimColor: drilldownTab !== "spans",
|
|
1916
|
+
bold: drilldownTab === "spans",
|
|
1917
|
+
children: [
|
|
1918
|
+
"Spans (",
|
|
1919
|
+
drilldownSpans.length,
|
|
1920
|
+
")"
|
|
1921
|
+
]
|
|
1922
|
+
}
|
|
1923
|
+
),
|
|
1924
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1925
|
+
ink.Text,
|
|
1926
|
+
{
|
|
1927
|
+
color: drilldownTab === "logs" ? "blue" : void 0,
|
|
1928
|
+
dimColor: drilldownTab !== "logs",
|
|
1929
|
+
bold: drilldownTab === "logs",
|
|
1930
|
+
children: [
|
|
1931
|
+
"Logs (",
|
|
1932
|
+
drilldownLogs.length,
|
|
1933
|
+
")"
|
|
1934
|
+
]
|
|
1935
|
+
}
|
|
1936
|
+
)
|
|
1937
|
+
] }),
|
|
1938
|
+
drilldownTab === "timeline" && (() => {
|
|
1939
|
+
const NAME_COL = 28;
|
|
1940
|
+
const SERVICE_COL = 12;
|
|
1941
|
+
const KIND_COL = 10;
|
|
1942
|
+
let traceStartMs = Infinity;
|
|
1943
|
+
for (const s of drilldownSummary?.spans ?? []) {
|
|
1944
|
+
if (s.startTime < traceStartMs) traceStartMs = s.startTime;
|
|
1945
|
+
}
|
|
1946
|
+
if (traceStartMs === Infinity) traceStartMs = 0;
|
|
1947
|
+
const traceDurMs = drilldownSummary?.durationMs ?? 1;
|
|
1948
|
+
const WATERFALL_WIDTH = 44;
|
|
1949
|
+
const items = drilldownTimeline.slice(drilldownScrollOffset, drilldownScrollOffset + LIST_HEIGHT);
|
|
1950
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1951
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", children: [
|
|
1952
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "".padEnd(NAME_COL + SERVICE_COL + KIND_COL + 2) }),
|
|
1953
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: buildTimeRuler(traceDurMs, WATERFALL_WIDTH) })
|
|
1954
|
+
] }),
|
|
1955
|
+
items.map((item, i) => {
|
|
1956
|
+
const isSel = i + drilldownScrollOffset === drilldownSelectedIndex;
|
|
1957
|
+
if (item.type === "span" && item.span) {
|
|
1958
|
+
const s = item.span;
|
|
1959
|
+
const node = drilldownTree.find(
|
|
1960
|
+
(n) => n.span.spanId === s.spanId
|
|
1961
|
+
);
|
|
1962
|
+
const depth = node?.depth ?? 0;
|
|
1963
|
+
const indent = " ".repeat(Math.min(depth, 4));
|
|
1964
|
+
const nameWidth = NAME_COL - Math.min(depth, 4) * 2 - 1;
|
|
1965
|
+
const svcName = spanServiceName(s);
|
|
1966
|
+
const svcColor = getServiceColor(svcName);
|
|
1967
|
+
const kindStr = (s.kind ?? "").padEnd(KIND_COL);
|
|
1968
|
+
const svcStr = truncate(svcName, SERVICE_COL - 2).padEnd(SERVICE_COL);
|
|
1969
|
+
const errorMark = s.status === "ERROR" ? "\u2717" : " ";
|
|
1970
|
+
const namePart = `${indent}${truncate(s.name, nameWidth - 1)}`.padEnd(NAME_COL - 1);
|
|
1971
|
+
const bar = buildWaterfallBar(
|
|
1972
|
+
s.startTime,
|
|
1973
|
+
s.durationMs,
|
|
1974
|
+
traceStartMs,
|
|
1975
|
+
traceDurMs,
|
|
1976
|
+
WATERFALL_WIDTH
|
|
1977
|
+
);
|
|
1978
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", children: [
|
|
1979
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { backgroundColor: isSel ? "blue" : void 0, color: isSel ? "white" : void 0, children: [
|
|
1980
|
+
isSel ? "\u25B8" : " ",
|
|
1981
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: s.status === "ERROR" ? "red" : void 0, children: errorMark }),
|
|
1982
|
+
namePart
|
|
1983
|
+
] }),
|
|
1984
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: svcColor, children: [
|
|
1985
|
+
" ",
|
|
1986
|
+
svcStr
|
|
1987
|
+
] }),
|
|
1988
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: kindStr }),
|
|
1989
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: s.status === "ERROR" ? "red" : svcColor, children: bar }),
|
|
1990
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: s.status === "ERROR" ? "red" : svcColor, children: [
|
|
1991
|
+
" ",
|
|
1992
|
+
formatDurationMs(s.durationMs)
|
|
1993
|
+
] })
|
|
1994
|
+
] }, `${s.spanId}-${i}`);
|
|
1995
|
+
} else if (item.type === "log" && item.log) {
|
|
1996
|
+
const l = item.log;
|
|
1997
|
+
const levelColor = l.level === "error" ? "red" : l.level === "warn" ? "yellow" : "blue";
|
|
1998
|
+
const relTime = drilldownSummary ? `+${formatDurationMs(l.time - traceStartMs)}` : "";
|
|
1999
|
+
const logName = ` ${l.level.toUpperCase()} ${truncate(l.message, NAME_COL - 8)}`.padEnd(NAME_COL);
|
|
2000
|
+
const logOffset = drilldownSummary ? Math.floor((l.time - traceStartMs) / traceDurMs * WATERFALL_WIDTH) : 0;
|
|
2001
|
+
const clampedOffset = Math.max(0, Math.min(logOffset, WATERFALL_WIDTH - 1));
|
|
2002
|
+
const logBar = " ".repeat(clampedOffset) + "\xB7" + " ".repeat(WATERFALL_WIDTH - clampedOffset - 1);
|
|
2003
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", children: [
|
|
2004
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { backgroundColor: isSel ? "blue" : void 0, color: isSel ? "white" : void 0, children: [
|
|
2005
|
+
isSel ? "\u25B8" : " ",
|
|
2006
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: levelColor, children: logName })
|
|
2007
|
+
] }),
|
|
2008
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: " ".padEnd(SERVICE_COL + KIND_COL + 1) }),
|
|
2009
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: logBar }),
|
|
2010
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2011
|
+
" ",
|
|
2012
|
+
relTime
|
|
2013
|
+
] })
|
|
2014
|
+
] }, `log-${i}`);
|
|
2015
|
+
}
|
|
2016
|
+
return null;
|
|
2017
|
+
}),
|
|
2018
|
+
Array.from({ length: Math.max(0, LIST_HEIGHT - items.length) }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " " }) }, `pad-${i}`))
|
|
2019
|
+
] });
|
|
2020
|
+
})(),
|
|
2021
|
+
drilldownTab === "spans" && drilldownTree.slice(drilldownScrollOffset, drilldownScrollOffset + LIST_HEIGHT).map((node, i) => renderTreeRow(node, i + drilldownScrollOffset)),
|
|
2022
|
+
drilldownTab === "spans" && Array.from({ length: Math.max(0, LIST_HEIGHT - Math.min(drilldownTree.length, LIST_HEIGHT)) }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " " }) }, `pad-${i}`)),
|
|
2023
|
+
drilldownTab === "logs" && drilldownLogs.slice(drilldownScrollOffset, drilldownScrollOffset + LIST_HEIGHT).map((log, i) => {
|
|
2024
|
+
const isSel = i + drilldownScrollOffset === drilldownSelectedIndex;
|
|
2025
|
+
const levelColor = log.level === "error" ? "red" : log.level === "warn" ? "yellow" : log.level === "info" ? "green" : void 0;
|
|
2026
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2027
|
+
ink.Text,
|
|
2028
|
+
{
|
|
2029
|
+
backgroundColor: isSel ? "blue" : void 0,
|
|
2030
|
+
color: isSel ? "white" : void 0,
|
|
2031
|
+
children: [
|
|
2032
|
+
isSel ? "\u25B8" : " ",
|
|
2033
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: levelColor, children: [
|
|
2034
|
+
" ",
|
|
2035
|
+
log.level.toUpperCase()
|
|
2036
|
+
] }),
|
|
2037
|
+
" ",
|
|
2038
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2039
|
+
"[",
|
|
2040
|
+
truncate(log.message, 50),
|
|
2041
|
+
"]"
|
|
2042
|
+
] })
|
|
2043
|
+
]
|
|
2044
|
+
}
|
|
2045
|
+
) }, `log-${i}`);
|
|
2046
|
+
}),
|
|
2047
|
+
drilldownTab === "logs" && Array.from({ length: Math.max(0, LIST_HEIGHT - Math.min(drilldownLogs.length, LIST_HEIGHT)) }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " " }) }, `pad-${i}`)),
|
|
2048
|
+
drilldownSelectedItem?.type === "span" && drilldownSelectedItem.span && (() => {
|
|
2049
|
+
const span = drilldownSelectedItem.span;
|
|
2050
|
+
const { key: keyAttrs, rest: restAttrs } = keyAttrsAndRest(span.attributes);
|
|
2051
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
|
|
2052
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", gap: 2, children: [
|
|
2053
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: span.name }),
|
|
2054
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: getServiceColor(spanServiceName(span)), children: spanServiceName(span) }),
|
|
2055
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: span.kind ?? "" }),
|
|
2056
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: span.status === "ERROR" ? "red" : "green", children: span.status }),
|
|
2057
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: formatDurationMs(span.durationMs) })
|
|
2058
|
+
] }),
|
|
2059
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", gap: 2, children: [
|
|
2060
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2061
|
+
"Trace: ",
|
|
2062
|
+
span.traceId
|
|
2063
|
+
] }),
|
|
2064
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2065
|
+
"Span: ",
|
|
2066
|
+
span.spanId
|
|
2067
|
+
] }),
|
|
2068
|
+
span.parentSpanId && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2069
|
+
"Parent: ",
|
|
2070
|
+
span.parentSpanId
|
|
2071
|
+
] })
|
|
2072
|
+
] }),
|
|
2073
|
+
keyAttrs.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
2074
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Key attributes" }),
|
|
2075
|
+
keyAttrs.slice(0, 6).map(([k, v]) => /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2076
|
+
truncate(k, 18),
|
|
2077
|
+
": ",
|
|
2078
|
+
truncate(String(v), 28)
|
|
2079
|
+
] }, k))
|
|
2080
|
+
] }),
|
|
2081
|
+
restAttrs.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
2082
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Attributes" }),
|
|
2083
|
+
restAttrs.slice(0, 8).map(([k, v]) => /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2084
|
+
truncate(k, 18),
|
|
2085
|
+
": ",
|
|
2086
|
+
truncate(String(v), 28)
|
|
2087
|
+
] }, k))
|
|
2088
|
+
] }),
|
|
2089
|
+
span.events && span.events.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", marginTop: 0, children: [
|
|
2090
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { bold: true, dimColor: true, children: [
|
|
2091
|
+
"Events (",
|
|
2092
|
+
span.events.length,
|
|
2093
|
+
")"
|
|
2094
|
+
] }),
|
|
2095
|
+
span.events.slice(0, 5).map((ev, i) => /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", children: [
|
|
2096
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: ev.name === "exception" ? "red" : "yellow", children: [
|
|
2097
|
+
" ",
|
|
2098
|
+
"\u25C6",
|
|
2099
|
+
" ",
|
|
2100
|
+
truncate(ev.name, 20)
|
|
2101
|
+
] }),
|
|
2102
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2103
|
+
" ",
|
|
2104
|
+
"+",
|
|
2105
|
+
formatDurationMs(ev.timeMs - span.startTime)
|
|
2106
|
+
] }),
|
|
2107
|
+
ev.attributes && Object.keys(ev.attributes).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2108
|
+
" ",
|
|
2109
|
+
Object.entries(ev.attributes).slice(0, 2).map(([k, v]) => `${k}=${String(v)}`).join(" ")
|
|
2110
|
+
] })
|
|
2111
|
+
] }, `ev-${i}`))
|
|
2112
|
+
] }),
|
|
2113
|
+
span.links && span.links.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", marginTop: 0, children: [
|
|
2114
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { bold: true, dimColor: true, children: [
|
|
2115
|
+
"Links (",
|
|
2116
|
+
span.links.length,
|
|
2117
|
+
")"
|
|
2118
|
+
] }),
|
|
2119
|
+
span.links.slice(0, 5).map((lnk, i) => /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", children: [
|
|
2120
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: "cyan", children: [
|
|
2121
|
+
" ",
|
|
2122
|
+
"\u2192",
|
|
2123
|
+
" trace:",
|
|
2124
|
+
lnk.traceId.slice(0, 8),
|
|
2125
|
+
"\u2026",
|
|
2126
|
+
" span:",
|
|
2127
|
+
lnk.spanId.slice(0, 8),
|
|
2128
|
+
"\u2026"
|
|
2129
|
+
] }),
|
|
2130
|
+
lnk.attributes && Object.keys(lnk.attributes).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2131
|
+
" ",
|
|
2132
|
+
Object.entries(lnk.attributes).slice(0, 2).map(([k, v]) => `${k}=${String(v)}`).join(" ")
|
|
2133
|
+
] })
|
|
2134
|
+
] }, `lnk-${i}`))
|
|
2135
|
+
] })
|
|
2136
|
+
] });
|
|
2137
|
+
})(),
|
|
2138
|
+
drilldownSelectedItem?.type === "log" && drilldownSelectedItem.log && (() => {
|
|
2139
|
+
const log = drilldownSelectedItem.log;
|
|
2140
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
|
|
2141
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", gap: 2, children: [
|
|
2142
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: log.level.toUpperCase() }),
|
|
2143
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: log.message })
|
|
2144
|
+
] }),
|
|
2145
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
2146
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Time: " }),
|
|
2147
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: new Date(log.time).toISOString() })
|
|
2148
|
+
] }),
|
|
2149
|
+
log.traceId && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2150
|
+
"Trace: ",
|
|
2151
|
+
log.traceId
|
|
2152
|
+
] }),
|
|
2153
|
+
log.spanId && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2154
|
+
"Span: ",
|
|
2155
|
+
log.spanId
|
|
2156
|
+
] }),
|
|
2157
|
+
log.attributes && Object.keys(log.attributes).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
2158
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Attributes" }),
|
|
2159
|
+
Object.entries(log.attributes).slice(0, 10).map(([k, v]) => /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2160
|
+
truncate(k, 18),
|
|
2161
|
+
": ",
|
|
2162
|
+
truncate(String(v), 40)
|
|
2163
|
+
] }, k))
|
|
2164
|
+
] })
|
|
2165
|
+
] });
|
|
2166
|
+
})()
|
|
2167
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", gap: 2, children: [
|
|
1660
2168
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1661
2169
|
ink.Box,
|
|
1662
2170
|
{
|
|
1663
2171
|
flexDirection: "column",
|
|
1664
2172
|
width: "55%",
|
|
1665
|
-
borderStyle: "
|
|
2173
|
+
borderStyle: "round",
|
|
1666
2174
|
borderColor: "gray",
|
|
1667
2175
|
paddingX: 1,
|
|
1668
2176
|
paddingY: 0,
|
|
@@ -1679,156 +2187,7 @@ ${json}
|
|
|
1679
2187
|
searchQuery
|
|
1680
2188
|
] }, "search-label")
|
|
1681
2189
|
] }),
|
|
1682
|
-
|
|
1683
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 0, flexDirection: "row", gap: 2, children: [
|
|
1684
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "yellow", children: drilldownSummary?.rootName ?? "unknown" }),
|
|
1685
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
1686
|
-
drilldownTraceId.slice(0, 16),
|
|
1687
|
-
"\u2026"
|
|
1688
|
-
] }),
|
|
1689
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "green", children: drilldownSummary ? formatDurationMs(drilldownSummary.durationMs) : "?" }),
|
|
1690
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
1691
|
-
drilldownSpans.length,
|
|
1692
|
-
" spans \u2022 ",
|
|
1693
|
-
drilldownLogs.length,
|
|
1694
|
-
" logs"
|
|
1695
|
-
] })
|
|
1696
|
-
] }),
|
|
1697
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 0, flexDirection: "row", gap: 2, children: [
|
|
1698
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1699
|
-
ink.Text,
|
|
1700
|
-
{
|
|
1701
|
-
color: drilldownTab === "timeline" ? "yellow" : void 0,
|
|
1702
|
-
dimColor: drilldownTab !== "timeline",
|
|
1703
|
-
underline: drilldownTab === "timeline",
|
|
1704
|
-
children: "Timeline"
|
|
1705
|
-
}
|
|
1706
|
-
),
|
|
1707
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1708
|
-
ink.Text,
|
|
1709
|
-
{
|
|
1710
|
-
color: drilldownTab === "spans" ? "yellow" : void 0,
|
|
1711
|
-
dimColor: drilldownTab !== "spans",
|
|
1712
|
-
underline: drilldownTab === "spans",
|
|
1713
|
-
children: [
|
|
1714
|
-
"Spans (",
|
|
1715
|
-
drilldownSpans.length,
|
|
1716
|
-
")"
|
|
1717
|
-
]
|
|
1718
|
-
}
|
|
1719
|
-
),
|
|
1720
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1721
|
-
ink.Text,
|
|
1722
|
-
{
|
|
1723
|
-
color: drilldownTab === "logs" ? "yellow" : void 0,
|
|
1724
|
-
dimColor: drilldownTab !== "logs",
|
|
1725
|
-
underline: drilldownTab === "logs",
|
|
1726
|
-
children: [
|
|
1727
|
-
"Logs (",
|
|
1728
|
-
drilldownLogs.length,
|
|
1729
|
-
")"
|
|
1730
|
-
]
|
|
1731
|
-
}
|
|
1732
|
-
)
|
|
1733
|
-
] })
|
|
1734
|
-
] }),
|
|
1735
|
-
drilldownTraceId != null && drilldownTab === "timeline" && (() => {
|
|
1736
|
-
let traceStartMs = Infinity;
|
|
1737
|
-
for (const s of drilldownSummary?.spans ?? []) {
|
|
1738
|
-
if (s.startTime < traceStartMs) traceStartMs = s.startTime;
|
|
1739
|
-
}
|
|
1740
|
-
if (traceStartMs === Infinity) traceStartMs = 0;
|
|
1741
|
-
const traceDurMs = drilldownSummary?.durationMs ?? 1;
|
|
1742
|
-
const WATERFALL_WIDTH = 24;
|
|
1743
|
-
const items = drilldownTimeline.slice(drilldownScrollOffset, drilldownScrollOffset + LIST_HEIGHT);
|
|
1744
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1745
|
-
items.map((item, i) => {
|
|
1746
|
-
const isSel = i + drilldownScrollOffset === drilldownSelectedIndex;
|
|
1747
|
-
if (item.type === "span" && item.span) {
|
|
1748
|
-
const s = item.span;
|
|
1749
|
-
const node = drilldownTree.find(
|
|
1750
|
-
(n) => n.span.spanId === s.spanId
|
|
1751
|
-
);
|
|
1752
|
-
const depth = node?.depth ?? 0;
|
|
1753
|
-
const indent = " ".repeat(Math.min(depth, 4));
|
|
1754
|
-
const nameWidth = 24 - Math.min(depth, 4) * 2;
|
|
1755
|
-
const bar = buildWaterfallBar(
|
|
1756
|
-
s.startTime,
|
|
1757
|
-
s.durationMs,
|
|
1758
|
-
traceStartMs,
|
|
1759
|
-
traceDurMs,
|
|
1760
|
-
WATERFALL_WIDTH
|
|
1761
|
-
);
|
|
1762
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", children: [
|
|
1763
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { backgroundColor: isSel ? "gray" : void 0, color: isSel ? "white" : void 0, children: [
|
|
1764
|
-
isSel ? "\u25B8" : " ",
|
|
1765
|
-
indent,
|
|
1766
|
-
truncate(s.name, nameWidth)
|
|
1767
|
-
] }),
|
|
1768
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " " }),
|
|
1769
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: s.status === "ERROR" ? "red" : "green", children: bar }),
|
|
1770
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
1771
|
-
" ",
|
|
1772
|
-
formatDurationMs(s.durationMs),
|
|
1773
|
-
s.kind ? ` ${s.kind}` : ""
|
|
1774
|
-
] })
|
|
1775
|
-
] }, `${s.spanId}-${i}`);
|
|
1776
|
-
} else if (item.type === "log" && item.log) {
|
|
1777
|
-
const l = item.log;
|
|
1778
|
-
const levelColor = l.level === "error" ? "red" : l.level === "warn" ? "yellow" : "blue";
|
|
1779
|
-
const relTime = drilldownSummary ? `+${formatDurationMs(l.time - traceStartMs)}` : "";
|
|
1780
|
-
const logOffset = drilldownSummary ? Math.floor((l.time - traceStartMs) / traceDurMs * WATERFALL_WIDTH) : 0;
|
|
1781
|
-
const clampedOffset = Math.max(0, Math.min(logOffset, WATERFALL_WIDTH - 1));
|
|
1782
|
-
const logBar = " ".repeat(clampedOffset) + "\xB7" + " ".repeat(WATERFALL_WIDTH - clampedOffset - 1);
|
|
1783
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", children: [
|
|
1784
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { backgroundColor: isSel ? "gray" : void 0, color: isSel ? "white" : void 0, children: [
|
|
1785
|
-
isSel ? "\u25B8" : " ",
|
|
1786
|
-
" ",
|
|
1787
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: levelColor, children: l.level.toUpperCase() }),
|
|
1788
|
-
" ",
|
|
1789
|
-
truncate(l.message, 18)
|
|
1790
|
-
] }),
|
|
1791
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " " }),
|
|
1792
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: logBar }),
|
|
1793
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
1794
|
-
" ",
|
|
1795
|
-
relTime
|
|
1796
|
-
] })
|
|
1797
|
-
] }, `log-${i}`);
|
|
1798
|
-
}
|
|
1799
|
-
return null;
|
|
1800
|
-
}),
|
|
1801
|
-
Array.from({ length: Math.max(0, LIST_HEIGHT - items.length) }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " " }) }, `pad-${i}`))
|
|
1802
|
-
] });
|
|
1803
|
-
})(),
|
|
1804
|
-
drilldownTraceId != null && drilldownTab === "spans" && drilldownTree.slice(drilldownScrollOffset, drilldownScrollOffset + LIST_HEIGHT).map((node, i) => renderTreeRow(node, i + drilldownScrollOffset)),
|
|
1805
|
-
drilldownTraceId != null && drilldownTab === "spans" && Array.from({ length: Math.max(0, LIST_HEIGHT - Math.min(drilldownTree.length, LIST_HEIGHT)) }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " " }) }, `pad-${i}`)),
|
|
1806
|
-
drilldownTraceId != null && drilldownTab === "logs" && drilldownLogs.slice(drilldownScrollOffset, drilldownScrollOffset + LIST_HEIGHT).map((log, i) => {
|
|
1807
|
-
const isSel = i + drilldownScrollOffset === drilldownSelectedIndex;
|
|
1808
|
-
const levelColor = log.level === "error" ? "red" : log.level === "warn" ? "yellow" : log.level === "info" ? "green" : void 0;
|
|
1809
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1810
|
-
ink.Text,
|
|
1811
|
-
{
|
|
1812
|
-
backgroundColor: isSel ? "gray" : void 0,
|
|
1813
|
-
color: isSel ? "white" : void 0,
|
|
1814
|
-
children: [
|
|
1815
|
-
isSel ? "\u25B8" : " ",
|
|
1816
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: levelColor, children: [
|
|
1817
|
-
" ",
|
|
1818
|
-
log.level.toUpperCase()
|
|
1819
|
-
] }),
|
|
1820
|
-
" ",
|
|
1821
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
1822
|
-
"[",
|
|
1823
|
-
truncate(log.message, 50),
|
|
1824
|
-
"]"
|
|
1825
|
-
] })
|
|
1826
|
-
]
|
|
1827
|
-
}
|
|
1828
|
-
) }, `log-${i}`);
|
|
1829
|
-
}),
|
|
1830
|
-
drilldownTraceId != null && drilldownTab === "logs" && Array.from({ length: Math.max(0, LIST_HEIGHT - Math.min(drilldownLogs.length, LIST_HEIGHT)) }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " " }) }, `pad-${i}`)),
|
|
1831
|
-
drilldownTraceId == null && /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: viewMode === "trace" ? filteredSummaries.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2190
|
+
/* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: viewMode === "trace" ? filteredSummaries.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1832
2191
|
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
1833
2192
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "No traces yet. Call a traced function or hit an endpoint to see them here." }),
|
|
1834
2193
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Tip: trace() your handlers with autotel to get spans." })
|
|
@@ -1853,6 +2212,11 @@ ${json}
|
|
|
1853
2212
|
" ",
|
|
1854
2213
|
formatRelative(t2.lastEndTime)
|
|
1855
2214
|
] }),
|
|
2215
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2216
|
+
" ",
|
|
2217
|
+
t2.traceId.slice(0, 12),
|
|
2218
|
+
"\u2026"
|
|
2219
|
+
] }),
|
|
1856
2220
|
t2.hasError && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "red", children: " \u25CF" })
|
|
1857
2221
|
] }, t2.traceId);
|
|
1858
2222
|
}),
|
|
@@ -1866,6 +2230,8 @@ ${json}
|
|
|
1866
2230
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1867
2231
|
filteredSpans.slice(0, 20).map((s, i) => {
|
|
1868
2232
|
const isSel = i === selected;
|
|
2233
|
+
const svcName = spanServiceName(s);
|
|
2234
|
+
const svcColor = getServiceColor(svcName);
|
|
1869
2235
|
const statusColor = s.status === "ERROR" ? "red" : s.durationMs > 500 ? "yellow" : "green";
|
|
1870
2236
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1871
2237
|
ink.Box,
|
|
@@ -1874,6 +2240,10 @@ ${json}
|
|
|
1874
2240
|
children: [
|
|
1875
2241
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: isSel ? "cyan" : void 0, children: isSel ? "\u203A " : " " }),
|
|
1876
2242
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: colors ? statusColor : void 0, children: truncate(s.name, 26) }),
|
|
2243
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: svcColor, children: [
|
|
2244
|
+
" ",
|
|
2245
|
+
truncate(svcName, 10)
|
|
2246
|
+
] }),
|
|
1877
2247
|
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
1878
2248
|
" ",
|
|
1879
2249
|
formatDurationMs(s.durationMs)
|
|
@@ -1972,7 +2342,7 @@ ${json}
|
|
|
1972
2342
|
{
|
|
1973
2343
|
flexDirection: "column",
|
|
1974
2344
|
width: "45%",
|
|
1975
|
-
borderStyle: "
|
|
2345
|
+
borderStyle: "round",
|
|
1976
2346
|
borderColor: "gray",
|
|
1977
2347
|
paddingX: 1,
|
|
1978
2348
|
paddingY: 0,
|
|
@@ -2020,92 +2390,7 @@ ${json}
|
|
|
2020
2390
|
] })
|
|
2021
2391
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2022
2392
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginBottom: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Details" }) }),
|
|
2023
|
-
|
|
2024
|
-
const span = drilldownSelectedItem.span;
|
|
2025
|
-
const { key: keyAttrs, rest: restAttrs } = keyAttrsAndRest(
|
|
2026
|
-
span.attributes
|
|
2027
|
-
);
|
|
2028
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2029
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
2030
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Name: " }),
|
|
2031
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: span.name })
|
|
2032
|
-
] }),
|
|
2033
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
2034
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Status: " }),
|
|
2035
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: span.status === "ERROR" ? "red" : "green", children: span.status })
|
|
2036
|
-
] }),
|
|
2037
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
2038
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Duration: " }),
|
|
2039
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: formatDurationMs(span.durationMs) })
|
|
2040
|
-
] }),
|
|
2041
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2042
|
-
"Trace: ",
|
|
2043
|
-
span.traceId
|
|
2044
|
-
] }),
|
|
2045
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2046
|
-
"Span: ",
|
|
2047
|
-
span.spanId
|
|
2048
|
-
] }),
|
|
2049
|
-
span.parentSpanId && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2050
|
-
"Parent: ",
|
|
2051
|
-
span.parentSpanId
|
|
2052
|
-
] }),
|
|
2053
|
-
span.kind && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2054
|
-
"Kind: ",
|
|
2055
|
-
span.kind
|
|
2056
|
-
] }),
|
|
2057
|
-
keyAttrs.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
2058
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Key attributes" }),
|
|
2059
|
-
keyAttrs.slice(0, 6).map(([k, v]) => /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2060
|
-
truncate(k, 18),
|
|
2061
|
-
": ",
|
|
2062
|
-
truncate(String(v), 28)
|
|
2063
|
-
] }, k))
|
|
2064
|
-
] }),
|
|
2065
|
-
restAttrs.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
2066
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Attributes" }),
|
|
2067
|
-
restAttrs.slice(0, 8).map(([k, v]) => /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2068
|
-
truncate(k, 18),
|
|
2069
|
-
": ",
|
|
2070
|
-
truncate(String(v), 28)
|
|
2071
|
-
] }, k))
|
|
2072
|
-
] }),
|
|
2073
|
-
keyAttrs.length === 0 && restAttrs.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "(no attributes)" })
|
|
2074
|
-
] });
|
|
2075
|
-
})() : drilldownTraceId != null && drilldownSelectedItem?.type === "log" && drilldownSelectedItem.log ? (() => {
|
|
2076
|
-
const log = drilldownSelectedItem.log;
|
|
2077
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2078
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
2079
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Level: " }),
|
|
2080
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: log.level.toUpperCase() })
|
|
2081
|
-
] }),
|
|
2082
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
2083
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Time: " }),
|
|
2084
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: new Date(log.time).toISOString() })
|
|
2085
|
-
] }),
|
|
2086
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
2087
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Message: " }),
|
|
2088
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: log.message })
|
|
2089
|
-
] }),
|
|
2090
|
-
log.traceId && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2091
|
-
"Trace: ",
|
|
2092
|
-
log.traceId
|
|
2093
|
-
] }),
|
|
2094
|
-
log.spanId && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2095
|
-
"Span: ",
|
|
2096
|
-
log.spanId
|
|
2097
|
-
] }),
|
|
2098
|
-
log.attributes && Object.keys(log.attributes).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
2099
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Attributes" }),
|
|
2100
|
-
Object.entries(log.attributes).slice(0, 10).map(([k, v]) => /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2101
|
-
truncate(k, 18),
|
|
2102
|
-
": ",
|
|
2103
|
-
truncate(String(v), 40)
|
|
2104
|
-
] }, k))
|
|
2105
|
-
] })
|
|
2106
|
-
] });
|
|
2107
|
-
})() : drilldownTraceId == null ? null : /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Select an item to view details." }),
|
|
2108
|
-
drilldownTraceId == null && (viewMode === "errors" ? (() => {
|
|
2393
|
+
viewMode === "errors" ? (() => {
|
|
2109
2394
|
const e = filteredErrorSummaries[selected] ?? null;
|
|
2110
2395
|
if (!e)
|
|
2111
2396
|
return /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Select an error to view details." });
|
|
@@ -2323,12 +2608,12 @@ ${json}
|
|
|
2323
2608
|
] }, w.span.spanId);
|
|
2324
2609
|
})
|
|
2325
2610
|
] })
|
|
2326
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Select a trace or span to view details." })
|
|
2611
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Select a trace or span to view details." })
|
|
2327
2612
|
] })
|
|
2328
2613
|
}
|
|
2329
2614
|
)
|
|
2330
|
-
] }),
|
|
2331
|
-
showStats && /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginTop: 1, borderStyle: "
|
|
2615
|
+
] })),
|
|
2616
|
+
showStats && /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
2332
2617
|
"Spans: ",
|
|
2333
2618
|
stats.total,
|
|
2334
2619
|
" | Span errors: ",
|
|
@@ -2523,6 +2808,8 @@ function* extractSpans(payload) {
|
|
|
2523
2808
|
if (!Array.isArray(resourceSpans)) return;
|
|
2524
2809
|
for (const resourceSpan of resourceSpans) {
|
|
2525
2810
|
if (!resourceSpan || typeof resourceSpan !== "object") continue;
|
|
2811
|
+
const resource = resourceSpan.resource;
|
|
2812
|
+
const resourceAttrs = attrsToRecord(resource?.attributes);
|
|
2526
2813
|
const scopeSpans = resourceSpan.scopeSpans;
|
|
2527
2814
|
if (!Array.isArray(scopeSpans)) continue;
|
|
2528
2815
|
for (const scopeSpan of scopeSpans) {
|
|
@@ -2531,15 +2818,33 @@ function* extractSpans(payload) {
|
|
|
2531
2818
|
if (!Array.isArray(spans)) continue;
|
|
2532
2819
|
for (const span of spans) {
|
|
2533
2820
|
if (span && typeof span === "object") {
|
|
2534
|
-
yield span;
|
|
2821
|
+
yield { span, resourceAttrs };
|
|
2535
2822
|
}
|
|
2536
2823
|
}
|
|
2537
2824
|
}
|
|
2538
2825
|
}
|
|
2539
2826
|
}
|
|
2540
|
-
function otlpSpanToTerminalEvent(span) {
|
|
2827
|
+
function otlpSpanToTerminalEvent(span, resourceAttrs = {}) {
|
|
2541
2828
|
const startTime = toMs(span.startTimeUnixNano);
|
|
2542
2829
|
const endTime = toMs(span.endTimeUnixNano);
|
|
2830
|
+
const spanAttrs = attrsToRecord(span.attributes);
|
|
2831
|
+
const mergedAttrs = {};
|
|
2832
|
+
for (const [k, v] of Object.entries(resourceAttrs)) {
|
|
2833
|
+
mergedAttrs[k] = v;
|
|
2834
|
+
}
|
|
2835
|
+
for (const [k, v] of Object.entries(spanAttrs)) {
|
|
2836
|
+
mergedAttrs[k] = v;
|
|
2837
|
+
}
|
|
2838
|
+
const parsedEvents = span.events?.length ? span.events.map((e) => ({
|
|
2839
|
+
name: e.name || "",
|
|
2840
|
+
timeMs: toMs(e.timeUnixNano),
|
|
2841
|
+
attributes: attrsToRecord(e.attributes)
|
|
2842
|
+
})) : void 0;
|
|
2843
|
+
const parsedLinks = span.links?.length ? span.links.map((l) => ({
|
|
2844
|
+
traceId: normalizeHexId(l.traceId, 32),
|
|
2845
|
+
spanId: normalizeHexId(l.spanId, 16),
|
|
2846
|
+
attributes: attrsToRecord(l.attributes)
|
|
2847
|
+
})) : void 0;
|
|
2543
2848
|
return {
|
|
2544
2849
|
name: span.name || "unnamed",
|
|
2545
2850
|
spanId: normalizeHexId(span.spanId, 16),
|
|
@@ -2550,7 +2855,9 @@ function otlpSpanToTerminalEvent(span) {
|
|
|
2550
2855
|
durationMs: Math.max(0, endTime - startTime),
|
|
2551
2856
|
status: mapStatus2(span.status?.code),
|
|
2552
2857
|
kind: mapKind2(span.kind),
|
|
2553
|
-
attributes:
|
|
2858
|
+
attributes: mergedAttrs,
|
|
2859
|
+
...parsedEvents ? { events: parsedEvents } : {},
|
|
2860
|
+
...parsedLinks ? { links: parsedLinks } : {}
|
|
2554
2861
|
};
|
|
2555
2862
|
}
|
|
2556
2863
|
async function readJsonBody(req) {
|
|
@@ -2575,8 +2882,8 @@ function sendJson(res, status, data) {
|
|
|
2575
2882
|
}
|
|
2576
2883
|
function parseOtlpEvents(payload) {
|
|
2577
2884
|
const events = [];
|
|
2578
|
-
for (const span of extractSpans(payload)) {
|
|
2579
|
-
events.push(otlpSpanToTerminalEvent(span));
|
|
2885
|
+
for (const { span, resourceAttrs } of extractSpans(payload)) {
|
|
2886
|
+
events.push(otlpSpanToTerminalEvent(span, resourceAttrs));
|
|
2580
2887
|
}
|
|
2581
2888
|
return events;
|
|
2582
2889
|
}
|