Repository navigation
feat: self-hosted error monitoring (client + server errors, admin Errors tab) - #173
Merged
Merged
Conversation
…min Errors tab)
- instrumentation.ts onRequestError: [server-error] structured log line +
best-effort, time-boxed record into app_errors
- instrumentation-client.ts: window error/unhandledrejection capture;
error.tsx + new global-error.tsx report boundary errors
- Client reporter batches via createTrackingQueue to POST /api/errors;
per-session dedupe (message+stack head), cap 10, noise filter
- /api/errors: single or {events:[...]}, zod + truncation, 32KB cap,
aggregates by sha1 fingerprint via record_app_error(); 202 when the
table/function is not deployed yet
- Migration 20261001120000_app_errors.sql (not applied): table, RLS
(service role only), last_seen index, atomic upsert function
- Admin Errors tab + GET/POST /api/admin/errors (list/filter/resolve)
- Tests for fingerprinting, noise filtering, schema, reporter
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lightweight error monitoring with no third-party account. Client and server errors are aggregated by fingerprint into a new Supabase table
app_errorsand shown in a new admin Errors tab.Server:
src/instrumentation.tsonRequestErrorlogs one structured line ([server-error] {route, routeType, method, path, digest, message, stack}, visible in Vercel logs) and records the error. The write is best effort, capped at 1.5s, and never throws. Next.js control-flow digests (NEXT_*) are skipped.Client:
src/instrumentation-client.ts(supported in Next 16) captureserrorandunhandledrejection.error.tsx(UI unchanged) and a newglobal-error.tsxreport boundary errors. Reports go throughcreateTrackingQueuetoPOST /api/errors. The client:Script error.with no stack, aborts and network failuresAPI
/api/errors: accepts a single report or{events:[...]}(max 20) throughparseBatch. Fields are zod-validated and truncated (message 500, stack 4000, url 500, UA 300). Bodies over 32KB get a 413. Each report goes torecord_app_error(), keyed by sha1(source + message + first stack frame), and duplicates inside one batch are merged into a single call. If the table or function isn't deployed yet, the route returns 202 and callsconsole.warn.Migration (NOT applied):
supabase/migrations/20261001120000_app_errors.sqllast_seen descindexrecord_app_error: SECURITY DEFINER, pinned search_path, EXECUTE only for service_role. A repeat of a resolved error reopens it.Admin: Errors tab with unresolved/resolved/all and source filters, expandable stack/URL/UA, and resolve/reopen buttons (single or bulk). Backed by
GET/POST /api/admin/errorswith password auth throughisAdminPassword.Before / after merge
POST /api/errors. It is public and unauthenticated.Testing
npx tsc --noEmit,npm test(307 passed, including newerror-monitoring.test.ts),npx next build, eslint on the new filesnext dev+ Playwright: uncaught errors and a rejection thrown from the page were batched to/api/errors, and the ResizeObserver noise was filtered out. The route returned 202 (table missing). curl checks: 413 for an oversized body, 400 for bad JSON, 401 for a bad admin password. A temporary throwing route produced the[server-error]log line.🤖 Generated with Claude Code