Skip to content

Commit 224e6dc

Browse files
authored
fix(trace): show span IDs in trace view and fix event_id mapping (#400)
## Summary `trace view` showed `undefined` for every span ID in the tree because the trace detail API returns `event_id` instead of `span_id`. This also broke `span view` lookups since `findSpanById` compared against the missing field. Two fixes: - Normalize the API response in `getDetailedTrace` by copying `event_id` → `span_id` when `span_id` is missing - Append the span ID (dimmed) to each span tree line so users can copy it into `span view` Before: ``` ├─ http.client — GET /api/users (52ms) undefined ``` After: ``` ├─ http.client — GET /api/users (52ms) a6ff0caaa87dd118 ``` ## Test plan - [x] `bun run typecheck` passes - [x] `bun run lint` passes - [x] `bun test test/lib/formatters/span-tree.test.ts` — 20 tests pass - [x] Manual: `trace view <trace-id>` shows real span IDs - [x] Manual: `span view <span-id> --trace <trace-id>` finds the span
1 parent 0671cfa commit 224e6dc

File tree

4 files changed

+51
-1
lines changed

4 files changed

+51
-1
lines changed

src/lib/api-client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export {
9090
export {
9191
getDetailedTrace,
9292
listTransactions,
93+
normalizeTraceSpan,
9394
} from "./api/traces.js";
9495

9596
export {

src/lib/api/traces.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,24 @@ export async function getDetailedTrace(
4848
},
4949
}
5050
);
51-
return data;
51+
return data.map(normalizeTraceSpan);
52+
}
53+
54+
/**
55+
* The trace detail API (`/trace/{id}/`) returns each span's unique identifier
56+
* as `event_id` rather than `span_id`. The value is the same 16-hex-char span
57+
* ID that `parent_span_id` references on child spans. We copy it to `span_id`
58+
* so the rest of the codebase can use a single, predictable field name.
59+
*/
60+
export function normalizeTraceSpan(span: TraceSpan): TraceSpan {
61+
const normalized = { ...span };
62+
if (!normalized.span_id && normalized.event_id) {
63+
normalized.span_id = normalized.event_id;
64+
}
65+
if (normalized.children) {
66+
normalized.children = normalized.children.map(normalizeTraceSpan);
67+
}
68+
return normalized;
5269
}
5370

5471
/** Fields to request from the transactions API */

src/lib/formatters/human.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,8 @@ function formatSpanSimple(span: TraceSpan, opts: FormatSpanOptions): void {
11221122
line += ` ${muted(`(${prettyMs(durationMs)})`)}`;
11231123
}
11241124

1125+
line += ` ${muted(span.span_id ?? "")}`;
1126+
11251127
lines.push(line);
11261128

11271129
if (currentDepth < maxDepth) {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { describe, expect, test } from "bun:test";
2+
import { normalizeTraceSpan } from "../../src/lib/api-client.js";
3+
4+
describe("normalizeTraceSpan", () => {
5+
test("copies event_id to span_id when span_id is missing", () => {
6+
const span = { event_id: "abc123", start_timestamp: 0 } as Parameters<
7+
typeof normalizeTraceSpan
8+
>[0];
9+
const result = normalizeTraceSpan(span);
10+
expect(result.span_id).toBe("abc123");
11+
});
12+
13+
test("preserves existing span_id", () => {
14+
const result = normalizeTraceSpan({
15+
span_id: "existing",
16+
event_id: "other",
17+
start_timestamp: 0,
18+
});
19+
expect(result.span_id).toBe("existing");
20+
});
21+
22+
test("recurses into children", () => {
23+
const result = normalizeTraceSpan({
24+
span_id: "parent",
25+
start_timestamp: 0,
26+
children: [{ event_id: "child1", start_timestamp: 1 } as any],
27+
});
28+
expect(result.children?.[0]?.span_id).toBe("child1");
29+
});
30+
});

0 commit comments

Comments
 (0)