Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Error Bundle Laravel Package

exs/error-bundle

Symfony 2.x bundle that logs all 4xx and 5xx HTTP errors and exceptions to the database, capturing useful debugging details for each one. Pair it with a cron-based monitor to spot error spikes and alert the dev team, enabling rapid fixes before small issues become outages. Quick to install via Compo...

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Confirm you actually have a Symfony 2.x app. This bundle is frozen at 2015: it targets Symfony 2, app/console, YAML service definitions, and a pre-DoctrineBundle-2 API. On Symfony 4+ Composer will refuse to resolve it; on Symfony 3 you'll have friction; don't force it with --ignore-platform-reqs on a live project.

  2. Install:

    composer require 'exs/error-bundle:~1.0@dev'
    
  3. Wire it up (three files, per the README):

    • app/AppKernel.php → new EXS\ErrorBundle\EXSErrorBundle() (add it last in $bundles; enable in prod only, or at least disable in test).
    • app/config/routing.yml → import the bundle's routing so any built-in listing/view route resolves.
    • Schema: app/console doctrine:schema:update --force (or generate a migration — see Gotchas).
  4. Where to look first, in this order:

    • Resources/config/services.yml — the listener service and its kernel.event_listener tag with event: kernel.exception and its priority. This is the whole package.
    • Entity/ — the error record (fields you'll query and index).
    • DependencyInjection/Configuration.php + the Extension class — the knobs you can tune (ignored status codes, maybe notification email).
    • The listener class itself — sanity-check for a try/catch around persistence before you trust it in production.
  5. First use case: throw a 404 (app/console router:debug → hit a bad URL), then SELECT * FROM <error_table> ORDER BY id DESC LIMIT 5;. If a row appears, the loop works. Then hit the same URL 50 times and watch it fill up — that tells you immediately why you need the filtering/aggregation patterns below.

Implementation Patterns

Alerting cron (the intended use case). The bundle only stores; the "alert" half is yours. A 5-minute cron that counts recent rows and pings Slack/email:

// bin/error-watch.php, bootstrapped with the app kernel
$since = new \DateTime('-15 minutes');
$counts = $em->getRepository(Error::class)->createQueryBuilder('e')
    ->select('e.statusCode, COUNT(e.id) AS c')
    ->where('e.createdAt >= :since')->setParameter('since', $since)
    ->groupBy('e.statusCode')->getQuery()->getArrayResult();
// alert if 5xx count > threshold, or total > threshold

Aggregate, don't append. If the table has a fingerprint-style column (class + message + route), do INSERT ... ON DUPLICATE KEY UPDATE hits = hits + 1, last_seen = NOW(). If it only stores raw rows, add a fingerprint column yourself and normalise on read. Raw-row logging is what makes this bundle unbounded and noisy.

Only log what matters. 404s from bots (/wp-login.php, /.env, favicon) will drown the table. Configure ignored status codes / path patterns if the bundle exposes them; otherwise subclass/decorate the listener and early-return on 404 for /wp-*, /\.git, and static assets. Keep 5xx and uncaught 500s at full fidelity.

Pair it with Monolog, don't replace it with it. The DB row is for trends and counts; the stack trace, request payload, and user id belong in a log file or an error service. Keep fingers_crossed/prod handlers as-is and treat the bundle as a metrics sink.

Respect environment. Gate registration on %kernel.debug% === false so dev/test pages don't write throwaway rows, and so your functional tests don't need the table.

Port it forward when you upgrade. The entire mechanism is ~40 lines on Symfony 5+:

class ErrorLogSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    { return [KernelEvents::EXCEPTION => ['onException', 0]]; }
    // persist HttpException status, else 500
}

Take the Entity and the query patterns from this bundle, drop the Symfony 2 plumbing. That is realistically the highest-value use of this repository in a modern codebase.

Gotchas and Tips

  • The DB write happens while you're handling a failure. If the exception is a database error, the EntityManager may be closed or the connection gone — you then throw a second exception inside kernel.exception and either loop or mask the original error. Wrap persistence in try/catch, check $em->isOpen() (consider resetManager()), and fall back to error_log().
  • Ordering vs. Symfony's own exception listener. Symfony converts exceptions to responses at a low priority (typically -128). Your listener must run before that to see the raw exception. Verify with app/console debug:event-dispatcher kernel.exception after clearing cache — if you see nothing, the tag didn't compile.
  • 4xx flooding / table growth. No pruning ships with this. Add both: a created_at index (grouping queries will table-scan without it) and a nightly DELETE WHERE created_at < NOW() - INTERVAL 90 DAY. Also cap row size — request URIs are unbounded (attackers send 8 KB paths).
  • PII in the rows. If the bundle stores request parameters, headers, or IPs, you've created a GDPR surface. Audit the entity columns; truncate/sanitize URI and never persist request bodies.
  • Schema management. Prefer generating a Doctrine migration from the entity over doctrine:schema:update --force on production, especially since the bundle predates migrations being standard.
  • Composer will lie to you. A ~1.0@dev constraint may clone dev-master; pin the commit if you must ship it. composer show exs/error-bundle and check composer why-not when resolution fails — usually it's symfony/symfony: ^2.x vs your ^6.
  • Extension points are thin. You have three levers: override the entity/repository in your own bundle (Doctrine resolve-target-entity), decorate the listener service, or just fork it — at 1 star with no dependents, treat it as a snippet library, not a dependency. Copying the two files into src/ is a defensible and safer choice than an unmaintained transitive dependency.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource