jeffersongoncalves / laravel-discord-logger
Send Laravel logs to Discord with deduplication, configurable error grouping, rate limiting, async delivery and a graceful no-op when the webhook URL is missing.
Package info
github.com/jeffersongoncalves/laravel-discord-logger
pkg:composer/jeffersongoncalves/laravel-discord-logger
Fund package maintenance!
Requires
- php: ^8.2|^8.3|^8.4
- laravel/framework: ^11.0|^12.0|^13.0
- spatie/laravel-package-tools: ^1.14.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.24
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.7.4|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
This package is auto-updated.
Last update: 2026-06-22 17:09:39 UTC
README
Laravel Discord Logger
Send Laravel logs to Discord — built for production. A Monolog channel with deduplication, configurable error grouping, rate limiting, async delivery with 429 backoff, and a graceful no-op when the webhook URL is missing.
Installation
composer require jeffersongoncalves/laravel-discord-logger
Publish the config (optional):
php artisan vendor:publish --tag="laravel-discord-logger-config"
Configuration
Add a channel to config/logging.php:
'discord' => [ 'driver' => 'custom', 'via' => \JeffersonGoncalves\DiscordLogger\Logger::class, 'level' => env('LOG_DISCORD_LEVEL', 'error'), 'url' => env('LOG_DISCORD_WEBHOOK_URL'), ],
Stack it onto your default channel so errors fan out:
'stack' => [ 'driver' => 'stack', 'channels' => ['single', 'discord'], 'ignore_exceptions' => false, ],
Set the webhook (leave empty in local/testing — nothing will be sent, nothing will break):
LOG_DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/xxx/yyy
How the improvements work
Error grouping (fingerprint)
config/discord-logger.php → grouping.strategy:
message— group by message textlevel_message— group by level + messageexception(default) — group by exception class + file + line- a
callbackfn (\Monolog\LogRecord $record): stringfor full control
With grouping.normalize on, volatile tokens (numbers, UUIDs, hashes) are stripped before hashing, so User 123 not found and User 456 not found collapse together.
Deduplication
Within deduplication.window seconds, only the first occurrence of a fingerprint is sent. Repeats are counted silently. When the window closes, a single summary (🔁 occurred N×) is delivered — turn it off with deduplication.summary => false.
Rate limiting
rate_limit.global— hard cap on total messages app-widerate_limit.per_fingerprint— extra guard against a single looping error
Async delivery
Delivery runs through a queued job by default (queue.enabled). Discord 429s are retried respecting Retry-After; bad webhooks (4xx) fail fast instead of looping. Set queue.enabled => false to send inline (best-effort, errors swallowed).
State store
Dedup + rate-limit counters live in the cache store named by store (null = default). Use Redis in production for atomic counters.
Per-level webhooks & mentions
Route a level to its own channel and ping someone when it matters:
'webhooks' => [ 'EMERGENCY' => env('DISCORD_LOGGER_WEBHOOK_ALERTS'), 'CRITICAL' => env('DISCORD_LOGGER_WEBHOOK_ALERTS'), ], 'mentions' => [ 'EMERGENCY' => '@here', 'CRITICAL' => '<@&123456789012345678>', // role id ],
allowed_mentions is set automatically, so @here / @everyone / role / user pings actually fire.
Context redaction
Sensitive context keys are masked before sending — configure with redact (case-insensitive key fragments). Defaults cover password, secret, token, authorization, api_key.
Fallback channel & safety
The handler never throws and is guarded against logging-while-logging recursion. If delivery throws, the error is swallowed; set fallback_channel (e.g. 'single') to record those failures instead of losing them.
Commands
# Publish the config + optionally star the repo php artisan discord-logger:install # Send a test message to verify the webhook php artisan discord-logger:test --channel=discord
Testing
composer test
Credits
Inspired by marvinlabs/laravel-discord-logger.
License
The MIT License (MIT). See License File.
