Logging

Introduzione

Per aiutarti a capire meglio cosa succede all’interno della tua applicazione, Laravel fornisce servizi di logging robusti che ti permettono di registrare messaggi in file, nel registro degli errori di sistema e persino su Slack per notificare l’intero team.

Il logging di Laravel si basa sui "canali". Ogni canale rappresenta un modo specifico di scrivere le informazioni di log. Ad esempio, il canale single scrive i file di log in un unico file, mentre il canale slack invia i messaggi di log a Slack. I messaggi di log possono essere scritti su più canali in base alla loro gravità.

Dietro le quinte, Laravel utilizza la libreria Monolog, che offre supporto per una varietà di gestori di log potenti. Laravel rende semplice configurare questi gestori, permettendoti di combinarli per personalizzare la gestione dei log della tua applicazione.

Configurazione

Tutte le opzioni di configurazione che controllano il comportamento del logging della tua applicazione si trovano nel file di configurazione config/logging.php. Questo file ti permette di configurare i canali di log della tua applicazione, quindi assicurati di esaminare ciascuno dei canali disponibili e le loro opzioni. Di seguito esamineremo alcune opzioni comuni.

Per impostazione predefinita, Laravel utilizza il canale stack per registrare i messaggi. Il canale stack serve ad aggregare più canali di log in un unico canale. Per maggiori informazioni sulla creazione di stack, consulta la documentazione di seguito.

Driver di canale disponibili

Ogni canale di log è gestito da un "driver". Il driver determina come e dove il messaggio di log viene effettivamente registrato. I seguenti driver di canale di log sono disponibili in ogni applicazione Laravel. Una voce per la maggior parte di questi driver è già presente nel file di configurazione config/logging.php della tua applicazione, quindi assicurati di rivedere questo file per familiarizzare con il suo contenuto:

Nome Descrizione
custom Un driver che richiama una factory specificata per creare un canale.
daily Un driver Monolog basato su RotatingFileHandler che ruota giornalmente.
errorlog Un driver Monolog basato su ErrorLogHandler.
monolog Un driver factory Monolog che può usare qualsiasi handler Monolog supportato.
papertrail Un driver Monolog basato su SyslogUdpHandler.
single Un canale di log basato su un file singolo o percorso (StreamHandler).
slack Un driver Monolog basato su SlackWebhookHandler.
stack Un wrapper per facilitare la creazione di canali "multi-canale".
syslog Un driver Monolog basato su SyslogHandler.

Consulta la documentazione su personalizzazione avanzata dei canali per saperne di più sui driver monolog e custom.

Configurare il Nome del Canale

Per impostazione predefinita, Monolog viene istanziato con un "nome canale" che corrisponde all’ambiente attuale, come production o local. Per modificare questo valore, puoi aggiungere un’opzione name alla configurazione del tuo canale:

'stack' => [
    'driver' => 'stack',
    'name' => 'channel-name',
    'channels' => ['single', 'slack'],
],

Prerequisiti del Channel

Configurazione dei canali Single e Daily

I canali single e daily hanno tre opzioni di configurazione opzionali: bubble, permission e locking.

Nome Descrizione Predefinito
bubble Indica se i messaggi devono propagarsi ad altri canali dopo essere stati gestiti. true
locking Tenta di bloccare il file di log prima di scriverci. false
permission Le autorizzazioni del file di log. 0644

Inoltre, la politica di conservazione per il canale daily può essere configurata tramite la variabile d’ambiente LOG_DAILY_DAYS o impostando l’opzione di configurazione days.

Nome Descrizione Predefinito
days Il numero di giorni in cui i file di log giornalieri devono essere mantenuti. 14

Configurare il Canale Papertrail

Il canale papertrail richiede le opzioni di configurazione host e port. Queste possono essere definite tramite le variabili d’ambiente PAPERTRAIL_URL e PAPERTRAIL_PORT. Puoi ottenere questi valori da Papertrail.

Configurazione del canale Slack

Il canale slack richiede un’opzione di configurazione url. Questo valore può essere definito tramite la variabile d’ambiente LOG_SLACK_WEBHOOK_URL. Questo URL deve corrispondere a un incoming webhook che hai configurato per il tuo team Slack.

Per impostazione predefinita, Slack riceverà solo i log a livello critical e superiori; tuttavia, puoi modificare questo comportamento utilizzando la variabile d’ambiente LOG_LEVEL o modificando l’opzione di configurazione level all’interno dell’array di configurazione del tuo canale log Slack.

Registrare Avvisi di Deprecazione

PHP, Laravel e altre librerie spesso informano gli utenti che alcune funzionalità sono state deprecate e saranno rimosse in future versioni. Se vuoi registrare questi avvisi di deprecazione, puoi specificare il canale di log preferito deprecations utilizzando la variabile d’ambiente LOG_DEPRECATIONS_CHANNEL, oppure nel file di configurazione config/logging.php della tua applicazione:

'deprecations' => [
    'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
    'trace' => env('LOG_DEPRECATIONS_TRACE', false),
],

'channels' => [
    // ...
]

Oppure, puoi definire un canale di log chiamato deprecations. Se esiste un canale di log con questo nome, verrà sempre utilizzato per registrare le deprecazioni:

'channels' => [
    'deprecations' => [
        'driver' => 'single',
        'path' => storage_path('logs/php-deprecation-warnings.log'),
    ],
],

Creare Stack di Log

Come menzionato precedentemente, il driver stack permette di combinare più canali in un unico canale di log per comodità. Per illustrare come utilizzare gli stack di log, diamo un’occhiata a un esempio di configurazione che potresti vedere in un’applicazione in produzione:

'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => ['syslog', 'slack'], // [tl! add]
        'ignore_exceptions' => false,
    ],

    'syslog' => [
        'driver' => 'syslog',
        'level' => env('LOG_LEVEL', 'debug'),
        'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
        'replace_placeholders' => true,
    ],

    'slack' => [
        'driver' => 'slack',
        'url' => env('LOG_SLACK_WEBHOOK_URL'),
        'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
        'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
        'level' => env('LOG_LEVEL', 'critical'),
        'replace_placeholders' => true,
    ],
],

Esaminiamo questa configurazione. Innanzitutto, notiamo che il nostro canale stack aggrega altri due canali tramite l’opzione channels: syslog e slack. Quindi, quando si registrano i messaggi, entrambi questi canali avranno la possibilità di registrare il messaggio. Tuttavia, come vedremo più avanti, se questi canali registrano effettivamente il messaggio può dipendere dalla severità / "level" del messaggio.

Livelli di Log

Fai attenzione all’opzione di configurazione level presente nelle configurazioni dei canali syslog e slack nell’esempio sopra. Questa opzione determina il "livello" minimo che un messaggio deve avere per essere registrato dal canale. Monolog, che alimenta i servizi di logging di Laravel, offre tutti i livelli di log definiti nella specifica RFC 5424. In ordine decrescente di gravità, questi livelli di log sono: emergency, alert, critical, error, warning, notice, info e debug.

Quindi, immagina di registrare un messaggio usando il metodo debug:

    Log::debug('Un messaggio informativo.');

Dato la nostra configurazione, il canale syslog scriverà il messaggio nel log di sistema; tuttavia, poiché il messaggio di errore non è critical o superiore, non sarà inviato a Slack. Tuttavia, se registriamo un messaggio di emergency, sarà inviato sia al log di sistema che a Slack poiché il livello emergency è al di sopra della soglia minima per entrambi i canali:

    Log::emergency('Il sistema è giù!');

Scrivere Messaggi di Log

Puoi scrivere informazioni nei log usando la facade Log facade. Come menzionato precedentemente, il logger fornisce otto livelli di log definiti nella specifica RFC 5424: emergency, alert, critical, error, warning, notice, info e debug:

    use Illuminate\Support\Facades\Log;

    Log::emergency($message);
    Log::alert($message);
    Log::critical($message);
    Log::error($message);
    Log::warning($message);
    Log::notice($message);
    Log::info($message);
    Log::debug($message);

Puoi chiamare uno qualsiasi di questi metodi per registrare un messaggio per il livello corrispondente. Per impostazione predefinita, il messaggio verrà scritto nel canale di log predefinito come configurato nel tuo file di configurazione logging:

    <?php

    namespace App\Http\Controllers;

    use App\Http\Controllers\Controller;
    use App\Models\User;
    use Illuminate\Support\Facades\Log;
    use Illuminate\View\View;

    class UserController extends Controller
    {
        /**
         * Mostra il profilo per l'utente dato.
         */
        public function show(string $id): View
        {
            Log::info('Showing the user profile for user: {id}', ['id' => $id]);

            return view('user.profile', [
                'user' => User::findOrFail($id)
            ]);
        }
    }

Informazioni Contestuali

Un array di dati contestuali può essere passato ai metodi di log. Questi dati verranno formattati e mostrati insieme al messaggio di log:

    use Illuminate\Support\Facades\Log;

    Log::info('User {id} failed to login.', ['id' => $user->id]);

A volte, potresti voler specificare alcune informazioni contestuali che devono essere incluse in tutte le voci di log successive in un canale particolare. Ad esempio, potresti voler registrare un ID richiesta associato a ogni richiesta in entrata nella tua applicazione. Per fare questo, puoi chiamare il metodo withContext della facade Log:

    <?php

    namespace App\Http\Middleware;

    use Closure;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Log;
    use Illuminate\Support\Str;
    use Symfony\Component\HttpFoundation\Response;

    class AssignRequestId
    {
        /**
         * Gestisce una richiesta in arrivo.
         *
         * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
         */
        public function handle(Request $request, Closure $next): Response
        {
            $requestId = (string) Str::uuid();

            Log::withContext([
                'request-id' => $requestId
            ]);

            $response = $next($request);

            $response->headers->set('Request-Id', $requestId);

            return $response;
        }
    }

Se desideri condividere informazioni contestuali su tutti i canali di log, puoi invocare il metodo Log::shareContext(). Questo metodo fornirà le informazioni contestuali a tutti i canali creati e a quelli che saranno creati successivamente:

    <?php

    namespace App\Http\Middleware;

    use Closure;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Log;
    use Illuminate\Support\Str;
    use Symfony\Component\HttpFoundation\Response;

    class AssignRequestId
    {
        /**
         * Gestisce una richiesta in arrivo.
         *
         * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
         */
        public function handle(Request $request, Closure $next): Response
        {
            $requestId = (string) Str::uuid();

            Log::shareContext([
                'request-id' => $requestId
            ]);

            // ...
        }
    }

Se hai bisogno di condividere il contesto del log durante l’elaborazione di job in coda, puoi utilizzare middleware per i job.

Scrivere su Canali Specifici

A volte potresti voler registrare un messaggio su un canale diverso dal canale predefinito della tua applicazione. Puoi usare il metodo channel sulla facade Log per recuperare e registrare su qualsiasi canale definito nel tuo file di configurazione:

use Illuminate\Support\Facades\Log;

Log::channel('slack')->info('Something happened!');

Se desideri creare uno stack di registrazione su richiesta composto da più canali, puoi usare il metodo stack:

Log::stack(['single', 'slack'])->info('Something happened!');

Canali On-Demand

È anche possibile creare un canale on-demand fornendo la configurazione a runtime senza che essa sia presente nel file di configurazione logging della tua applicazione. Per fare ciò, puoi passare un array di configurazione al metodo build del facade Log:

use Illuminate\Support\Facades\Log;

Log::build([
  'driver' => 'single',
  'path' => storage_path('logs/custom.log'),
])->info('Qualcosa è successo!');

Potresti anche voler includere un canale on-demand in uno stack di logging on-demand. Questo può essere fatto includendo l’istanza del tuo canale on-demand nell’array passato al metodo stack:

use Illuminate\Support\Facades\Log;

$channel = Log::build([
  'driver' => 'single',
  'path' => storage_path('logs/custom.log'),
]);

Log::stack(['slack', $channel])->info('Qualcosa è successo!');

Personalizzazione del Canale Monolog

Personalizzare Monolog per i Canali

A volte potresti aver bisogno di avere il completo controllo su come Monolog è configurato per un canale esistente. Ad esempio, potresti voler configurare un’implementazione personalizzata di Monolog FormatterInterface per il canale predefinito single di Laravel.

Per iniziare, definisci un array tap nella configurazione del canale. L’array tap dovrebbe contenere una lista di classi che possono personalizzare (o "tappare") l’istanza di Monolog dopo che è stata creata. Non c’è una posizione convenzionale in cui inserire queste classi, quindi sei libero di creare una directory nella tua applicazione per contenerle:

    'single' => [
        'driver' => 'single',
        'tap' => [App\Logging\CustomizeFormatter::class],
        'path' => storage_path('logs/laravel.log'),
        'level' => env('LOG_LEVEL', 'debug'),
        'replace_placeholders' => true,
    ],

Una volta configurata l’opzione tap nel tuo canale, sei pronto per definire la classe che personalizzerà l’istanza di Monolog. Questa classe necessita solo di un metodo: __invoke, che riceve un’istanza di Illuminate\Log\Logger. L’istanza di Illuminate\Log\Logger proxy tutte le chiamate ai metodi all’istanza sottostante di Monolog:

    <?php

    namespace App\Logging;

    use Illuminate\Log\Logger;
    use Monolog\Formatter\LineFormatter;

    class CustomizeFormatter
    {
        /**
         * Personalizza l'istanza del logger fornita.
         */
        public function __invoke(Logger $logger): void
        {
            foreach ($logger->getHandlers() as $handler) {
                $handler->setFormatter(new LineFormatter(
                    '[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
                ));
            }
        }
    }

Tutte le tue classi "tap" vengono risolte dal service container, quindi tutte le dipendenze del costruttore necessarie verranno automaticamente iniettate.

Creazione di Canali Handler Monolog

Monolog offre una varietà di handler disponibili e Laravel non include un canale predefinito per ognuno di essi. In alcuni casi, potresti voler creare un canale personalizzato che sia semplicemente un’istanza di uno specifico handler Monolog che non ha un driver di log Laravel corrispondente. Questi canali possono essere facilmente creati usando il driver monolog.

Quando si utilizza il driver monolog, l’opzione di configurazione handler serve a specificare quale handler sarà istanziato. Facoltativamente, eventuali parametri del costruttore di cui l’handler ha bisogno possono essere specificati usando l’opzione di configurazione with:

'logentries' => [
    'driver'  => 'monolog',
    'handler' => Monolog\Handler\SyslogUdpHandler::class,
    'with' => [
        'host' => 'my.logentries.internal.datahubhost.company.com',
        'port' => '10000',
    ],
],

Formattatori Monolog

Quando usi il driver monolog, il LineFormatter di Monolog viene utilizzato come formattatore predefinito. Tuttavia, puoi personalizzare il tipo di formattatore passato all’handler usando le opzioni di configurazione formatter e formatter_with:

    'browser' => [
        'driver' => 'monolog',
        'handler' => Monolog\Handler\BrowserConsoleHandler::class,
        'formatter' => Monolog\Formatter\HtmlFormatter::class,
        'formatter_with' => [
            'dateFormat' => 'Y-m-d',
        ],
    ],

Se usi un handler di Monolog che può fornire il proprio formattatore, puoi impostare il valore dell’opzione di configurazione formatter su default:

    'newrelic' => [
        'driver' => 'monolog',
        'handler' => Monolog\Handler\NewRelicHandler::class,
        'formatter' => 'default',
    ],

Processori di Monolog

Monolog può anche elaborare i messaggi prima di registrarli. Puoi creare i tuoi processori oppure utilizzare i processori esistenti offerti da Monolog.

Se desideri personalizzare i processori per un driver monolog, aggiungi un valore di configurazione processors alla configurazione del tuo canale:

'memory' => [
    'driver' => 'monolog',
    'handler' => Monolog\Handler\StreamHandler::class,
    'with' => [
        'stream' => 'php://stderr',
    ],
    'processors' => [
        // Sintassi semplice...
        Monolog\Processor\MemoryUsageProcessor::class,

        // Con opzioni...
        [
           'processor' => Monolog\Processor\PsrLogMessageProcessor::class,
           'with' => ['removeUsedContextFields' => true],
       ],
    ],
],

Creare Canali Personalizzati tramite Factory

Se desideri definire un canale completamente personalizzato in cui hai il pieno controllo sull’istanza e la configurazione di Monolog, puoi specificare un driver di tipo custom nel tuo file di configurazione config/logging.php. La tua configurazione dovrebbe includere un’opzione via che contiene il nome della classe factory che verrà utilizzata per creare l’istanza di Monolog:

'channels' => [
    'example-custom-channel' => [
        'driver' => 'custom',
        'via' => App\Logging\CreateCustomLogger::class,
    ],
],

Una volta configurato il driver custom, sei pronto a definire la classe che creerà l’istanza di Monolog. Questa classe ha bisogno solo di un metodo __invoke che deve restituire l’istanza del logger Monolog. Il metodo riceverà l’array di configurazione dei canali come unico argomento:

<?php

namespace App\Logging;

use Monolog\Logger;

class CreateCustomLogger
{
    /**
     * Crea un'istanza personalizzata di Monolog.
     */
    public function __invoke(array $config): Logger
    {
        return new Logger(/* ... */);
    }
}

Monitorare i Messaggi di Log con Pail

Spesso potresti aver bisogno di monitorare in tempo reale i log della tua applicazione. Ad esempio, durante il debug di un problema o per controllare i log in cerca di specifici tipi di errori.

Laravel Pail è un pacchetto che ti permette di esplorare facilmente i file di log della tua applicazione Laravel direttamente dalla riga di comando. A differenza del comando standard tail, Pail è progettato per funzionare con qualsiasi log driver, inclusi Sentry o Flare. Inoltre, Pail offre una serie di filtri utili per aiutarti a trovare rapidamente ciò che cerchi.

Installazione

Laravel Pail richiede PHP 8.2+ e l’estensione PCNTL.

Per iniziare, installa Pail nel tuo progetto usando il gestore di pacchetti Composer:

composer require laravel/pail

Uso

Per iniziare a visualizzare i log in tempo reale, esegui il comando pail:

php artisan pail

Per aumentare la verbosità dell’output e evitare troncamenti (…), usa l’opzione -v:

php artisan pail -v

Per la massima verbosità e per mostrare le tracce degli stack delle eccezioni, usa l’opzione -vv:

php artisan pail -vv

Per fermare la visualizzazione dei log, premi Ctrl+C in qualsiasi momento.

Filtrare i Log

--filter

Puoi usare l’opzione --filter per filtrare i log per tipo, file, messaggio e contenuto dello stack trace:

php artisan pail --filter="QueryException"

--message

Per filtrare i log in base al loro messaggio, puoi usare l’opzione --message:

php artisan pail --message="User created"

--level

L’opzione --level può essere usata per filtrare i log in base al loro livello di log:

php artisan pail --level=error

--user

Per visualizzare solo i log scritti mentre un determinato utente era autenticato, puoi fornire l’ID dell’utente all’opzione --user:

php artisan pail --user=1
Lascia un commento

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *