ruudk/campfire-exception-bundle
A Symfony bundle that posts application exceptions to a Campfire chat room, so your team sees errors as they happen. It hooks into Symfony's exception handling to push formatted notifications with the message, file, and trace straight into the room you configure.
What is actually being evaluated. A Symfony2 bundle (~2014) that registers a kernel.exception listener, formats the exception into a text/JSON payload, and POSTs it to a Campfire chat room via the 37signals Campfire API. Signal quality is essentially nil: 1 star, 0 dependents, archived, last release 2014-04-10. This is a dormant artifact, not a maintained dependency.
Architecture fit. Poor, and the mismatch is structural rather than cosmetic. The package's integration contract is Symfony's DependencyInjection component, services.yml definitions, and the Symfony event dispatcher (kernel.exception). Laravel exposes none of those seams — it has its own container, a report()/ExceptionHandler pipeline, and a Monolog-based logging stack. The only genuinely shared surface is PSR-3 logging, and this bundle predates any PSR-3 usage. There is no version of "install and configure" that works here.
Technical viability of the code itself. Even inside a Symfony app, the code targets APIs that no longer exist:
GetResponseForExceptionEvent (the likely listener signature) was removed in Symfony 5.guzzlehttp/guzzle ^7, so Composer will either refuse to resolve or drag in a conflicting subtree.The real blocker: the destination is gone. Campfire is a discontinued 37signals product. The tenant subdomain and /room/{id}/speak.json endpoint the bundle targets no longer exist. A perfect installation yields a listener that throws a connection error on every exception — turning a logging concern into a second failure path.
Security posture. Exception payloads are among the highest-sensitivity data an app emits: stack traces with absolute paths, environment names, request headers, occasionally DSNs, tokens, and user identifiers. This code was written before redaction, sampling, and PII scrubbing were standard expectations, and an archived repo receives no CVE patching. Sending raw traces to a dead third-party endpoint over a legacy TLS stack is not defensible in a current review.
Value vs. cost. The functional core is small — format an exception, fingerprint it, POST to a chat webhook. Estimate 100–200 lines to reimplement natively with redaction, dedupe, and async delivery. The package contributes essentially zero leverage over that rewrite while adding supply-chain and upgrade-blocking risk.
Risk summary.
| Risk | Severity | Notes |
|---|---|---|
| Framework/API incompatibility | High | Symfony2 idioms removed in Symfony 5; no Laravel seam |
| Dead upstream endpoint | High | Campfire discontinued; guaranteed runtime failures |
| Composer conflict blocking upgrades | High | Legacy Guzzle/Symfony pins can block PHP/Laravel bumps org-wide |
| Sensitive data egress, unpatched | High | Stack traces to third party; archived = no security response |
| Abandonment (archived, 0 dependents) | Medium | No maintainer, no issue triage, no roadmap |
| Functional value delivered | Low | ~150 LOC of reimplementable behavior |
Key questions for the requestor.
Verdict: do not install. Transplant the pattern. Treat this repository as a design reference — a listener that snapshots an exception and pushes a concise summary to a team channel — and implement that behavior natively in Laravel. Installing a 2014 Symfony2 bundle into a modern Laravel app is not an integration; it is a fork-and-rewrite with extra steps and a worse starting point.
Stack fit.
report() / shouldReport() in bootstrap/app.php (Laravel 11+) or App\Exceptions\Handler::report() (Laravel ≤10). From there, dispatch a queued job that formats a payload and posts to a chat webhook. This inherits the container, config, queue, and logging for free.slack log driver backed by Monolog's Slack/webhook handlers. Point a dedicated channel at the team room and route specific exception levels to it. Zero new dependencies; parity for the "exception goes to chat" use case in under an hour.Compatibility. Target PHP 8.1+ and Laravel 10/11. Do not attempt Composer resolution of the original package; if institutional reasons force reuse, vendor the file into an internal namespace, replace the HTTP client with Illuminate\Support\Facades\Http, strip the Symfony DI wiring, and add tests — at which point you have rewritten it anyway.
Migration path (if replacing an existing Campfire feed).
CampfireExceptionBundle, campfirenow.com, and any stored API tokens. Confirm no live consumer depends on the room.Sequencing and effort.
| Phase | Duration | Exit criteria |
|---|---|---|
| Inventory & decision | 1–2 days | Written confirmation no live Campfire consumer |
| Baseline + rebuild | 2–3 days | Alerts land in staging channel with parity payload |
| Hardening (dedupe, redaction, queue) | 2–4 days | Storm test produces ≤N messages; no PII in payload |
| Shadow-run | 1–2 weeks | Volume and signal match expectations |
| Cutover & cleanup | 1 day | Credentials revoked, package removed, runbook updated |
Explicit non-goal. Do not build a generic Symfony-bundle-to-Laravel compatibility shim to host this. That shim would cost more than the replacement and would become a permanent maintenance liability with exactly one consumer.
Maintenance. The upstream is archived; there is no path for patches, PHP 8+ fixes, or security response. Any adopted copy becomes permanently internal, owned by whichever team inherits it, and must be tracked as a forked dependency in the vulnerability-management process. Even at ~150 LOC, that is a real inventory item with nonzero carrying cost forever. The replacement is smaller and lives inside the app's own test suite.
Support. No maintainer, no issue triage, no documentation beyond a 2014 README, and effectively no community (1 star, 0 dependents means no peer knowledge base and no Stack Overflow corpus). Incident response must be fully internal: the runbook has to explain what the listener does, where it sends, and how to silence it — knowledge that exists only if someone writes it during migration.
Scaling and failure modes. The design is synchronous delivery from inside the exception handler, which is the worst possible place to do network I/O:
Ramp-up. Comprehending the package's behavior takes an experienced PHP engineer a couple of hours — the logic is simple. The hidden cost is context: understanding Symfony2 DI conventions, services.yml wiring, and 2014-era Guzzle has near-zero transferable value to a Laravel team, and the "why does this exist and can we delete it" archaeology consumes more time than the rewrite. Building the Laravel-native equivalent is a half-day task for any mid-level engineer already fluent in the framework.
Recommendation. Reject adoption. Close the request as "replace, don't integrate": ship a queued, deduplicated, redacted exception-to-chat notifier on Laravel's native reporting seam — or, preferably, adopt a managed error tracker with a chat integration — and retire the Campfire reference entirely. The only scenario warranting further review is a still-running Symfony 2.x service, and in that case the correct ticket is application retirement, not package installation.
How can I help you explore Laravel packages today?