DEV Community

Abdullah Alhumsi
Abdullah Alhumsi

Posted on

How I Built a Laravel Package to Send Error Alerts to Telegram, Slack & Discord

Every Laravel app breaks in production. The question is: do you
find out before your users do?

I got tired of checking logs manually and built a package that
sends instant, actionable error alerts to Telegram, Slack, and
Discord the moment something breaks.

Here's how I built it and what I learned.

The Problem

Most Laravel error monitoring solutions are either too expensive,
too complex to set up, or send you a generic alert with no context.

I wanted something that:

  • Sends alerts instantly to channels I already use
  • Shows me exactly what broke and where
  • Suggests what to do about it
  • Is dead simple to install

So I built laravel-error-notifier.

What It Does

The package hooks into Laravel's exception handler and sends
a formatted alert to your configured channels the moment an
exception is thrown in production.

A Telegram alert looks like this:

๐Ÿšจ EMERGENCY | your-app.com
QueryException
SQLSTATE[42S02]: Table 'users' doesn't exist
๐Ÿ“ app/Services/UserService.php:45
๐Ÿ’ก Suggestion: Check your migration status

Installation

composer require alhumsi/laravel-error-notifier
php artisan vendor:publish --tag=error-notifier-config
Enter fullscreen mode Exit fullscreen mode

Add your channel credentials to .env:

ERROR_NOTIFIER_TELEGRAM_BOT_TOKEN=your-token
ERROR_NOTIFIER_TELEGRAM_CHAT_ID=your-chat-id
ERROR_NOTIFIER_SLACK_WEBHOOK=https://hooks.slack.com/...
ERROR_NOTIFIER_DISCORD_WEBHOOK=https://discord.com/api/webhooks/...

That's it. No manual registration needed โ€” the package
auto-discovers itself.

How It Works Under the Hood

The package uses three core abstractions:

AnalyzerInterface โ€” maps exception types to severity levels.
A QueryException is critical. A ValidationException is error.
You can override this with your own analyzer.

MessageFormatterInterface โ€” formats the alert for each channel.
Telegram uses MarkdownV2. Slack uses Block Kit. Discord uses embeds.
Each formatter produces a channel-native message.

NotifierInterface โ€” sends the formatted message via HTTP.
Swap this with a queue-backed implementation for async delivery.

Routing Alerts by Severity

In config/error-notifier.php:

'levels' => [
    'emergency' => ['slack', 'telegram'],
    'critical'  => ['slack'],
    'error'     => ['discord'],
],
Enter fullscreen mode Exit fullscreen mode

High-severity alerts go to Slack where your team sees them
immediately. Lower severity goes to Discord as a log channel.
You decide what goes where.

Custom Icons Per Severity

'icons' => [
    'emergency' => '๐Ÿšจ',
    'critical'  => '๐Ÿ”ฅ',
    'error'     => 'โŒ',
    'warning'   => 'โš ๏ธ',
],
Enter fullscreen mode Exit fullscreen mode

Small detail but it makes alerts scannable at a glance.

What I Learned Building This

1. Contracts make packages extensible.
Every core behavior is behind an interface. Users can swap
the analyzer, formatter, or notifier without touching package code.
This is the difference between a package people use and one
they fork and modify.

2. Auto-discovery matters.
Adding a ServiceProvider to config/app.php is a friction point.
Laravel's package auto-discovery removes that friction entirely.
Less friction = more installs.

3. Write tests from day one.
I wrote tests before the implementation was complete. It forced
me to think about the public API first and made the code
significantly cleaner.

Try It

composer require alhumsi/laravel-error-notifier
Enter fullscreen mode Exit fullscreen mode

GitHub: https://github.com/Alhumsiabdo/laravel-error-notifier
Packagist: https://packagist.org/packages/alhumsi/laravel-error-notifier

If you find it useful, a โญ on GitHub goes a long way.

What error monitoring are you using in your Laravel apps?
I'd love to know in the comments.

Top comments (0)