logo

Davide Lobascio

web developer

Background Overlay

Loggare Errori su Discord da Laravel _

29/11/2025 15 mins Davide Lobascio
Loggare Errori su Discord da Laravel

Notifiche LOG direttamente su un server Discord. Con un canale per ogni progetto, permette di centralizzare il monitoraggio dei progetti da me gestiti su uno strumento, Discord, che di default è sempre open, e quindi di facile visione.

Guida Completa: Inviare i Log di Errore di Laravel su Discord

Avere un sistema di monitoraggio degli errori in tempo reale è fondamentale per qualsiasi applicazione in produzione. Invece di controllare manualmente i file di log, ho pensato di ricevere notifiche istantanee direttamente su un'applicazione che di default ho sempre aperta: Discord!

In questa guida, vi guiderò nell'installazione di un sistema di logging per Laravel che invia automaticamente gli errori critici a un canale Discord, senza sacrificare il logging tradizionale su file.

📋 Indice


A Cosa Serve e Quali Vantaggi Offre?

  1. Notifiche Immediate e Leggibili: Ricevi un avviso su Discord nel momento esatto in cui si verifica un errore, formattato con colori e campi strutturati per una facile lettura.
  2. Monitoraggio Centralizzato: Se gestisci più progetti, puoi centralizzare i log di errore di tutti in un unico server Discord, usando canali diversi.
  3. Sistema Ridondante e Robusto: I log continueranno a essere salvati anche nel file storage/logs/laravel.log. Se Discord non fosse raggiungibile, il fallimento della notifica verrebbe registrato nel file di log locale, senza mai bloccare l'applicazione.
  4. Facilità di Implementazione: Con pochi passaggi, avrai un sistema di logging robusto e professionale.

Pronto? Iniziamo!


🚀 Implementazione Passo-Passo

Step 1: Creare un Webhook su Discord

Il primo passo è dire a Discord di "ascoltare" i messaggi in arrivo. Lo facciamo creando un Webhook.

  1. Apri Discord, vai sul server e scegli il canale dove vuoi ricevere le notifiche (es. #error-logs).
  2. Clicca sull'icona a forma di ingranaggio (⚙️) accanto al nome del canale per aprire le Impostazioni del canale.
  3. Vai alla sezione Integrazioni e clicca su Webhook.
  4. Clicca sul pulsante Nuovo Webhook.
  5. Assegna un nome riconoscibile al Webhook (es. "Laravel Logs - Progetto X") e, se vuoi, un avatar.
  6. Infine, clicca su Copia l'URL del Webhook. Tienilo a portata di mano, ci servirà tra poco.

L'URL sarà simile a questo: https://discord.com/api/webhooks/1234567890/abcdefg...

⚠️ Nota di Sicurezza: Tratta l'URL del Webhook come se fosse una password. Chiunque lo possieda può inviare messaggi al tuo canale Discord. Non condividerlo pubblicamente e non esporlo in repository pubblici!

Step 2: Configurare le Variabili d'Ambiente (.env)

Ora configuriamo Laravel per utilizzare il nostro nuovo sistema di logging. Apri il file .env del tuo progetto e aggiungi queste righe:

# modifica
LOG_CHANNEL=stack
LOG_STACK=single,discord

#aggiungi
DISCORD_WEBHOOK_URL=IL_TUO_URL_WEBHOOK_COPIATO_PRIMA
DISCORD_LOG_LEVEL=error

Cosa significano queste variabili?

  • LOG_CHANNEL=stack: Questa è la chiave di tutto. Stiamo dicendo a Laravel di non usare un singolo canale di log, ma uno "stack" (una pila) di canali. Questo ci permette di inviare lo stesso log a più destinazioni contemporaneamente.
  • LOG_STACK=single,discord: Qui definiamo quali canali fanno parte del nostro stack. single è il canale predefinito che scrive su file, mentre discord è il nome del nostro nuovo canale personalizzato.
  • DISCORD_WEBHOOK_URL: L'URL del Webhook che hai appena creato.
  • DISCORD_LOG_LEVEL: Il livello minimo di log da inviare a Discord. error è una scelta sicura per evitare spam, ma puoi usare anche warning, info, etc.

Step 3: Definire il Canale di Log Personalizzato

Laravel ha bisogno di sapere cosa significa discord. Apri il file config/logging.php e aggiungi il nostro canale all'array channels:

'channels' => [
    'discord' => [
        'driver' => 'custom',
        'via' => App\Logging\DiscordLogger::class,
        'level' => env('DISCORD_LOG_LEVEL', 'error'),
        'url' => env('DISCORD_WEBHOOK_URL'),
    ],

    // ... altri canali come 'single', 'daily', etc.
],

Assicurati anche che il canale stack sia configurato per leggere dinamicamente i canali dal file .env:

'stack' => [
    'driver' => 'stack',
    // Questa riga legge LOG_STACK e divide la stringa in un array
    'channels' => explode(',', (string) env('LOG_STACK', 'single')),
    'ignore_exceptions' => false,
],

Step 4: Creare il DiscordWebhookHandler

Questa è la classe che si occuperà del lavoro sporco: formattare il messaggio di log in un ricco "Embed" colorato e inviarlo a Discord.

Crea il file app/Logging/DiscordWebhookHandler.php e inserisci il seguente codice:

<?php

namespace App\Logging;

use Illuminate\Support\Facades\Http;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Level;
use Monolog\LogRecord;

class DiscordWebhookHandler extends AbstractProcessingHandler
{
    protected string $webhookUrl;

    public function __construct(string $webhookUrl, Level|int $level = Level::Error, bool $bubble = true)
    {
        $this->webhookUrl = $webhookUrl;
        parent::__construct($level, $bubble);
    }

    /**
     * Scrive il record di log sulla destinazione (Discord) usando Embeds.
     */
    protected function write(LogRecord $record): void
    {
        try {
            Http::asJson()->post($this->webhookUrl, [
                'embeds' => [
                    [
                        'title' => $record->level->getName() . ': ' . $record->message,
                        'description' => '```json' . PHP_EOL . json_encode($record->context, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL . '```',
                        'color' => $this->getColorForLevel($record->level),
                        'timestamp' => $record->datetime->format('c'),
                        'footer' => [
                            'text' => config('app.name'),
                        ],
                    ],
                ],
            ]);
        } catch (\Throwable $e) {
            // Se l'invio a Discord fallisce, logga l'errore nel canale 'single' (file di log locale).
            // Questo evita loop infiniti e informa sul problema di logging.
            logger()->channel('single')->warning(
                'Impossibile inviare il log a Discord.',
                [
                    'exception' => $e->getMessage(),
                ]
            );
        }
    }

    /**
     * Restituisce un colore esadecimale (in formato decimale) per il livello di log.
     */
    private function getColorForLevel(Level $level): int
    {
        return match ($level) {
            Level::Debug => 0x808080,     // Grigio
            Level::Info => 0x0000FF,      // Blu
            Level::Notice => 0x00FFFF,    // Ciano
            Level::Warning => 0xFFD700,   // Giallo Oro
            Level::Error => 0xFF0000,      // Rosso
            Level::Critical => 0xDC143C,  // Rosso Cremisi
            Level::Alert => 0xFF4500,     // Rosso Arancio
            Level::Emergency => 0x8B0000,  // Rosso Scuro
            default => 0x000000,          // Nero
        };
    }

    /**
     * Definisce il formato di default per i messaggi di log (non più usato direttamente per l\embed).
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new LineFormatter(
            "[%channel%] %level_name%: %message% %context% %extra%\n",
            null,
            true,
            true
        );
    }
}

Step 5: Creare la Factory DiscordLogger

Questa classe "costruisce" il nostro logger, collegandolo all'handler.

Crea il file app/Logging/DiscordLogger.php con il seguente contenuto:

<?php

namespace App\Logging;

use Monolog\Level;
use Monolog\Logger;

class DiscordLogger
{
    /**
     * Crea un'istanza Monolog personalizzata.
     *
     * @param  array<string, mixed>  $config
     */
    public function __invoke(array $config): Logger
    {
        $logger = new Logger('discord');

        $url = '';
        if (app()->environment('production')) {
            $url = $config['url'] ?? env('DISCORD_WEBHOOK_URL');
        }

        // Se l'URL non è configurato (o non siamo in produzione), restituiamo un logger vuoto.
        if (!$url) {
            return $logger;
        }

        // Logica corretta per determinare il livello, risolvendo il bug.
        try {
            // Prova a convertire il livello dalla configurazione (es. 'warning' -> Level::Warning).
            $level = Level::fromName(strtoupper($config['level'] ?? 'error'));
        } catch (\Throwable $e) {
            // Se il livello specificato non è valido, usa 'Error' come fallback sicuro.
            $level = Level::Error;
        }
        $logger->pushHandler(new DiscordWebhookHandler($url, $level));

        return $logger;
    }
}

⚠️ Nota: Qui c'è un controllo sull'ambiente per evitare che vengano spammate notifiche in fase di sviluppo. Per testare il Discord Logger, commentare l'if di controllo dell'env, o spostare momentaneamente fuori dall'if la riga $url = $config['url'] ?? env('DISCORD_WEBHOOK_URL');

Step 6: Pulire la Cache della Configurazione

Ogni volta che modifichi un file nella cartella config, è una buona pratica pulire la cache di configurazione per assicurarti che Laravel carichi le nuove impostazioni.

Esegui questo comando nel terminale:

php artisan config:clear

Step 7: Verificare i Prerequisiti

Prima di procedere con il test, assicurati che tutto sia configurato correttamente:

  • ✅ Il Webhook Discord sia attivo e l'URL sia stato copiato correttamente
  • ✅ La variabile DISCORD_WEBHOOK_URL sia presente nel file .env
  • ✅ Non ci siano errori di sintassi nei file PHP creati (DiscordWebhookHandler.php e DiscordLogger.php)
  • ✅ La cache di configurazione sia stata pulita con php artisan config:clear

Step 8: Testare il Sistema

È il momento della verità! Il modo più rapido per testare è usare artisan tinker:

php artisan tinker --execute="Log::error('Questo è un test di logging da Bauhouse! Qualcuno ha rotto qualcosa.', ['user_id' => 123, 'details' => 'Action failed']);"

Cosa dovrebbe succedere?

  1. Il messaggio di errore apparirà immediatamente nel tuo file storage/logs/laravel.log.
  2. Se la tua variabile APP_ENV nel file .env è impostata su production, nel canale Discord apparirà un messaggio "Embed" rosso contenente il titolo dell'errore e i dati contestuali formattati in un blocco di codice JSON.

Nota per i test in locale: Il nostro codice invia log a Discord solo in produzione (APP_ENV=production). Per testare in locale, puoi temporaneamente cambiare APP_ENV in production, eseguire il test e poi ricordarti di rimetterlo a local.


🔧 Troubleshooting

Se qualcosa non funziona come previsto, ecco le soluzioni ai problemi più comuni:

❌ Problema: Non ricevo messaggi su Discord

Possibili cause e soluzioni:

  1. Ambiente non corretto

    • Verifica che APP_ENV=production nel file .env
    • Se stai testando in locale, ricorda di cambiare temporaneamente l'ambiente
  2. URL del Webhook errato

    • Controlla che DISCORD_WEBHOOK_URL nel .env sia esattamente quello copiato da Discord
    • Verifica che non ci siano spazi prima o dopo l'URL
  3. Cache di configurazione

    • Esegui php artisan config:clear per pulire la cache
    • Riavvia il server se usi php artisan serve
  4. Errori di connessione

    • Controlla storage/logs/laravel.log per messaggi del tipo "Impossibile inviare il log a Discord"
    • Verifica la connessione internet del server

⚠️ Problema: Ricevo troppi messaggi su Discord

Soluzione:

Aumenta il livello di log minimo nel file .env:

# Invece di 'error', usa un livello più restrittivo
DISCORD_LOG_LEVEL=critical

I livelli disponibili in ordine crescente di gravità sono:

  • debuginfonoticewarningerrorcriticalalertemergency

🎨 Problema: L'embed non è formattato correttamente

Possibili cause:

  1. Context array non serializzabile

    • Assicurati che l'array context passato al log contenga solo dati serializzabili in JSON
    • Evita di passare oggetti complessi o risorse
  2. Caratteri speciali

    • I caratteri speciali vengono automaticamente escaped, ma verifica che non ci siano problemi di encoding

Esempio di log corretto:

Log::error('Errore durante il pagamento', [
    'user_id' => $user->id,
    'amount' => 49.99,
    'error_code' => 'PAYMENT_DECLINED',
]);

🔄 Problema: Loop infiniti di logging

Se noti che i log si moltiplicano all'infinito, verifica che:

  • Il canale discord non sia incluso nello stack quando loggi errori di Discord stesso
  • La gestione degli errori nel try-catch di DiscordWebhookHandler usi specificamente logger()->channel('single') e non Log::error()

🎉 Conclusione e Best Practices

Congratulazioni! Hai appena implementato un sistema di monitoraggio degli errori potente, leggibile e professionale per la tua applicazione Laravel.

💡 Suggerimenti per l'Uso Ottimale

1. Personalizza gli Embeds

Sentiti libero di modificare la struttura dell'embed in DiscordWebhookHandler per aggiungere informazioni utili:

'embeds' => [
    [
        'title' => $record->level->getName() . ': ' . $record->message,
        'description' => '```json' . PHP_EOL . json_encode($record->context, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL . '```',
        'color' => $this->getColorForLevel($record->level),
        'timestamp' => $record->datetime->format('c'),
        'fields' => [
            [
                'name' => 'Environment',
                'value' => config('app.env'),
                'inline' => true,
            ],
            [
                'name' => 'URL',
                'value' => request()->fullUrl() ?? 'N/A',
                'inline' => true,
            ],
        ],
        'footer' => [
            'text' => config('app.name') . ' v' . config('app.version', '1.0'),
        ],
    ],
],

2. Anti-Spam: Scegli il Livello Giusto

Resisti alla tentazione di abbassare DISCORD_LOG_LEVEL a info o debug in produzione. Rischieresti di inondare il canale con centinaia di notifiche irrilevanti.

Livelli consigliati per ambiente:

  • Production: error o critical
  • Staging: warning o error
  • Development: usa solo il file di log, disabilita Discord

3. Separa gli Ambienti

Usa Webhook e canali Discord diversi per ogni ambiente:

# Production (.env.production)
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/xxx/prod-webhook

# Staging (.env.staging)
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/xxx/staging-webhook

In questo modo potrai:

  • Distinguere visivamente i messaggi (usa colori/icone diverse)
  • Applicare livelli di log diversi per ambiente
  • Gestire i permessi del canale in modo granulare

4. Monitora Più Progetti

Se gestisci più applicazioni Laravel, centralizza tutti i log in un unico server Discord usando canali diversi:

  • #logs-sito-X
  • #logs-sito-Y
  • #logs-crm-sito-Z

5. Integra con il Flusso di Lavoro del Team

  • Mention automatiche: Modifica l'embed per menzionare utenti specifici su errori critici
  • Thread automatici: Discord permette di creare thread da webhook per organizzare le discussioni
  • Emoji reactions: Usa le reactions per segnare gli errori come "risolti" o "in lavorazione"

🔗 Risorse Utili


Ti è stata utile? Se questa guida ti è stata utile, non esitare a farmelo sapere!

Galleria Immagini _

Articolo
Privacy Policy