{"slug": "beacon-a-self-hosted-error-tracking-and-llm-observability-in-one-place", "title": "Beacon: A self-hosted error tracking and LLM observability in one place", "summary": "Beacon, a self-hosted error tracking and LLM observability tool from developer Tboworst, ingests errors from any service, groups them by root cause, and displays them in a live terminal dashboard or browser dashboard, with no third-party service or data leaving the user's infrastructure. The tool, available on GitHub, supports Node.js and Python SDKs, tracks LLM calls with token usage and cost, and integrates with Slack alerts, requiring Node.js 18+ for the web dashboard and running on port 7000 by default.", "body_md": "**Self-hosted error tracking and monitoring for developers who want control.**\n\nBeacon ingests errors from any service, groups them by root cause, and surfaces everything in a live terminal dashboard — no third-party service, no data leaving your infrastructure, no monthly bill.\n\nThink lightweight Sentry, built for the terminal, owned by you.\n\n## Beacon_updated_WebUI_compressed.mp4\n\n```\ngit clone https://github.com/Tboworst/beacon.git\ncd beacon\ncp .env.example .env   # add your API key and Slack webhook\ndocker-compose up\n```\n\nServer is running at `http://localhost:7000`\n\n.\n\nOpen the dashboard in a separate terminal:\n\n```\npip install textual\npython3 start_dashboard.py\ngit clone https://github.com/Tboworst/beacon.git\ncd beacon\npip install -r requirements.txt\ncp .env.example .env\npython3 start_server.py      # terminal 1\npython3 start_dashboard.py   # terminal 2\n```\n\nBeacon also ships a browser dashboard — same data as the TUI, served straight from the ingest server. Issues, LLM calls, alerts and deploys, with search, environment filters, resolve / reopen, and GitHub issue creation.\n\nBuild it once, then the server does the rest (requires Node.js 18+; Docker users skip this — the image builds it automatically):\n\n```\ncd web\nnpm install\nnpm run build\n```\n\nStart the server as usual and open `http://localhost:7000`\n\n:\n\n```\npython3 start_server.py\n```\n\nFor frontend development, run the Vite dev server alongside — it proxies `/api`\n\nto the ingest server and hot-reloads:\n\n```\npython3 start_server.py      # terminal 1\ncd web && npm run dev        # terminal 2 → http://localhost:5173\n```\n\nThe TUI keeps working unchanged — both dashboards read the same `beacon.db`\n\n.\n\nmacOS note: AirPlay Receiver also listens on port 7000. If\n\n`http://localhost:7000`\n\nmisbehaves, use`http://127.0.0.1:7000`\n\n.\n\nCopy `sdk/node/`\n\ninto your project (or `npm install beacon-monitor`\n\nonce published):\n\n``` js\nconst beacon = require('./beacon');\n\nbeacon.init({\n  endpoint: 'http://your-beacon-server:7000/ingest',\n  service: 'my-app',\n  environment: 'production',\n  apiKey: 'your-secret-key-here',\n});\n```\n\nUnhandled exceptions and promise rejections are captured automatically. For caught errors:\n\n```\ntry {\n  riskyOperation();\n} catch (err) {\n  beacon.capture(err);\n}\n```\n\nLLM call tracking:\n\n``` js\nconst t0 = Date.now();\ntry {\n  const res = await openai.chat.completions.create({ model: 'gpt-4o', messages });\n  beacon.captureLlm({\n    model: 'gpt-4o',\n    inputTokens: res.usage.prompt_tokens,\n    outputTokens: res.usage.completion_tokens,\n    latencyMs: Date.now() - t0,\n    costUsd: res.usage.prompt_tokens * 0.000005 + res.usage.completion_tokens * 0.000015,\n    feature: 'document-summarizer',\n  });\n} catch (err) {\n  beacon.captureLlm({ model: 'gpt-4o', inputTokens: 0, outputTokens: 0,\n                      latencyMs: Date.now() - t0, costUsd: 0,\n                      feature: 'document-summarizer', error: err });\n}\n```\n\nZero dependencies — uses Node's built-in `http`\n\n/`https`\n\nmodules only.\n\nInstall in your app:\n\n```\npip install requests\n```\n\nCopy `sdk/python/beacon/`\n\ninto your project, then add two lines to your entry point:\n\n``` python\nimport beacon\n\nbeacon.init(\n    endpoint=\"http://your-beacon-server:7000/ingest\",\n    service=\"my-app\",\n    environment=\"production\",\n    api_key=\"your-secret-key-here\"   # matches BEACON_API_KEY in .env\n)\n```\n\nEvery unhandled exception is now automatically captured. For handled exceptions:\n\n```\ntry:\n    risky_operation()\nexcept Exception as e:\n    beacon.capture(e)\n```\n\n| Variable | Description |\n|---|---|\n`BEACON_API_KEY` |\nSecret key required on all ingest requests. If not set, server accepts all requests (local dev mode). |\n`SLACK_WEBHOOK_URL` |\nSlack incoming webhook URL. Alerts fire when an error group crosses the threshold. |\n\n```\nraw errors:  \"NoneType has no attribute 'email'\"\n             \"NoneType has no attribute 'username'\"\n\nfingerprint: AttributeError\n             NoneType has no attribute <attr>        ← normalized\n             handle_request → get_current_user → find_user_by_token\n\nresult:      same group. one bug. one row.\n```\n\nFingerprint = hash of exception type + normalized message + function call chain. Line numbers are ignored — they change on every reformat. Function names are stable.\n\n```\nbeacon/\n├── core/               ← ingest server, storage, fingerprinting (Python → Go)\n├── dashboard/          ← live TUI dashboard (Textual)\n├── web/                ← web dashboard (Vite + React, served by the ingest server)\n├── sdk/\n│   └── python/         ← Python SDK\n├── start_server.py     ← python3 start_server.py\n├── start_dashboard.py  ← python3 start_dashboard.py\n├── docker-compose.yml\n└── requirements.txt\n```\n\nAny service can send errors directly over HTTP — no SDK required:\n\n```\ncurl -X POST http://localhost:7000/ingest \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Api-Key: your-secret-key-here\" \\\n  -d '{\n    \"timestamp\": \"2024-01-15T10:23:45Z\",\n    \"exception_type\": \"AttributeError\",\n    \"message\": \"NoneType object has no attribute email\",\n    \"stack_trace\": [\n      {\"function\": \"handle_request\", \"file\": \"/app/server.py\", \"line\": 42},\n      {\"function\": \"get_current_user\", \"file\": \"/app/auth.py\", \"line\": 87}\n    ]\n  }'\n```\n\n- Deploy markers — correlate errors with deploys\n- Environment tagging — separate prod vs staging\n- Spike detection — alert on rate of increase, not just total count\n- Regression alerts — error quiet for 7 days that suddenly fires again\n- Resolve / ignore groups from the TUI\n- GitHub issue creation from any error group\n- Node.js SDK\n- Go rewrite of the core server with Redis hot path\n\n| Layer | Tech |\n|---|---|\n| Ingest server | Python + Flask |\n| Storage | SQLite |\n| Dashboard | Python + Textual |\n| Alerts | Slack webhooks |\n| Containerisation | Docker |\n\nSentry is excellent. Beacon is for when you want:\n\n- No data leaving your network\n- No per-event pricing at scale\n- A terminal-native workflow\n- Something you can read, modify, and own completely\n\nBuilt in public. Stars appreciated.", "url": "https://wpnews.pro/news/beacon-a-self-hosted-error-tracking-and-llm-observability-in-one-place", "canonical_source": "https://github.com/Tboworst/beacon", "published_at": "2026-08-09 19:42:24+00:00", "updated_at": "2026-08-09 20:04:50.008620+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "large-language-models"], "entities": ["Beacon", "Tboworst", "GitHub", "Sentry", "Slack", "Node.js", "Python", "Vite"], "alternates": {"html": "https://wpnews.pro/news/beacon-a-self-hosted-error-tracking-and-llm-observability-in-one-place", "markdown": "https://wpnews.pro/news/beacon-a-self-hosted-error-tracking-and-llm-observability-in-one-place.md", "text": "https://wpnews.pro/news/beacon-a-self-hosted-error-tracking-and-llm-observability-in-one-place.txt", "jsonld": "https://wpnews.pro/news/beacon-a-self-hosted-error-tracking-and-llm-observability-in-one-place.jsonld"}}