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

Laravel Discord Logger Laravel Package

marvinlabs/laravel-discord-logger

Laravel Discord Logger adds a Discord logging channel to Laravel, sending application logs to Discord webhooks with configurable formatting, levels, and mention support. Useful for real-time alerts and monitoring without extra infrastructure.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and publish the config

composer require marvinlabs/laravel-discord-logger
php artisan vendor:publish --provider="MarvinLabs\DiscordLogger\ServiceProvider"

This gives you config/discord-logger.php. That file is the source of truth: it declares one or more named Discord targets (webhook URL + minimum level + formatting options). Read it before touching config/logging.php, because the two files reference each other by name.

2. Create the webhook

In Discord: Channel settings → Integrations → Webhooks → New Webhook → Copy Webhook URL. Treat that URL as a credential — anyone holding it can post to the channel as your app.

LOG_DISCORD_WEBHOOK=https://discord.com/api/webhooks/xxxx/yyyy
LOG_DISCORD_LEVEL=error

3. Register a Monolog channel

In config/logging.php, declare a discord channel that instantiates the package's custom logger driver and points at the named target from discord-logger.php. The shape is:

'discord' => [
    'driver' => 'custom',
    'via'    => \MarvinLabs\DiscordLogger\Logger::class,
    'level'  => 'error',
    // name of the entry under `channels` in config/discord-logger.php
],

Verify the exact keys against the published config — the package wires the webhook/level/mentions through the discord-logger.php entry, not through the logging.php array, and mismatched names fail quietly rather than loudly.

4. First use case: prove the pipe works

use Illuminate\Support\Facades\Log;

Log::channel('discord')->error('Deploy check: Discord logging is live', [
    'env'    => app()->environment(),
    'commit' => config('app.version'),
]);

If nothing appears, the three things to check in order are: webhook URL actually set (an empty string silently disables), the channel name match between the two config files, and php artisan config:clear.

5. Second use case: uncaught exceptions

Add discord to a stack and make that stack the default in production:

'stack' => [
    'driver'   => 'stack',
    'channels' => ['daily', 'discord'],
],

That alone gets you every report()-level exception into the channel — no Handler subclassing required on Laravel 11+. This is the highest-value, lowest-effort setup and is where most teams should stop.

Implementation Patterns

Separate channels per severity, not one catch-all. Discord channels are cheap; attention is not. A common layout:

// config/logging.php
'discord-errors' => ['driver' => 'custom', 'via' => Logger::class, 'level' => 'error'],
'discord-info'   => ['driver' => 'custom', 'via' => Logger::class, 'level' => 'info'],

Point discord-errors at the on-call channel (with a role mention) and discord-info at a low-traffic #app-feed. The level in logging.php and the level inside discord-logger.php both act as minimums — set the effective one deliberately and leave the other permissive or they'll fight each other in confusing ways.

Stack for persistence, channel for paging. Log::channel('discord') replaces the stack; stack appends to it. Use the stack so Discord is additive (file logs stay your audit trail), and use the direct channel when you specifically want a message in Discord and nowhere else — e.g. a synthetic "deploy started" notice that shouldn't pollute laravel.log.

Exception handler wiring (Laravel 11/12/13). For control over which exceptions page humans:

// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
    $exceptions->report(function (PaymentFailedException $e) {
        Log::channel('discord')->critical('Payment failed', [
            'order' => $e->orderId,
            'gateway' => $e->gateway,
        ]);
    });
})

Keep the message short and push everything else into context — context renders as embed fields, so the channel stays scannable.

Custom record formatters for embed control. The default rendering is fine for triage, but embeds (title, colour per level, field layout) are the reason to adopt this over a plain webhook curl. Subclass the default formatter (or implement the package's formatter contract) and map record level → colour, and context → fields. Register it on the channel via the formatter key in discord-logger.php. This is the main extension point; do your customisation here rather than post-processing strings in application code.

Mentions reserved for critical. Role/user mentions should be driven by level, not sprinkled through call sites. Configure the mention in discord-logger.php for the on-call channel only, and keep it off the info feed. A channel that pings on every error gets muted within a week, and then you have no alerting at all.

Queue the noisy paths. Anything that can fire in a loop — job failures, retry storms, third-party API degradation — should not synchronously POST to Discord inside the request/job that's already failing. Options, in order of preference: log at error to a file channel and let a scheduled command summarise to Discord; dispatch a small SendDiscordAlert job on a dedicated queue with $tries = 1; or accept the package's internal handling of unreachable/rate-limited endpoints but keep the payload small.

Use it for non-log signals too. Queue failures (Queue::failing), scheduled-task failures, and backup results are all just log lines. A single discord-ops channel used by three or four of these gives you an ops feed that costs almost nothing to maintain.

Gotchas and Tips

The webhook POST is synchronous. The handler runs inline with the log call. Discord is normally fast, but a degraded Discord adds latency to whatever request just logged an error — i.e. exactly when you can least afford it. Set an aggressive HTTP timeout in the package config and prefer the stack/queued patterns above for high-frequency paths.

Rate limits cause silent loss, not errors. Discord throttles webhooks (roughly per-webhook and per-route buckets, 429 with Retry-After). The package handles the common case, but a debugging session that dumps hundreds of debug records will drop messages and you'll misread that as "the handler is broken." If you need debug-level Discord output, do it in a staging channel with its own webhook and expect gaps.

Discord's payload limits are your formatting budget. ~2000 chars per message content, 10 embeds, 25 fields per embed, 1024 chars per field value, ~6000 chars total. Long stack traces and big context arrays get truncated or rejected. Log a short summary plus an identifier, and keep the full trace in laravel.log — this is a notification system, not a log store.

Fails gracefully means fails quietly. If the webhook is unreachable, blank, or the payload is rejected, you generally get no local exception — by design. That's correct for production but dangerous during setup. Verify with a real message after every config change; don't infer success from the absence of errors.

Empty or missing webhook is the #1 "it doesn't work". An unset LOG_DISCORD_WEBHOOK yields a handler that does nothing. Check config('discord-logger.channels.<name>.webhook') resolves non-empty inside php artisan tinker before debugging anything else.

Tests will post to a real channel. If your phpunit.xml keeps .env values, CI runs can spam production Discord. Explicitly null out the Discord channel or the webhook env in the testing environment, and use Http::fake() carefully — depending on how the handler builds its client, fakes registered on Laravel's HTTP client may not intercept the package's Guzzle call. Assert on the absence of a configured webhook rather than on a fake response.

config:cache staleness. Env changes require php artisan config:cache (or config:clear) — the classic symptom is "the webhook changed but logs go to the old channel." Bake this into your deploy script ordering.

Levels multiply. logging.php level and discord-logger.php level both apply, and bubble (default true) lets the record continue to other handlers. If you see duplicates, you've registered Discord twice — once directly and once inside a stack. If you see nothing at warning, check monotonic ordering of the two level settings.

Monolog 3 record shape. Custom formatters written against Monolog 2 (array $record) need updating for Monolog 3 (LogRecord). This bites on Laravel major upgrades — relevant given v1.4.4 added Laravel 13 support, so if you're moving the framework, re-test any custom formatter in the same PR.

Rotate webhooks like secrets. Deleted/rotated webhooks leave the handler POSTing into a 404 forever, silently. Keep the URL in env/secret manager, never in config/, and add "does the Discord channel still receive test messages" to your post-deploy checklist.

Extension points worth knowing: the custom formatter (embeds/colours/field mapping), per-channel config blocks in discord-logger.php for multiple webhooks, and the channel-name indirection in logging.php — which lets you swap #dev-alerts for #prod-alerts purely through env without touching the logging stack. Container-bind overrides of the package's Logger class are possible but rarely worth the upgrade friction; formatter customisation covers most needs.

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.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox