# Bugsink Error Tracking Setup and Usage Guide This document covers the complete setup and usage of Bugsink for error tracking in the Flyer Crawler application. ## Table of Contents - [What is Bugsink](#what-is-bugsink) - [Environments](#environments) - [Token Creation](#token-creation) - [MCP Integration](#mcp-integration) - [Application Integration](#application-integration) - [Logstash Integration](#logstash-integration) - [Using Bugsink](#using-bugsink) - [Common Workflows](#common-workflows) - [Troubleshooting](#troubleshooting) --- ## What is Bugsink Bugsink is a lightweight, self-hosted error tracking platform that is fully compatible with the Sentry SDK ecosystem. We use Bugsink instead of Sentry SaaS or self-hosted Sentry for several reasons: | Aspect | Bugsink | Self-Hosted Sentry | | ----------------- | -------------------------- | -------------------------------- | | Resource Usage | Single process, ~256MB RAM | 16GB+ RAM, Kafka, ClickHouse | | Deployment | Simple pip/binary install | Docker Compose with 20+ services | | SDK Compatibility | Full Sentry SDK support | Full Sentry SDK support | | Database | PostgreSQL or SQLite | PostgreSQL + ClickHouse | | Cost | Free, self-hosted | Free, self-hosted | | Maintenance | Minimal | Significant | **Key Benefits:** 1. **Sentry SDK Compatibility**: Uses the same `@sentry/node` and `@sentry/react` SDKs as Sentry 2. **Self-Hosted**: All error data stays on our infrastructure 3. **Lightweight**: Runs alongside the application without significant overhead 4. **MCP Integration**: AI tools (Claude Code) can query errors via the bugsink-mcp server **Architecture Decision**: See [ADR-015: Application Performance Monitoring and Error Tracking](../adr/0015-application-performance-monitoring-and-error-tracking.md) for the full rationale. --- ## Environments ### Dev Container (Local Development) | Item | Value | | ---------------- | ----------------------------------------------------------------- | | Web UI | `https://localhost:8443` (nginx proxy) | | Internal URL | `http://localhost:8000` (direct) | | Credentials | `admin@localhost` / `admin` | | Backend Project | Project ID 1 - `Backend API (Dev)` | | Frontend Project | Project ID 2 - `Frontend (Dev)` | | Infra Project | Project ID 4 - `Infrastructure (Dev)` | | Backend DSN | `http://@localhost:8000/1` | | Frontend DSN | `https://@localhost/bugsink-api/2` (via nginx proxy) | | Infra DSN | `http://@localhost:8000/4` (Logstash only) | | Database | `postgresql://bugsink:bugsink_dev_password@postgres:5432/bugsink` | **Important:** The Frontend DSN uses an nginx proxy (`/bugsink-api/`) because the browser cannot reach `localhost:8000` directly (container-internal port). See [Frontend Nginx Proxy](#frontend-nginx-proxy) for details. **Configuration Files:** | File | Purpose | | ------------------------------ | ------------------------------------------------------- | | `compose.dev.yml` | Initial DSNs using `127.0.0.1:8000` (container startup) | | `.env.local` | **OVERRIDES** compose.dev.yml (app runtime) | | `docker/nginx/dev.conf` | Nginx proxy for Bugsink API (frontend error reporting) | | `docker/logstash/bugsink.conf` | Log routing to Backend/Infrastructure projects | **Note:** `.env.local` takes precedence over `compose.dev.yml` environment variables. ### Production | Item | Value | | ---------------- | --------------------------------------- | | Web UI | `https://bugsink.projectium.com` | | Credentials | Managed separately (not shared in docs) | | Backend Project | `flyer-crawler-backend` | | Frontend Project | `flyer-crawler-frontend` | | Infra Project | `flyer-crawler-infrastructure` | **Bugsink Projects:** | Project Slug | Type | Environment | | --------------------------------- | -------- | ----------- | | flyer-crawler-backend | Backend | Production | | flyer-crawler-backend-test | Backend | Test | | flyer-crawler-frontend | Frontend | Production | | flyer-crawler-frontend-test | Frontend | Test | | flyer-crawler-infrastructure | Infra | Production | | flyer-crawler-test-infrastructure | Infra | Test | --- ## Token Creation Bugsink 2.0.11 does **NOT** have a "Settings > API Keys" menu in the UI. API tokens must be created via Django management command. ### Dev Container Token Run this command from the Windows host (Git Bash or PowerShell): ```bash MSYS_NO_PATHCONV=1 podman exec -e DATABASE_URL=postgresql://bugsink:bugsink_dev_password@postgres:5432/bugsink -e SECRET_KEY=dev-bugsink-secret-key-minimum-50-characters-for-security flyer-crawler-dev sh -c 'cd /opt/bugsink/conf && DJANGO_SETTINGS_MODULE=bugsink_conf PYTHONPATH=/opt/bugsink/conf:/opt/bugsink/lib/python3.10/site-packages /opt/bugsink/bin/python -m django create_auth_token' ``` **Output:** A 40-character lowercase hex token (e.g., `a609c2886daa4e1e05f1517074d7779a5fb49056`) ### Production Token User executes this command on the production server: ```bash cd /opt/bugsink && bugsink-manage create_auth_token ``` **Output:** Same format - 40-character hex token. ### Token Storage | Environment | Storage Location | Notes | | ----------- | ----------------------------- | ---------------------- | | Dev | `.mcp.json` (project-level) | Not committed to git | | Production | Gitea secrets + settings.json | `BUGSINK_TOKEN` secret | --- ## MCP Integration The bugsink-mcp server allows Claude Code and other AI tools to query Bugsink for error information. ### Installation ```bash # Clone the MCP server cd d:\gitea git clone https://github.com/j-shelfwood/bugsink-mcp.git cd bugsink-mcp npm install npm run build ``` ### Configuration **IMPORTANT:** Localhost MCP servers must use project-level `.mcp.json` due to a known Claude Code loader issue. See [BUGSINK-MCP-TROUBLESHOOTING.md](../BUGSINK-MCP-TROUBLESHOOTING.md) for details. #### Production (Global settings.json) Location: `~/.claude/settings.json` (or `C:\Users\\.claude\settings.json`) ```json { "mcpServers": { "bugsink": { "command": "node", "args": ["d:\\gitea\\bugsink-mcp\\dist\\index.js"], "env": { "BUGSINK_URL": "https://bugsink.projectium.com", "BUGSINK_TOKEN": "<40-char-hex-token>" } } } } ``` #### Dev Container (Project-level .mcp.json) Location: Project root `.mcp.json` ```json { "mcpServers": { "localerrors": { "command": "node", "args": ["d:\\gitea\\bugsink-mcp\\dist\\index.js"], "env": { "BUGSINK_URL": "http://127.0.0.1:8000", "BUGSINK_TOKEN": "<40-char-hex-token>" } } } } ``` ### Environment Variables The bugsink-mcp package requires exactly TWO environment variables: | Variable | Description | Required | | --------------- | ----------------------- | -------- | | `BUGSINK_URL` | Bugsink instance URL | Yes | | `BUGSINK_TOKEN` | API token (40-char hex) | Yes | **Common Mistakes:** - Using `BUGSINK_API_TOKEN` (wrong - use `BUGSINK_TOKEN`) - Including `BUGSINK_ORG_SLUG` (not used by the package) ### Available MCP Tools | Tool | Purpose | | ----------------- | ------------------------------------ | | `test_connection` | Verify MCP server can reach Bugsink | | `list_projects` | List all projects in the instance | | `get_project` | Get project details including DSN | | `list_issues` | List issues for a project | | `get_issue` | Get detailed issue information | | `list_events` | List individual error occurrences | | `get_event` | Get full event details with context | | `get_stacktrace` | Get pre-rendered Markdown stacktrace | | `list_releases` | List releases for a project | | `create_release` | Create a new release | **Tool Prefixes:** - Production: `mcp__bugsink__*` - Dev Container: `mcp__localerrors__*` ### Verifying MCP Connection After configuration, restart Claude Code and test: ```typescript // Production mcp__bugsink__test_connection(); // Expected: "Connection successful: Connected successfully. Found N project(s)." // Dev Container mcp__localerrors__test_connection(); // Expected: "Connection successful: Connected successfully. Found N project(s)." ``` --- ## Application Integration ### Backend (Express/Node.js) **File:** `src/services/sentry.server.ts` The backend uses `@sentry/node` SDK v8+ to capture errors: ```typescript import * as Sentry from '@sentry/node'; import { config, isSentryConfigured, isProduction, isTest } from '../config/env'; export function initSentry(): void { if (!isSentryConfigured || isTest) return; Sentry.init({ dsn: config.sentry.dsn, environment: config.sentry.environment || config.server.nodeEnv, debug: config.sentry.debug, tracesSampleRate: 0, // Performance monitoring disabled beforeSend(event, hint) { // Custom filtering logic return event; }, }); } ``` **Key Functions:** | Function | Purpose | | ----------------------- | -------------------------------------------- | | `initSentry()` | Initialize SDK at application startup | | `captureException()` | Manually capture caught errors | | `captureMessage()` | Log non-exception events | | `setUser()` | Set user context after authentication | | `addBreadcrumb()` | Add navigation/action breadcrumbs | | `getSentryMiddleware()` | Get Express middleware for automatic capture | **Integration in server.ts:** ```typescript // At the very top of server.ts, before other imports import { initSentry, getSentryMiddleware } from './services/sentry.server'; initSentry(); // After Express app creation const { requestHandler, errorHandler } = getSentryMiddleware(); app.use(requestHandler); // ... routes ... // Before final error handler app.use(errorHandler); ``` ### Frontend (React) **File:** `src/services/sentry.client.ts` The frontend uses `@sentry/react` SDK: ```typescript import * as Sentry from '@sentry/react'; import config from '../config'; export function initSentry(): void { if (!config.sentry.dsn || !config.sentry.enabled) return; Sentry.init({ dsn: config.sentry.dsn, environment: config.sentry.environment, debug: config.sentry.debug, tracesSampleRate: 0, integrations: [ Sentry.breadcrumbsIntegration({ console: true, dom: true, fetch: true, history: true, xhr: true, }), ], beforeSend(event) { // Filter browser extension errors if ( event.exception?.values?.[0]?.stacktrace?.frames?.some((frame) => frame.filename?.includes('extension://'), ) ) { return null; } return event; }, }); } ``` **Client Configuration (src/config.ts):** ```typescript const config = { sentry: { dsn: import.meta.env.VITE_SENTRY_DSN, environment: import.meta.env.VITE_SENTRY_ENVIRONMENT || import.meta.env.MODE, debug: import.meta.env.VITE_SENTRY_DEBUG === 'true', enabled: import.meta.env.VITE_SENTRY_ENABLED !== 'false', }, }; ``` ### Environment Variables **Backend (src/config/env.ts):** | Variable | Description | Default | | -------------------- | ------------------------------ | ---------- | | `SENTRY_DSN` | Sentry-compatible DSN | (optional) | | `SENTRY_ENABLED` | Enable/disable error reporting | `true` | | `SENTRY_ENVIRONMENT` | Environment tag | NODE_ENV | | `SENTRY_DEBUG` | Enable SDK debug logging | `false` | **Frontend (Vite):** | Variable | Description | | ------------------------- | ------------------------------- | | `VITE_SENTRY_DSN` | Frontend DSN (separate project) | | `VITE_SENTRY_ENVIRONMENT` | Environment tag | | `VITE_SENTRY_DEBUG` | Enable SDK debug logging | | `VITE_SENTRY_ENABLED` | Enable/disable error reporting | --- ## Frontend Nginx Proxy The frontend Sentry SDK runs in the browser, which cannot directly reach `localhost:8000` (the Bugsink container-internal port). To solve this, we use an nginx proxy. ### How It Works ```text Browser --HTTPS--> https://localhost/bugsink-api/2/store/ | v (nginx proxy) http://localhost:8000/api/2/store/ | v Bugsink (internal) ``` ### Nginx Configuration Location: `docker/nginx/dev.conf` ```nginx # Proxy Bugsink Sentry API for frontend error reporting location /bugsink-api/ { proxy_pass http://localhost:8000/api/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Allow large error payloads with stack traces client_max_body_size 10M; # Timeouts for error reporting proxy_connect_timeout 10s; proxy_send_timeout 30s; proxy_read_timeout 30s; } ``` ### Frontend DSN Format ```bash # .env.local # Uses nginx proxy path instead of direct port VITE_SENTRY_DSN=https://@localhost/bugsink-api/2 ``` ### Testing Frontend Error Reporting 1. Open browser console at `https://localhost` 2. Trigger a test error: ```javascript throw new Error('Test frontend error from browser'); ``` 3. Check Bugsink Frontend (Dev) project for the error 4. Verify browser console shows Sentry SDK activity (if VITE_SENTRY_DEBUG=true) --- ## Logstash Integration Logstash aggregates logs from multiple sources and forwards error patterns to Bugsink. **Note:** See [ADR-015](../adr/0015-application-performance-monitoring-and-error-tracking.md) for the full architecture. ### 3-Project Architecture Logstash routes errors to different Bugsink projects based on log source: | Project | ID | Receives | | -------------------- | --- | --------------------------------------------- | | Backend API (Dev) | 1 | Pino app errors, PostgreSQL errors | | Frontend (Dev) | 2 | Browser errors (via Sentry SDK, not Logstash) | | Infrastructure (Dev) | 4 | Redis warnings, NGINX errors, Vite errors | ### Log Sources | Source | Log Path | Project Destination | Error Detection | | ---------- | --------------------------- | ------------------- | ------------------------- | | PM2 API | `/var/log/pm2/api-*.log` | Backend (1) | level >= 50 (error/fatal) | | PM2 Worker | `/var/log/pm2/worker-*.log` | Backend (1) | level >= 50 (error/fatal) | | PM2 Vite | `/var/log/pm2/vite-*.log` | Infrastructure (4) | error keyword patterns | | PostgreSQL | `/var/log/postgresql/*.log` | Backend (1) | ERROR/FATAL log levels | | Redis | `/var/log/redis/*.log` | Infrastructure (4) | WARNING level (`#`) | | NGINX | `/var/log/nginx/error.log` | Infrastructure (4) | error/crit/alert/emerg | ### Pipeline Configuration **Location:** `/etc/logstash/conf.d/bugsink.conf` (or `docker/logstash/bugsink.conf` in project) The configuration: 1. **Inputs**: Reads from PM2 logs, PostgreSQL logs, Redis logs, NGINX logs 2. **Filters**: Detects errors and assigns tags based on log type 3. **Outputs**: Routes to appropriate Bugsink project based on log source **Key Routing Logic:** ```ruby # Infrastructure logs -> Project 4 if "error" in [tags] and ([type] == "redis" or [type] == "nginx_error" or [type] == "pm2_vite") { http { url => "http://localhost:8000/api/4/store/" ... } } # Backend logs -> Project 1 else if "error" in [tags] and ([type] in ["pm2_api", "pm2_worker", "pino", "postgres"]) { http { url => "http://localhost:8000/api/1/store/" ... } } ``` ### Benefits 1. **Separation of Concerns**: Application errors separate from infrastructure issues 2. **Secondary Capture Path**: Catches errors before SDK initialization 3. **Log-Based Errors**: Captures errors that don't throw exceptions 4. **Infrastructure Monitoring**: Redis, NGINX, build tooling issues 5. **Historical Analysis**: Process existing log files --- ## Using Bugsink ### Accessing the Web UI **Dev Container:** 1. Open `https://localhost:8443` in your browser 2. Accept the self-signed certificate warning 3. Login with `admin@localhost` / `admin` **Production:** 1. Open `https://bugsink.projectium.com` 2. Login with your credentials ### Projects and Teams Bugsink organizes errors into projects: | Concept | Description | | ------- | ---------------------------------------------- | | Team | Group of projects (e.g., "Flyer Crawler") | | Project | Single application/service | | DSN | Data Source Name - unique key for each project | To view projects: 1. Click the project dropdown in the top navigation 2. Or use MCP: `mcp__bugsink__list_projects()` ### Viewing Issues **Issues** represent grouped error occurrences. Multiple identical errors are deduplicated into a single issue. **Issue List View:** - Navigate to a project - Issues are sorted by last occurrence - Each issue shows: title, count, first/last seen **Issue Detail View:** - Click an issue to see full details - View aggregated statistics - See list of individual events - Access full stacktrace ### Viewing Events **Events** are individual error occurrences. **Event Information:** - Full stacktrace - Request context (URL, method, headers) - User context (if set) - Breadcrumbs (actions leading to error) - Tags and extra data **Via MCP:** ```typescript // List events for an issue mcp__bugsink__list_events({ issue_id: 'uuid-here' }); // Get full event details mcp__bugsink__get_event({ event_id: 'uuid-here' }); // Get readable stacktrace mcp__bugsink__get_stacktrace({ event_id: 'uuid-here' }); ``` ### Stacktraces and Context Stacktraces show the call stack at the time of error: **Via Web UI:** - Open an event - Expand the "Exception" section - Click frames to see source code context **Via MCP:** - `get_stacktrace` returns pre-rendered Markdown - Includes file paths, line numbers, function names ### Filtering and Searching **Web UI Filters:** - By status: unresolved, resolved, muted - By date range - By release version - By environment **MCP Filtering:** ```typescript // Filter by status mcp__bugsink__list_issues({ project_id: 1, status: 'unresolved', limit: 25, }); // Sort options mcp__bugsink__list_issues({ project_id: 1, sort: 'last_seen', // or "digest_order" order: 'desc', // or "asc" }); ``` ### Release Tracking Releases help identify which version introduced or fixed issues. **Creating Releases:** ```typescript mcp__bugsink__create_release({ project_id: 1, version: '1.2.3', }); ``` **Viewing Releases:** ```typescript mcp__bugsink__list_releases({ project_id: 1 }); ``` --- ## Common Workflows ### Investigating Production Errors 1. **Check for new errors** (via MCP): ```typescript mcp__bugsink__list_issues({ project_id: 1, status: 'unresolved', sort: 'last_seen', limit: 10, }); ``` 2. **Get issue details**: ```typescript mcp__bugsink__get_issue({ issue_id: 'uuid' }); ``` 3. **View stacktrace**: ```typescript mcp__bugsink__list_events({ issue_id: 'uuid', limit: 1 }); mcp__bugsink__get_stacktrace({ event_id: 'event-uuid' }); ``` 4. **Examine the code**: Use the file path and line numbers from the stacktrace to locate the issue in the codebase. ### Tracking Down Bugs 1. **Identify error patterns**: - Group similar errors by message or location - Check occurrence counts and frequency 2. **Examine request context**: ```typescript mcp__bugsink__get_event({ event_id: 'uuid' }); ``` Look for: URL, HTTP method, request body, user info 3. **Review breadcrumbs**: Understand the sequence of actions leading to the error. 4. **Correlate with logs**: Use the request ID from the event to search application logs. ### Monitoring Error Rates 1. **Check issue counts**: Compare event counts over time 2. **Watch for regressions**: Resolved issues that reopen 3. **Track new issues**: Filter by "first seen" date ### Dev Container Debugging 1. **Access local Bugsink**: `https://localhost:8443` 2. **Trigger a test error**: ```bash curl -X POST http://localhost:3001/api/test/error ``` 3. **View in Bugsink**: Check the dev project for the captured error 4. **Query via MCP**: ```typescript mcp__localerrors__list_issues({ project_id: 1 }); ``` --- ## Troubleshooting ### MCP Server Not Available **Symptoms:** - `mcp__localerrors__*` tools return "No such tool available" - `mcp__bugsink__*` works but `mcp__localerrors__*` fails **Solutions:** 1. **Check configuration location**: Localhost servers must use project-level `.mcp.json`, not global settings.json 2. **Verify token variable name**: Use `BUGSINK_TOKEN`, not `BUGSINK_API_TOKEN` 3. **Test manually**: ```bash cd d:\gitea\bugsink-mcp set BUGSINK_URL=http://localhost:8000 set BUGSINK_TOKEN= node dist/index.js ``` Expected: `Bugsink MCP server started` 4. **Full restart**: Close VS Code completely, restart See [BUGSINK-MCP-TROUBLESHOOTING.md](../BUGSINK-MCP-TROUBLESHOOTING.md) for detailed troubleshooting. ### Connection Refused to localhost:8000 **Cause:** Dev container Bugsink service not running **Solutions:** 1. **Check container status**: ```bash podman exec flyer-crawler-dev systemctl status bugsink ``` 2. **Start the service**: ```bash podman exec flyer-crawler-dev systemctl start bugsink ``` 3. **Check logs**: ```bash podman exec flyer-crawler-dev journalctl -u bugsink -n 50 ``` ### Errors Not Appearing in Bugsink **Backend:** 1. **Check DSN**: Verify `SENTRY_DSN` environment variable is set 2. **Check enabled flag**: `SENTRY_ENABLED` should be `true` 3. **Check test environment**: Sentry is disabled in `NODE_ENV=test` **Frontend:** 1. **Check Vite env**: `VITE_SENTRY_DSN` must be set 2. **Verify initialization**: Check browser console for Sentry init message 3. **Check filtering**: `beforeSend` may be filtering the error ### HTTPS Certificate Warnings **Dev Container:** Self-signed certificates are expected. Accept the warning. **Production:** Should use valid certificates. If warnings appear, check certificate expiration. ### Token Invalid or Expired **Symptoms:** MCP returns authentication errors **Solutions:** 1. **Regenerate token**: Use Django management command (see [Token Creation](#token-creation)) 2. **Update configuration**: Put new token in `.mcp.json` or `settings.json` 3. **Restart Claude Code**: Required after config changes ### Bugsink Database Issues **Symptoms:** 500 errors in Bugsink UI, connection refused **Dev Container:** ```bash # Check PostgreSQL podman exec flyer-crawler-dev pg_isready -U bugsink -d bugsink -h postgres # Check database exists podman exec flyer-crawler-dev psql -U postgres -h postgres -c "\l" | grep bugsink ``` **Production** (user executes on server): ```bash cd /opt/bugsink && bugsink-manage check ``` ### PostgreSQL Sequence Out of Sync (Duplicate Key Errors) **Symptoms:** - Bugsink throws `duplicate key value violates unique constraint "projects_project_pkey"` - Error detail shows: `Key (id)=(1) already exists` - New projects or other entities fail to create **Root Cause:** PostgreSQL sequences can become out of sync with actual data after: - Manual data insertion or database seeding - Restoring from backup - Copying data between environments The sequence generates IDs that already exist in the table. **Diagnosis:** ```bash # Dev Container - Check sequence vs max ID podman exec flyer-crawler-dev psql -U bugsink -h postgres -d bugsink -c " SELECT (SELECT MAX(id) FROM projects_project) as max_id, (SELECT last_value FROM projects_project_id_seq) as seq_last_value, CASE WHEN (SELECT MAX(id) FROM projects_project) <= (SELECT last_value FROM projects_project_id_seq) THEN 'OK' ELSE 'OUT OF SYNC - Needs reset' END as status; " # Production (user executes on server) cd /opt/bugsink && bugsink-manage dbshell # Then run: SELECT MAX(id) as max_id, (SELECT last_value FROM projects_project_id_seq) as seq_value FROM projects_project; ``` **Solution:** Reset the sequence to the maximum existing ID: ```bash # Dev Container podman exec flyer-crawler-dev psql -U bugsink -h postgres -d bugsink -c " SELECT setval('projects_project_id_seq', COALESCE((SELECT MAX(id) FROM projects_project), 1), true); " # Production (user executes on server) cd /opt/bugsink && bugsink-manage dbshell # Then run: SELECT setval('projects_project_id_seq', COALESCE((SELECT MAX(id) FROM projects_project), 1), true); ``` **Verification:** After running the fix, verify: ```bash # Next ID should be max_id + 1 podman exec flyer-crawler-dev psql -U bugsink -h postgres -d bugsink -c " SELECT nextval('projects_project_id_seq') - 1 as current_seq_value; " ``` **Prevention:** When manually inserting data or restoring backups, always reset sequences: ```sql -- Generic pattern for any table/sequence SELECT setval('SEQUENCE_NAME', COALESCE((SELECT MAX(id) FROM TABLE_NAME), 1), true); -- Common Bugsink sequences that may need reset: SELECT setval('projects_project_id_seq', COALESCE((SELECT MAX(id) FROM projects_project), 1), true); SELECT setval('teams_team_id_seq', COALESCE((SELECT MAX(id) FROM teams_team), 1), true); SELECT setval('releases_release_id_seq', COALESCE((SELECT MAX(id) FROM releases_release), 1), true); ``` ### Logstash Level Field Constraint Violation **Symptoms:** - Bugsink errors: `value too long for type character varying(7)` - Errors in Backend API project from Logstash - Log shows `%{sentry_level}` literal string being sent **Root Cause:** Logstash sends the literal placeholder `%{sentry_level}` (16 characters) to Bugsink when: - No error pattern is detected in the log message - The `sentry_level` field is not properly initialized - Bugsink's `level` column has a `varchar(7)` constraint Valid Sentry levels are: `fatal`, `error`, `warning`, `info`, `debug` (all <= 7 characters). **Diagnosis:** ```bash # Check for recent level constraint errors in Bugsink # Via MCP: mcp__localerrors__list_issues({ project_id: 1, status: 'unresolved' }) # Or check Logstash logs for HTTP 500 responses podman exec flyer-crawler-dev cat /var/log/logstash/logstash.log | grep "500" ``` **Solution:** The fix requires updating the Logstash configuration (`docker/logstash/bugsink.conf`) to: 1. Validate `sentry_level` is not nil, empty, or contains placeholder text 2. Set a default value of "error" for any error-tagged event without a valid level 3. Normalize levels to lowercase **Key filter block (Ruby):** ```ruby ruby { code => ' level = event.get("sentry_level") # Check if level is invalid (nil, empty, contains placeholder, or invalid value) if level.nil? || level.to_s.empty? || level.to_s.include?("%{") || level.to_s.length > 7 # Default to "error" for error-tagged events, "info" otherwise if event.get("tags")&.include?("error") event.set("sentry_level", "error") else event.set("sentry_level", "info") end else # Normalize to lowercase and validate normalized = level.to_s.downcase valid_levels = ["fatal", "error", "warning", "info", "debug"] unless valid_levels.include?(normalized) normalized = "error" end event.set("sentry_level", normalized) end ' } ``` **Verification:** After applying the fix: 1. Restart Logstash: `podman exec flyer-crawler-dev systemctl restart logstash` 2. Generate a test error and verify it appears in Bugsink without level errors 3. Check no new "value too long" errors appear in the project ### CSRF Verification Failed **Symptoms:** "CSRF verification failed. Request aborted." error when performing actions in Bugsink UI (resolving issues, changing settings, etc.) **Root Cause:** Django 4.0+ requires `CSRF_TRUSTED_ORIGINS` to be explicitly configured for HTTPS POST requests. The error occurs because: 1. Bugsink is accessed via `https://localhost:8443` (nginx HTTPS proxy) 2. Django's CSRF protection validates the `Origin` header against `CSRF_TRUSTED_ORIGINS` 3. Without explicit configuration, Django rejects POST requests from HTTPS origins **Why localhost vs 127.0.0.1 Matters:** - `localhost` and `127.0.0.1` are treated as DIFFERENT origins by browsers - If you access Bugsink via `https://localhost:8443`, Django must trust `https://localhost:8443` - If you access via `https://127.0.0.1:8443`, Django must trust `https://127.0.0.1:8443` - The fix includes BOTH to allow either access pattern **Configuration (Already Applied):** The Bugsink Django configuration in `Dockerfile.dev` includes: ```python # CSRF Trusted Origins (Django 4.0+ requires full origin for HTTPS POST requests) CSRF_TRUSTED_ORIGINS = [ "https://localhost:8443", "https://127.0.0.1:8443", "http://localhost:8000", "http://127.0.0.1:8000", ] # HTTPS proxy support (nginx reverse proxy on port 8443) SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") ``` **Verification:** ```bash # Verify CSRF_TRUSTED_ORIGINS is configured podman exec flyer-crawler-dev sh -c 'cat /opt/bugsink/conf/bugsink_conf.py | grep -A 6 CSRF_TRUSTED' # Expected output: # CSRF_TRUSTED_ORIGINS = [ # "https://localhost:8443", # "https://127.0.0.1:8443", # "http://localhost:8000", # "http://127.0.0.1:8000", # ] ``` **If Issue Persists After Fix:** 1. **Rebuild the container image** (configuration is baked into the image): ```bash podman-compose -f compose.dev.yml down podman build -f Dockerfile.dev -t localhost/flyer-crawler-dev:latest . podman-compose -f compose.dev.yml up -d ``` 2. **Clear browser cookies** for localhost:8443 3. **Check nginx X-Forwarded-Proto header** - the nginx config must set this header for Django to recognize HTTPS: ```bash podman exec flyer-crawler-dev cat /etc/nginx/sites-available/bugsink | grep X-Forwarded-Proto # Should show: proxy_set_header X-Forwarded-Proto $scheme; ``` --- ## Related Documentation - [ADR-015: Application Performance Monitoring and Error Tracking](../adr/0015-application-performance-monitoring-and-error-tracking.md) - [BUGSINK-MCP-TROUBLESHOOTING.md](../BUGSINK-MCP-TROUBLESHOOTING.md) - [DEV-CONTAINER-BUGSINK.md](../DEV-CONTAINER-BUGSINK.md) - [BUGSINK-SYNC.md](../BUGSINK-SYNC.md) - Bugsink to Gitea issue synchronization - [bugsink-mcp Repository](https://github.com/j-shelfwood/bugsink-mcp) - [Bugsink Documentation](https://www.bugsink.com/docs/) - [@sentry/node Documentation](https://docs.sentry.io/platforms/javascript/guides/node/) - [@sentry/react Documentation](https://docs.sentry.io/platforms/javascript/guides/react/)