Files
trackbugs/PLAN.md
T

328 lines
12 KiB
Markdown
Raw Normal View History

2026-03-19 19:25:51 +02:00
# Error Tracker — Build Plan
A self-hosted error tracking application (inspired by [Bugsink](https://www.bugsink.com/) / Sentry) built on this Laravel 13 app.
## Stack Summary
| Layer | Technology |
|-------|-----------|
| Backend | Laravel 13, PHP 8.3+ |
| Database | SQLite (dev), easily swappable to Postgres/MySQL |
| Frontend | Blade + Tailwind CSS 4 + Vite 8 |
| Queue | Database driver (upgrade to Redis for production) |
| Auth | Laravel Breeze (to be installed) |
---
## Phase 1 — Foundation & Auth
### 1.1 Install Auth Scaffolding
- Install Laravel Breeze (Blade + Tailwind stack) for login, registration, password reset, email verification.
- This gives us the authenticated layout, middleware, and user management out of the box.
### 1.2 Core Data Models & Migrations
#### `projects` table
| Column | Type | Notes |
|--------|------|-------|
| id | ulid (pk) | |
| name | string | |
| platform | string (nullable) | e.g. "php", "javascript", "python" |
| dsn_public_key | string (unique) | Auto-generated UUID used in the DSN |
| dsn_secret_key | string (unique) | Auto-generated secret for server-side SDKs |
| created_at / updated_at | timestamps | |
#### `project_user` pivot table
| Column | Type | Notes |
|--------|------|-------|
| project_id | foreign | |
| user_id | foreign | |
| role | string | "owner", "member" |
#### `issues` table (groups of similar errors)
| Column | Type | Notes |
|--------|------|-------|
| id | ulid (pk) | |
| project_id | foreign | |
| fingerprint | string (index) | Hash used to group events |
| title | string | Short description, e.g. exception class + message |
| culprit | string (nullable) | File/function where it occurred |
| level | string | "fatal", "error", "warning", "info", "debug" |
| status | string | "unresolved", "resolved", "ignored", "muted" |
| first_seen_at | datetime | |
| last_seen_at | datetime | |
| times_seen | integer | Counter cache |
| is_regression | boolean | Re-opened after being resolved |
| metadata | json (nullable) | Platform, runtime, SDK info |
| created_at / updated_at | timestamps | |
#### `events` table (individual error occurrences)
| Column | Type | Notes |
|--------|------|-------|
| id | ulid (pk) | |
| issue_id | foreign | |
| project_id | foreign | |
| event_id | string (unique) | UUID sent by the SDK |
| level | string | |
| message | text (nullable) | |
| platform | string (nullable) | |
| timestamp | datetime | Event timestamp from SDK |
| server_name | string (nullable) | |
| release | string (nullable) | App version/release tag |
| environment | string (nullable) | "production", "staging", etc. |
| exception | json | Structured exception data (type, value, stacktrace) |
| breadcrumbs | json (nullable) | Trail of events leading up to the error |
| contexts | json (nullable) | OS, browser, device, runtime, etc. |
| tags | json (nullable) | Key-value pairs for filtering |
| extra | json (nullable) | Arbitrary extra data |
| user_context | json (nullable) | Info about the affected user |
| request_context | json (nullable) | HTTP method, URL, headers, body |
| sdk | json (nullable) | SDK name + version |
| raw_payload | json | Full original envelope for debugging |
| created_at | timestamp | |
---
## Phase 2 — Event Ingestion API
### 2.1 Sentry-Compatible Ingest Endpoint
Implement a `/api/{project_id}/store/` endpoint that accepts the Sentry event payload format. This allows using existing Sentry SDKs (for PHP, JS, Python, etc.) as clients without any custom SDK work.
- **Route:** `POST /api/{project_id}/store/`
- **Auth:** Validate DSN public key via `sentry_key` query param or `X-Sentry-Auth` header.
- **Rate limiting:** Per-project throttle (e.g. 100 events/minute).
- **No user auth required** — this is an SDK-to-server API.
### 2.2 Event Processing Pipeline
```
Ingest Request
→ Authenticate DSN key
→ Validate & normalize payload
→ Dispatch job to queue
→ Compute fingerprint (grouping)
→ Find or create Issue
→ Store Event
→ Update Issue counters (last_seen_at, times_seen)
→ Check if regression (status was "resolved")
→ Trigger notifications if needed
```
### 2.3 Fingerprinting / Grouping Strategy
Group events into issues using a fingerprint hash built from:
1. Exception type (class name)
2. Exception message (with variable parts stripped, e.g. IDs, numbers)
3. Top N frames of the stacktrace (file + function)
Users can override with a custom `fingerprint` array in the event payload (Sentry SDK standard).
### 2.4 DSN Format
```
https://<public_key>@<host>/api/<project_id>/store/
```
Clients configure this DSN in their Sentry SDK — no changes needed on the SDK side.
---
## Phase 3 — Web Dashboard
### 3.1 Layouts & Navigation
- Authenticated layout with sidebar navigation.
- Global project switcher in the sidebar/topbar.
- Sections: **Issues**, **Project Settings**, **Team Members**.
### 3.2 Issues List Page (`/projects/{project}/issues`)
- Table/list of issues sorted by last seen (most recent first).
- Columns: status badge, title, culprit, event count sparkline, times seen, last seen.
- Filters: status (unresolved/resolved/ignored), level, environment, date range.
- Search: full-text search on title and culprit.
- Bulk actions: resolve, ignore, mute, delete.
- Pagination (cursor-based for performance).
### 3.3 Issue Detail Page (`/projects/{project}/issues/{issue}`)
- Header: title, status controls (resolve / ignore / unresolve), level badge, first/last seen, times seen.
- **Stacktrace viewer:** collapsible frames, source context (if available), highlighted app frames vs vendor frames.
- **Breadcrumbs timeline:** chronological list of breadcrumb entries.
- **Tags panel:** key-value tag list with links to filter by tag.
- **Context panels:** device, OS, browser, runtime, user info, request details.
- **Events sub-list:** paginated list of individual occurrences with ability to view each one.
- **Activity:** timeline of status changes (resolved, regressed, etc.).
### 3.4 Event Detail Page (`/projects/{project}/issues/{issue}/events/{event}`)
- Full event data: raw JSON viewer + structured panels.
- Navigation: previous/next event within the issue.
### 3.5 Project Settings Page
- Rename project, change platform.
- DSN display with copy button.
- Regenerate keys.
- Delete project (with confirmation).
### 3.6 Dashboard / Overview Page
- Summary cards: total unresolved issues, events in last 24h, most active projects.
- Line chart: events over time (last 7/14/30 days).
---
## Phase 4 — Notifications & Alerting
### 4.1 Notification Channels
- **Email:** on first occurrence of a new issue (not on every event).
- **Database (in-app):** notification bell with unread count.
- Extensible to Slack/webhooks later.
### 4.2 Alert Rules (per project)
- Notify on: new issue, regression, spike (N events in M minutes).
- Per-user mute controls.
---
## Phase 5 — Polish & Advanced Features
### 5.1 Release Tracking
- Parse `release` field from events.
- Show which release introduced an issue.
- Mark issues as resolved-in-release; auto-regress if they appear in a newer release.
### 5.2 Environment Filtering
- Global environment selector (production / staging / etc.).
- Filter issues and events by environment.
### 5.3 Retention & Cleanup
- Artisan command / scheduled job to delete events older than N days (configurable).
- Keep issue summaries even after events are pruned.
### 5.4 Source Maps / Source Context (optional, stretch)
- Upload source maps for JS projects.
- Store and display source context lines around each stacktrace frame.
### 5.5 API Tokens & External API
- Personal API tokens for programmatic access.
- REST API for listing/resolving issues (useful for CI/CD integrations).
---
## Implementation Order & Milestones
### Milestone 1 — MVP (walking skeleton)
1. Install Breeze, set up auth.
2. Create migrations for `projects`, `issues`, `events`, `project_user`.
3. Build models with relationships.
4. Build the ingest endpoint (`POST /api/{project_id}/store/`).
5. Implement fingerprinting and the `ProcessEventJob`.
6. Build a minimal issues list page and issue detail page.
7. Test with a real Sentry SDK (e.g. `sentry/sentry-laravel` on another app pointing at this tracker).
### Milestone 2 — Usable product
8. Project CRUD and settings pages.
9. Team/member management (invite users to projects).
10. Stacktrace viewer with collapsible frames.
11. Breadcrumbs, tags, context panels on issue detail.
12. Filters, search, bulk actions on issues list.
13. Status management (resolve, ignore, mute, auto-regression detection).
### Milestone 3 — Notifications & dashboard
14. Email notifications on new issue / regression.
15. In-app notification system.
16. Overview dashboard with charts (events over time).
### Milestone 4 — Production-readiness
17. Rate limiting on ingest.
18. Event retention / cleanup scheduler.
19. Release tracking.
20. Environment filtering.
21. Performance optimization (indexes, query tuning, caching).
### Milestone 5 — Advanced (stretch)
22. Source map upload & processing.
23. API tokens & external REST API.
24. Webhook / Slack notifications.
25. CQRS-style read models for heavy dashboards (if needed).
---
## File / Directory Structure (planned)
```
app/
├── Http/
│ ├── Controllers/
│ │ ├── DashboardController.php
│ │ ├── ProjectController.php
│ │ ├── IssueController.php
│ │ ├── EventController.php
│ │ └── Api/
│ │ └── EventIngestController.php
│ ├── Middleware/
│ │ └── AuthenticateDsn.php
│ └── Requests/
│ ├── StoreProjectRequest.php
│ └── UpdateProjectRequest.php
├── Jobs/
│ └── ProcessEventJob.php
├── Models/
│ ├── Project.php
│ ├── Issue.php
│ └── Event.php
├── Notifications/
│ ├── NewIssueNotification.php
│ └── IssueRegressionNotification.php
├── Services/
│ ├── EventProcessor.php
│ ├── Fingerprinter.php
│ └── PayloadNormalizer.php
├── Policies/
│ ├── ProjectPolicy.php
│ └── IssuePolicy.php
database/
├── migrations/
│ ├── xxxx_create_projects_table.php
│ ├── xxxx_create_project_user_table.php
│ ├── xxxx_create_issues_table.php
│ └── xxxx_create_events_table.php
resources/
├── views/
│ ├── dashboard.blade.php
│ ├── projects/
│ │ ├── index.blade.php
│ │ ├── show.blade.php
│ │ ├── create.blade.php
│ │ └── edit.blade.php
│ ├── issues/
│ │ ├── index.blade.php
│ │ └── show.blade.php
│ └── events/
│ └── show.blade.php
routes/
├── web.php (dashboard, projects, issues, events)
└── api.php (event ingest endpoint)
```
---
## Key Design Decisions
1. **Sentry-compatible protocol** — Reuse the massive ecosystem of existing Sentry SDKs instead of building custom clients.
2. **Queue-based processing** — The ingest endpoint returns `202 Accepted` immediately and processes asynchronously, so SDK clients never block.
3. **ULID primary keys** — Sortable, time-ordered, no sequential ID leakage.
4. **Fingerprint-based grouping** — Same approach as Sentry/Bugsink; keeps the issues list manageable.
5. **Blade + Tailwind (no SPA)** — Keep complexity low; server-rendered pages with sprinkles of Alpine.js where needed (e.g. collapsible stacktrace frames, modals).
6. **SQLite for dev, Postgres for prod** — SQLite is fine for development and small-scale use. The JSON columns and indexing strategy work on both.