Naar hoofdinhoud gaan
Wanneer je vanuit een package eigen logoutput wilt aanbieden, scheid je het mechanisme dat de logger aanmaakt van de channelconfiguratie die de gebruikende applicatie kiest. Alleen een driver registreren bouwt bestaande channels niet opnieuw op, en ook na een configuratiewijziging kan een oude Handler nog in gebruik zijn. Deze pagina is gecontroleerd aan de hand van de implementatie van Laravel Framework v13.39.0. Zie Logging voor de basisbediening en Packageontwikkeling voor het voorbereiden van de provider.

Kies het juiste uitbreidingspunt

Als het met een bestaande driver kan, hoef je geen eigen driver toe te voegen. De klassen voor via en tap worden via de container geresolved, dus je kunt ook constructor-injectie gebruiken. tap is een aanpassing bij het aanmaken van het channel, geen hook die bij elke geschreven logregel wordt uitgevoerd.
De audit in logging.channels.audit is de channelnaam. De driver daarin is de naam van de aanmaakmethode. Met Log::channel('audit') kies je de channelnaam, zodat de aanroepende kant dezelfde naam kan blijven gebruiken, ook als je later de driver wijzigt.

Publiceer eerst een factory met custom en via

In het fictieve package Acme\Audit bieden we een factory voor bestandsoutput aan. Plaats die in src/Logging/CreateAuditLogger.php, zodat hij via PSR-4 kan worden geladen.
Voeg in de gebruikende applicatie het volgende element toe aan de bestaande channels-array in config/logging.php. Laat bestaande instellingen zoals default en stack staan.
LogManager::createCustomDriver() haalt de via-klasse uit de container en roept die aan met de configuratie-array van het channel als enige argument. Met alleen deze aanpak heb je geen provider nodig om een driver te registreren.
Deze factory geeft level expliciet door aan de StreamHandler. LogManager past de level uit de configuratie niet automatisch toe op de logger die een custom-factory teruggeeft. Ook als je eigen configuratie-opties toevoegt, verwerk je die in de factory.
Als je alleen de bestemming van het logbestand wilt wijzigen, volstaat de single-driver. Dit voorbeeld is de minimale opzet om een eigen Handler of initialisatie van het package in te bouwen. Gebruik voor via een klassenaam die in de configuratiecache kan worden opgeslagen, en sla geen closures of logger-instanties op in de configuratie-array.

Gebruik extend om een eigen drivernaam aan te bieden

Als je de gebruikende kant een instelling als driver: acme-audit wilt bieden, registreer je een driver die dezelfde factory gebruikt. Breid in boot() van src/AuditServiceProvider.php de log-service van Laravel uit.
Laad de provider bijvoorbeeld via automatische packagedetectie. Vervang aan de gebruikende kant channels.audit door de volgende configuratie in plaats van de vorige. via is dan niet meer nodig.
  • De LogServiceProvider van Laravel registreert log als singleton. callAfterResolving() wacht als de service nog niet is geresolved, en past de registratie ook toe op de instantie als die al is geresolved. Zie Hooks na het resolven van services voor details.
  • extend() registreert alleen de closure die de logger aanmaakt. Bij het registreren van deze callback wordt er nog geen channel aangemaakt of log geschreven.
  • callCustomCreator() geeft $app en de configuratie-array van het channel door aan de closure. Dat zijn andere argumenten dan die aan de via-factory worden doorgegeven.
  • Een driver met dezelfde naam opnieuw registreren overschrijft de aanmaakclosure. Gebruik geen ingebouwde namen zoals single of namen die met andere packages botsen.
In de gecontroleerde implementatie wordt een anonieme closure die aan extend() wordt doorgegeven opnieuw gebonden aan de scope van LogManager. $this in een niet-statische closure is dus niet de service provider. Gebruik zoals in het voorbeeld een statische closure met het argument $app, en maak de implementatie niet afhankelijk van $this van de provider.
Het package levert de driver, en de gebruikende kant kiest in welk channel of welke stack die wordt gebruikt. Ontwerp de provider zo dat hij logging.default of de volledige bestaande logging.channels niet overschrijft.

Channels worden per naam vastgehouden

LogManager::get() slaat geresolvede channels op in $channels. Als je een channel met dezelfde naam opnieuw ophaalt, wordt de configuratie-array niet opnieuw gelezen, maar krijg je het vastgehouden object terug. Dit staat los van de configuratiecache die in een bestand wordt opgeslagen: het gaat om het vasthouden van objecten binnen de huidige LogManager. Alleen config() wijzigen of extend() opnieuw registreren vervangt reeds aangemaakte Handlers niet.
In dit voorbeeld is $same hetzelfde object als $before. Zodra forgetChannel('audit') het vasthouden in de Manager opheft, wordt bij de volgende aanroep $after aangemaakt op basis van de nieuwe configuratie.

forgetChannel werkt bestaande referenties niet bij

forgetChannel() verwijdert alleen de entry in de Manager.
  • Services die $before vasthouden, kunnen daarna de oude logger blijven gebruiken. Het is geen bewerking die bestaande referenties vervangt door het nieuwe object.
  • Een stack die al is aangemaakt, bijvoorbeeld met Log::channel('stack'), heeft een eigen Monolog-instantie waarin de Handlers en Processors van de onderliggende channels zijn verzameld. Als je alleen een onderliggend channel met forgetChannel() vergeet, verandert de opbouw van de bestaande stack niet. Hef zo nodig ook het vasthouden voor de channelnaam van die stack op en haal hem opnieuw op.
  • forgetChannel() roept zelf geen close() aan op de Handler. Het is geen API die het afsluiten van verbindingen of buffers garandeert.
Neem bij een normale deploy het bijwerken van de configuratiecache en het herstarten van gebruikte langlopende workers op in je werkwijze. Vertrouw niet op een mechanisme dat per request de configuratie wijzigt en opnieuw resolvet, en houd ook rekening met de levenscyclus van services.

Voor gebruik in een stack zijn ook Monolog-compatibele methoden nodig

Een eigen driver kan een implementatie van Psr\Log\LoggerInterface teruggeven, maar createStackDriver() van Laravel roept getHandlers() en getProcessors() aan op de onderliggende channels. Die maken geen deel uit van de PSR-interface. Een willekeurige PSR-logger teruggeven betekent dus niet automatisch dat hij ook in een stack werkt. Packages die ook gebruik in een stack ondersteunen, geven zoals in het voorbeeld een Monolog-instantie terug, of documenteren en testen expliciet het ondersteunde gebruik.

Verifieer niet alleen met mocks, maar ook de echte channelresolutie

Met alleen Log::shouldReceive('channel') kun je de registratie van de provider, de keuze van de factory en het toepassen van de configuratie niet controleren. Het volgende voorbeeld laat in de testomgeving voor packages de echte Manager en factory doorlopen. Gebruik php://memory als bestemming voor de testoutput. Hier verifiëren we de opbouw van het channel en de instellingen van de Handler, zonder daadwerkelijk logs te schrijven.
LogManager::get() vangt exceptions op die tijdens het aanmaken van een channel optreden en kan dan de emergency logger teruggeven. Controleren dat “er geen exception optrad” of dat “het een LoggerInterface was” bewijst dus niet dat het bedoelde channel is opgebouwd. De test hierboven controleert ook de naam, de Handler en het logniveau van de Monolog-instantie, zodat een fallback niet ten onrechte als succes wordt gezien. Schrijffouten na het aanmaken worden niet noodzakelijk via hetzelfde mechanisme opgevangen.

Extra verificaties voor langdurig onderhoud

Geraadpleegde primaire bronnen

Laatst gewijzigd op 28 oktober 2026