Improve error display in logs viewer: show message and stack trace
Some checks failed
CI / test (pull_request) Has been cancelled

Errors from JSONL files are serialized as plain objects with message/stack
properties, not Error instances. The summary line now displays the error
message correctly, and the expandable detail panel shows the full stack
trace in a monospace preformatted block for easier debugging.
This commit is contained in:
Corey Johnson 2026-03-10 11:05:01 -07:00
parent f750663eca
commit afb15a8584

View File

@ -19,11 +19,28 @@ const typeColors: Record<string, string> = {
const summaryFields = ["type", "ts"] as const
function errorMessage(error: unknown): string {
if (error instanceof Error) return error.message
if (typeof error === "object" && error && "message" in error) return String((error as any).message)
return String(error)
}
function errorStack(error: unknown): string | undefined {
if (error instanceof Error) return error.stack
if (typeof error === "object" && error && "stack" in error) return String((error as any).stack)
}
function getEventMeta(event: StoredLogEvent): Record<string, string> {
const meta: Record<string, string> = {}
for (const [key, value] of Object.entries(event)) {
if (summaryFields.includes(key as any)) continue
if (value === undefined) continue
if (key === "error") {
meta[key] = errorMessage(value)
const stack = errorStack(value)
if (stack) meta.stack = stack
continue
}
meta[key] = typeof value === "string" ? value : String(value)
}
return meta
@ -43,8 +60,7 @@ function EventRow({ event, index }: { event: StoredLogEvent; index: number }) {
if ("eventType" in event) details.push(event.eventType)
if (event.type === "error" && event.context) details.push(event.context)
if (event.type === "error") {
const msg = event.error instanceof Error ? event.error.message : String(event.error)
details.push(msg)
details.push(errorMessage(event.error))
}
const meta = getEventMeta(event)
@ -72,10 +88,17 @@ function EventRow({ event, index }: { event: StoredLogEvent; index: number }) {
<td colspan="3" style="padding: 8px 12px 12px">
<div style="display: flex; flex-wrap: wrap; gap: 6px 20px; font-size: 12px">
{Object.entries(meta).map(([key, value]) => (
<div style="display: flex; gap: 6px">
<span style="color: #6b7280">{key}</span>
<span style="color: #d1d5db; max-width: 500px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">{value}</span>
</div>
key === "stack" ? (
<div style="width: 100%">
<span style="color: #6b7280">{key}</span>
<pre style="color: #d1d5db; margin: 4px 0 0; font-size: 11px; white-space: pre-wrap; opacity: 0.8">{value}</pre>
</div>
) : (
<div style="display: flex; gap: 6px">
<span style="color: #6b7280">{key}</span>
<span style="color: #d1d5db; max-width: 500px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">{value}</span>
</div>
)
))}
</div>
</td>