v13.39.0. Die Grundlagen finden Sie unter Logging, die Vorbereitung des Providers unter Paketentwicklung.
Den passenden Erweiterungspunkt wählen
Wenn sich das Ziel mit einem vorhandenen Treiber erreichen lässt, müssen Sie keinen eigenen Treiber hinzufügen.
Die Klassen für
via und tap werden über den Container aufgelöst, sodass auch Konstruktorinjektion möglich ist. tap ist eine Anpassung bei der Erzeugung des Channels und kein Hook, der bei jedem einzelnen Log-Eintrag ausgeführt wird.
audit in logging.channels.audit ist der Channel-Name. Das darin enthaltene driver ist der Name der Erzeugungsmethode. Mit Log::channel('audit') wählen Sie den Channel-Namen, sodass der Name auf Aufruferseite erhalten bleibt, auch wenn Sie später den Treiber ändern.Zuerst eine Factory mit custom und via veröffentlichen
Im fiktiven PaketAcme\Audit stellen wir eine Factory für die Ausgabe in eine Datei bereit. Sie wird in src/Logging/CreateAuditLogger.php abgelegt, das per PSR-4 geladen werden kann.
channels-Array in config/logging.php das folgende Element hinzu. Die vorhandenen Einstellungen für default und stack bleiben erhalten.
LogManager::createCustomDriver() bezieht die Klasse aus via über den Container und ruft sie mit dem Konfigurations-Array des Channels als einzigem Argument auf. Bei diesem Ansatz allein ist kein Provider für die Treiberregistrierung nötig.
level explizit an den StreamHandler. Der LogManager wendet das level aus der Konfiguration nicht pauschal auf den Logger an, den eine custom-Factory zurückgibt. Auch wenn Sie eigene Konfigurationsoptionen hinzufügen, müssen diese in der Factory berücksichtigt werden.
Für eigene Treibernamen extend verwenden
Wenn Sie der nutzenden Seite eine Konfiguration wiedriver: acme-audit anbieten möchten, registrieren Sie einen Treiber, der dieselbe Factory verwendet. In boot() von src/AuditServiceProvider.php erweitern Sie den log-Service von Laravel.
channels.audit durch die folgende Konfiguration anstelle der vorherigen. via wird nicht mehr benötigt.
- Der
LogServiceProvidervon Laravel registriertlogals Singleton.callAfterResolving()wartet, solange der Service noch nicht aufgelöst ist, und wendet die Registrierung auch auf die Instanz an, wenn er bereits aufgelöst wurde. Details finden Sie unter Hooks nach der Service-Auflösung. extend()registriert lediglich die Erzeugungs-Closure. Beim Registrieren dieses Callbacks werden weder Channels erzeugt noch Logs geschrieben.callCustomCreator()übergibt der Closure$appund das Konfigurations-Array des Channels. Das unterscheidet sich von den Argumenten, die an einevia-Factory übergeben werden.- Eine erneute Registrierung desselben Treibernamens überschreibt die Erzeugungs-Closure. Verwenden Sie keine eingebauten Namen wie
singleund keine Namen, die mit anderen Paketen kollidieren.
logging.default noch die gesamten bestehenden logging.channels überschreibt.
Channels werden pro Name zwischengespeichert
LogManager::get() speichert aufgelöste Channels in $channels. Wird ein Channel mit demselben Namen erneut abgerufen, liest der Manager das Konfigurations-Array nicht neu ein, sondern gibt das zwischengespeicherte Objekt zurück.
Dies ist nicht der in Dateien gespeicherte Konfigurations-Cache, sondern das Vorhalten von Objekten im aktuellen LogManager. Eine Änderung über config() oder eine erneute Registrierung mit extend() allein tauscht bereits erzeugte Handler nicht aus.
$same dasselbe Objekt wie $before. Sobald forgetChannel('audit') den Eintrag im Manager entfernt, wird beim nächsten Abruf $after aus der neuen Konfiguration erzeugt.
forgetChannel aktualisiert keine bestehenden Referenzen
forgetChannel() entfernt lediglich den Eintrag im Manager.
- Services, die
$beforehalten, können den alten Logger weiterhin verwenden. Die Operation ersetzt bestehende Referenzen nicht durch das neue Objekt. - Ein bereits erzeugter Stack, etwa über
Log::channel('stack'), besitzt ein eigenständiges Monolog, das die Handler und Processors der untergeordneten Channels sammelt. Wenn Sie nur einen untergeordneten Channel mitforgetChannel()entfernen, ändert sich die Zusammensetzung des bestehenden Stacks nicht. Entfernen Sie bei Bedarf auch den Eintrag für den Channel-Namen des Stacks und rufen Sie ihn neu ab. forgetChannel()selbst ruftclose()der Handler nicht auf. Die API garantiert nicht das Beenden von Verbindungen oder Puffern.
Für die Einbindung in einen Stack sind auch Monolog-kompatible Operationen nötig
Ein eigener Treiber kann eine Implementierung vonPsr\Log\LoggerInterface zurückgeben, doch createStackDriver() von Laravel ruft bei untergeordneten Channels getHandlers() und getProcessors() auf. Diese sind nicht Teil des PSR-Interfaces.
Da ein beliebiger PSR-Logger nicht zwangsläufig auch in einem Stack funktioniert, sollten Pakete, die auch die Einbindung in einen Stack unterstützen, wie im Beispiel ein Monolog zurückgeben oder den Einsatzbereich explizit angeben und testen.
Nicht nur Mocks, sondern die tatsächliche Channel-Auflösung prüfen
MitLog::shouldReceive('channel') allein lassen sich die Registrierung des Providers, die Auswahl der Factory und die Übernahme der Konfiguration nicht prüfen. Das folgende Beispiel nutzt die Testumgebung für Pakete und durchläuft den tatsächlichen Manager und die Factory.
Als Ausgabeziel für Tests wird php://memory angegeben. Hier werden der Aufbau des Channels und die Einstellungen des Handlers geprüft, ohne tatsächlich Logs zu schreiben.
Zusätzliche Prüfungen für die langfristige Wartung
Verwendete Primärquellen
- Offizielle Laravel-Dokumentation: Logging
- Framework v13.39.0: Erzeugung, Erweiterung und Freigabe von Channels im LogManager
- Framework v13.39.0: Umhüllung und Methodenweiterleitung im Logger
- Framework v13.39.0: Singleton-Registrierung im LogServiceProvider
- Framework v13.39.0: ServiceProvider::callAfterResolving
- Framework v13.39.0: RebindsCallbacksToSelf
- Offizielle Laravel-Tests: LogManagerTest