Add richer metadata to logs and expandable detail panel
Some checks failed
CI / test (pull_request) Has been cancelled
Some checks failed
CI / test (pull_request) Has been cancelled
Enriches webhook logs with sender and ref for better visibility into who triggered events and what they affected (e.g. which branch). PR logs now include the title, comment logs include the first 200 characters of the comment body. Adds an expandable detail row in the log viewer UI that shows all metadata as key-value pairs when clicked, keeping the summary line clean. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
726c76cf96
commit
6555be012a
|
|
@ -60,7 +60,7 @@ class PRHandler {
|
|||
await this.handleStateChange(pullRequest, repository)
|
||||
}
|
||||
|
||||
log({ type: "pr", action, pr: payload.number, repo: repository.full_name, user: pullRequest.user.login })
|
||||
log({ type: "pr", action, pr: payload.number, repo: repository.full_name, user: pullRequest.user.login, title: pullRequest.title })
|
||||
}
|
||||
|
||||
static async handleOpened(pullRequest: Gitea.PullRequest, repository: Gitea.Repository) {
|
||||
|
|
@ -153,7 +153,7 @@ class CommentHandler {
|
|||
await this.handleDeleted(comment)
|
||||
}
|
||||
|
||||
log({ type: "comment", action, pr: issue.number, repo: repository.full_name, user: comment.user.login })
|
||||
log({ type: "comment", action, pr: issue.number, repo: repository.full_name, user: comment.user.login, body: comment.body.slice(0, 200) })
|
||||
}
|
||||
|
||||
static async handleCreated(issue: Gitea.Issue, comment: Gitea.Comment, repo: string) {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import { basename } from "node:path"
|
|||
import { getConfig } from "./config"
|
||||
|
||||
export type LogEvent =
|
||||
| { type: "webhook"; eventType: string; repo: string }
|
||||
| { type: "webhook"; eventType: string; repo: string; sender?: string; ref?: string }
|
||||
| { type: "webhook-ignored"; eventType: string; repo: string }
|
||||
| { type: "pr"; action: string; pr: number; repo: string; user: string }
|
||||
| { type: "comment"; action: string; pr: number; repo: string; user: string }
|
||||
| { type: "pr"; action: string; pr: number; repo: string; user: string; title?: string }
|
||||
| { type: "comment"; action: string; pr: number; repo: string; user: string; body?: string }
|
||||
| { type: "comment-skipped"; pr: number; repo: string; commentId: number }
|
||||
| { type: "review"; action: string; pr: number; repo: string; user: string }
|
||||
| { type: "thread-auto-created"; pr: number; repo: string }
|
||||
|
|
@ -96,6 +96,9 @@ onLog((event) => {
|
|||
if ("repo" in event) parts.push(dim(event.repo))
|
||||
if ("pr" in event) parts.push(cyan(`#${event.pr}`))
|
||||
if ("user" in event) parts.push(dim(event.user))
|
||||
if ("sender" in event && event.sender) parts.push(dim(event.sender))
|
||||
if ("ref" in event && event.ref) parts.push(dim(event.ref))
|
||||
if ("title" in event && event.title) parts.push(event.title)
|
||||
if ("detail" in event) parts.push(event.detail)
|
||||
if ("command" in event) parts.push(event.command)
|
||||
if ("eventType" in event) parts.push(event.eventType)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ const server = serve({
|
|||
const payload = await req.json()
|
||||
const eventType = req.headers.get("X-Gitea-Event") || "unknown"
|
||||
const repo = (payload as any)?.repository?.full_name || "unknown"
|
||||
log({ type: "webhook", eventType, repo })
|
||||
const sender = (payload as any)?.sender?.login as string | undefined
|
||||
const ref = (payload as any)?.ref as string | undefined
|
||||
log({ type: "webhook", eventType, repo, sender, ref })
|
||||
|
||||
try {
|
||||
await handleGiteaWebhook(payload, eventType as any)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,19 @@ const typeColors: Record<string, string> = {
|
|||
error: "#f87171",
|
||||
}
|
||||
|
||||
function EventRow({ event }: { event: StoredLogEvent }) {
|
||||
const summaryFields = ["type", "ts"] as const
|
||||
|
||||
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
|
||||
meta[key] = typeof value === "string" ? value : String(value)
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
function EventRow({ event, index }: { event: StoredLogEvent; index: number }) {
|
||||
const color = typeColors[event.type] || "#9ca3af"
|
||||
const time = event.ts.slice(11, 19)
|
||||
|
||||
|
|
@ -35,14 +47,41 @@ function EventRow({ event }: { event: StoredLogEvent }) {
|
|||
details.push(msg)
|
||||
}
|
||||
|
||||
const meta = getEventMeta(event)
|
||||
const hasExtra = Object.keys(meta).length > 0
|
||||
|
||||
return (
|
||||
<tr style="border-bottom: 1px solid #2a2a2a">
|
||||
<td data-ts={event.ts} style="padding: 6px 12px; color: #6b7280; white-space: nowrap; font-size: 13px">{time}</td>
|
||||
<td style={`padding: 6px 12px; color: ${color}; white-space: nowrap; font-weight: 600; font-size: 13px`}>
|
||||
{event.type}
|
||||
</td>
|
||||
<td style="padding: 6px 12px; color: #d1d5db; font-size: 13px">{details.join(" ")}</td>
|
||||
</tr>
|
||||
<>
|
||||
<tr
|
||||
style={`border-bottom: 1px solid #2a2a2a${hasExtra ? "; cursor: pointer" : ""}`}
|
||||
data-row={index}
|
||||
onclick={hasExtra ? `(function(e){var d=document.getElementById('detail-${index}');if(d){d.style.display=d.style.display==='none'?'table-row':'none';e.currentTarget.querySelector('.expand-icon').textContent=d.style.display==='none'?'+':'-'}})(event)` : undefined}
|
||||
>
|
||||
<td style="padding: 6px 4px 6px 12px; color: #4a4a4a; font-size: 11px; width: 16px; user-select: none">
|
||||
{hasExtra && <span class="expand-icon">+</span>}
|
||||
</td>
|
||||
<td data-ts={event.ts} style="padding: 6px 12px; color: #6b7280; white-space: nowrap; font-size: 13px">{time}</td>
|
||||
<td style={`padding: 6px 12px; color: ${color}; white-space: nowrap; font-weight: 600; font-size: 13px`}>
|
||||
{event.type}
|
||||
</td>
|
||||
<td style="padding: 6px 12px; color: #d1d5db; font-size: 13px">{details.join(" ")}</td>
|
||||
</tr>
|
||||
{hasExtra && (
|
||||
<tr id={`detail-${index}`} style="display: none; background: #141414">
|
||||
<td></td>
|
||||
<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>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -154,8 +193,8 @@ export function LogsPage(req: Request) {
|
|||
{/* Events table */}
|
||||
<table style="width: 100%; border-collapse: collapse">
|
||||
<tbody>
|
||||
{events.map((event) => (
|
||||
<EventRow event={event} />
|
||||
{events.map((event, i) => (
|
||||
<EventRow event={event} index={i} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user