Vai al contenuto principale
Quando un pacchetto deve fornire un proprio output di log, separa il meccanismo che crea il logger dalla configurazione dei channel scelta dall’applicazione che usa il pacchetto. Registrare un driver non ricrea i channel esistenti, e anche dopo una modifica della configurazione può capitare che venga ancora usato il vecchio Handler. Questa pagina esamina l’implementazione di Laravel Framework v13.39.0. Per le operazioni di base consulta Logging, per la preparazione del provider consulta Sviluppo di pacchetti.

Scegliere il punto di estensione necessario

Se puoi ottenere il risultato con un driver esistente, non serve aggiungere un driver personalizzato. Le classi di via e di tap vengono risolte dal container, quindi puoi usare anche l’iniezione nel costruttore. tap è una regolazione eseguita alla creazione del channel, non un hook eseguito a ogni singola scrittura di log.
audit in logging.channels.audit è il nome del channel. Il driver al suo interno è il nome del metodo di creazione. Con Log::channel('audit') si sceglie il nome del channel, quindi anche se in seguito cambi il driver puoi mantenere invariato il nome usato dal codice chiamante.

Esporre prima una factory con custom e via

Nel pacchetto fittizio Acme\Audit forniamo una factory per l’output su file. La collochiamo in src/Logging/CreateAuditLogger.php, caricabile tramite PSR-4.
Nell’applicazione che usa il pacchetto, aggiungi il seguente elemento all’array channels esistente in config/logging.php. Mantieni le configurazioni esistenti di default e stack.
LogManager::createCustomDriver() recupera la classe di via dal container e la invoca passando l’array di configurazione del channel come unico argomento. Con questo solo approccio non serve un provider per registrare il driver.
Questa factory passa esplicitamente level allo StreamHandler. LogManager non applica in modo uniforme il level della configurazione al logger restituito da una factory custom. Anche quando aggiungi opzioni di configurazione personalizzate, applicale nella factory.
Se ti serve solo cambiare la destinazione del file, il driver single è sufficiente. Questo esempio è la configurazione minima per integrare un Handler o un’inizializzazione specifici del pacchetto. Per via usa un nome di classe memorizzabile nella cache della configurazione e non salvare closure o istanze di logger nell’array di configurazione.

Usare extend per fornire un nome di driver personalizzato

Se vuoi esporre all’utente una configurazione come driver: acme-audit, registra un driver che usa la stessa factory. Nel metodo boot() di src/AuditServiceProvider.php estendi il servizio log di Laravel.
Il provider viene caricato, ad esempio, tramite il package auto-discovery. Nell’applicazione, sostituisci la configurazione precedente di channels.audit con la seguente. via non è più necessario.
  • Il LogServiceProvider di Laravel registra log come singleton. callAfterResolving() attende se il servizio non è ancora stato risolto e, se è già risolto, applica la registrazione anche a quell’istanza. Per i dettagli consulta Hook di risoluzione dei servizi.
  • extend() registra soltanto la closure di creazione. Al momento della registrazione di questa callback non vengono creati channel né scritti log.
  • callCustomCreator() passa alla closure $app e l’array di configurazione del channel. Gli argomenti sono diversi da quelli passati alla factory di via.
  • Registrare di nuovo lo stesso nome di driver sovrascrive la closure di creazione. Non usare nomi integrati come single né nomi che possano entrare in conflitto con altri pacchetti.
Nell’implementazione esaminata, la closure anonima passata a extend() viene ricollegata allo scope di LogManager. In una closure non static, $this non è il service provider. Come nell’esempio, usa una closure static che si basa sull’argomento $app, senza dipendere dal $this del provider.
Il pacchetto fornisce il driver, mentre è l’utente a scegliere in quale channel o stack usarlo. Progetta il provider in modo che non riscriva logging.default né l’intero logging.channels esistente.

I channel vengono conservati per nome

LogManager::get() salva i channel risolti in $channels. Quando richiedi un channel con lo stesso nome, restituisce l’oggetto conservato senza rileggere l’array di configurazione. Si tratta della conservazione degli oggetti all’interno del LogManager corrente, distinta dalla cache della configurazione salvata su file. Modificare config() o registrare di nuovo extend() non basta a sostituire un Handler già creato.
In questo esempio $same è lo stesso oggetto di $before. Quando forgetChannel('audit') rimuove la conservazione nel Manager, alla richiesta successiva $after viene generato dalla nuova configurazione.

forgetChannel non aggiorna i riferimenti esistenti

forgetChannel() si limita a eliminare la voce all’interno del Manager.
  • Un servizio che conserva $before può continuare a usare il vecchio logger. Non è un’operazione che sostituisce i riferimenti esistenti con il nuovo oggetto.
  • Uno stack già generato, ad esempio con Log::channel('stack'), possiede un Monolog indipendente che raccoglie gli Handler e i Processor dei channel figli. Applicare forgetChannel() solo a un figlio non modifica la composizione dello stack esistente. Se necessario, rimuovi la conservazione anche per il nome di channel dello stack e richiedilo di nuovo.
  • forgetChannel() di per sé non chiama close() sugli Handler. Non è un’API che garantisce la chiusura di connessioni o buffer.
Nei normali deploy, includi nelle procedure operative l’aggiornamento della cache della configurazione e il riavvio dei worker di lunga durata in uso. Non affidarti a un meccanismo che riscrive la configurazione e risolve di nuovo i channel a ogni richiesta, e considera anche il ciclo di vita dei servizi.

L’integrazione in uno stack richiede anche operazioni compatibili con Monolog

Un driver personalizzato può restituire un’implementazione di Psr\Log\LoggerInterface, ma createStackDriver() di Laravel chiama getHandlers() e getProcessors() sui channel figli. Questi metodi non fanno parte dell’interfaccia PSR. Restituire un qualsiasi logger PSR non garantisce che funzioni anche in uno stack, quindi i pacchetti che supportano anche l’integrazione negli stack dovrebbero restituire un Monolog come nell’esempio, oppure documentare chiaramente l’ambito d’uso e testarlo.

Verificare la risoluzione reale dei channel, non solo i mock

Con il solo Log::shouldReceive('channel') non puoi verificare la registrazione del provider, la scelta della factory né l’applicazione della configurazione. Di seguito un esempio che, nell’ambiente di test dei pacchetti, passa per il Manager e la factory reali. Come destinazione dell’output nei test specifichiamo php://memory. Qui verifichiamo la costruzione del channel e la configurazione dell’Handler, senza scrivere effettivamente log.
LogManager::get() può catturare le eccezioni sollevate durante la creazione del channel e restituire l’emergency logger. Verificare soltanto che “non è stata sollevata alcuna eccezione” o che “si tratta di un LoggerInterface” non prova che il channel desiderato sia stato costruito. Il test qui sopra controlla anche il nome del Monolog, l’Handler e il livello di log, per non scambiare il fallback per un successo. Gli errori di scrittura successivi alla creazione non vengono necessariamente catturati dallo stesso meccanismo.

Verifiche da aggiungere per la manutenzione a lungo termine

Fonti primarie consultate

Ultima modifica il 28 ottobre 2026