Wenn Sie die verschiedenen Module für Magento 2 studieren, werden Sie feststellen, dass die Protokollierung viel seltener verwendet wird als Magento 1. Dies liegt hauptsächlich an der Tatsache, dass die Protokollierung schwieriger geworden ist. Hier möchte ich mich auf die technische Seite des Problems konzentrieren, nämlich wie Daten protokolliert werden, wie Protokolle in Ihre eigene Datei geschrieben werden und was Monolog ist.
Inhaltsverzeichnis
Monolog
Anwendungsfunktionen in Magento 2
Implementierung
Protokollierung mit dem Standardlogger
Protokollierung mit einem Standardlogger mit einem benutzerdefinierten Kanal
Schreiben in eine benutzerdefinierte Datei mit Ihrem eigenen Handler
Schreiben in eine benutzerdefinierte Datei mit virtualType
Schnelle Datenerfassung
Fazit
Monolog
Beginnen wir mit der wichtigsten Frage: Was ist Monolog und woher kommt es?
Monolog - Dies ist eine Bibliothek, die den PSR-3-Standard für die Datenprotokollierung implementiert. Es ist Monolog, das in Magento 2 zum Aufzeichnen von Protokollen verwendet wird.
PSR-3 ist wiederum ein Standard, der einen gemeinsamen Ansatz für die Datenprotokollierung und Empfehlungen für die Implementierung von Protokollierern beschreibt, die eine gemeinsame Schnittstelle bieten.
PSR-3-Highlights1. Der Logger (Objekt) muss die Schnittstelle \ Psr \ Log \ LoggerInterface implementieren.
2. Wir haben die folgenden Fehlerstufen (angegeben in der Reihenfolge der Priorität von größer nach kleiner):
NOTFALL - System ist unbrauchbar.
ALERT - Maßnahmen müssen sofort ergriffen werden. Beispiel: Ganze Website ausgefallen, Datenbank nicht verfügbar usw.
KRITISCH - Kritische Bedingungen. Beispiel: Anwendungskomponente nicht verfügbar, unerwartete Ausnahme.
FEHLER - Laufzeitfehler, die keine sofortige Aktion erfordern, aber normalerweise überwacht werden sollten.
WARNUNG - Außergewöhnliche Vorkommen, bei denen es sich nicht um Fehler handelt. Beispiel: Verwendung veralteter APIs.
HINWEIS - Normale, aber wichtige Ereignisse.
INFO - Interessante Ereignisse. Beispiel: Benutzer meldet sich an, SQL-Protokolle.
DEBUG - Detaillierte Debug-Informationen.
3. Jede Ebene hat ihre eigene Methode (Debug, Info, Benachrichtigung, Warnung, Fehler, Kritisch, Alarm, Notfall / Notfall) und es sollte auch eine Protokollmethode geben, die die Fehlerstufe als ersten Parameter verwendet.
4. Die Methoden akzeptieren eine Zeichenfolge oder etwas, das __toString () implementiert (dh Sie müssen print_r ($ message, true) manuell für Arrays verwenden oder sie im nächsten Parameter übergeben).
5. Alle Methoden akzeptieren ein $ context-Array, das das Protokoll ergänzt.
6. Kann, aber nicht unbedingt, das Ersetzen von Daten aus dem $ context-Array in die Nachricht implementiert werden. In diesem Fall wird das Format {name} empfohlen, wobei name -> der Schlüssel des Arrays im $ -Kontext ist.
Monolog ist ziemlich einfach zu bedienen. Schauen wir uns das folgende Beispiel an.
use Monolog\Logger; use Monolog\Handler\StreamHandler; use Monolog\Formatter\HtmlFormatter;
Höhepunkte der Arbeit von Monolog zu beachten:
Da PSR-3 Entwickler nicht verpflichtet, AutoCorrect-Werte im Text zu implementieren, führt Monolog dies standardmäßig nicht aus. Wenn Sie schreiben -> emerg ('test 1111 {placeholder}', ['placeholder' => 'foo']), erhalten Sie Folgendes
[2019-08-12 02:57:52] main.EMERGENCY: test 1111 {placeholder} {"placeholder": "foo"} []
Damit der Austausch funktioniert, müssen Sie einen zusätzlichen Prozessor anschließen - \ Monolog \ Processor \ PsrLogMessageProcessor.
Es ist erwähnenswert, dass Monolog eine große Anzahl von Formatierern, Prozessoren und Handlern im Auslieferungszustand hat. Sie können sie entweder verwenden oder Ihre eigenen schreiben.
Anwendungsfunktionen in Magento 2
Auf der
offiziellen Magento-Website finden Sie ein allgemeines Beispiel für die Verwendung des Loggers. Leider enthüllt das vorgestellte Beispiel nicht alle Details und beantwortet leider nicht die Frage "Wie schreibe ich Protokolle in Ihre eigene Datei?". Lassen Sie uns deshalb alles genauer verstehen.
In den Tagen von Magento 1 verwendeten wahrscheinlich früher oder später alle die Mage :: log-Methode, die überall im Code verfügbar war, und der einfachste Protokolleintrag sah aus wie Mage :: log ('ALARM!', Null, 'api.log'). Als Ergebnis hatten wir einen Datensatz des folgenden Formulars in der Datei var / log / api.log
2019-08-12T01:00:27+00:00 DEBUG (7): ALARM!
Standardformat:% Zeitstempel %% Prioritätsname% (% Priorität%):% Nachricht%.Mal sehen, wie man Daten im einfachsten Fall in Magento 2 protokolliert. Meistens verwenden Sie $ this -> _ logger-> info ('ALARM!'); (Wenn ein Objekt beispielsweise eine solche Eigenschaft geerbt hat).
Als Ergebnis eines solchen Aufrufs erhalten wir den folgenden Eintrag in der Datei var / log / system.log
[2019-08-12 02:56:43] main.INFO: ALARM! [] []
Das Standardformat ist [% datetime%]% channel%.% Level_name%:% message %% context %% extra%Wenn das Objekt keine solche Eigenschaft hat (_logger oder logger), müssen wir zuerst die Abhängigkeit \ Psr \ Log \ LoggerInterface zu Ihrer Klasse hinzufügen und das resultierende Objekt in die Eigenschaft $ logger schreiben (gemäß PSR-2 Punkt 4.2 und dem auf der Magento-Website dargestellten Beispiel ). .
Im Gegensatz zu Magento 1 gibt es hier viel mehr Nuancen.
1. Parameter für die Protokollierung.Betrachten Sie einen generischen Aufruf der Schreibmethode
$this->_logger->{level}($message, $context = []);
1) Wobei {level} gemäß PSR-3 eine der Methoden ist, die für die Protokollierung einer bestimmten Fehlerstufe reserviert sind (Debug, Info, Benachrichtigung, Warnung, Fehler, kritisch, Alarm, Notfall / Notfall).
2) $ message - im Gegensatz zu Magento 1 sollte es eine Zeichenfolge sein. Das heißt, $ object-> getData () funktioniert hier nicht. Das Datenarray muss an den nächsten Parameter übergeben werden. \ Exception-Objekte sind eine Ausnahme, da die Implementierung von \ Magento \ Framework \ Logger \ Monolog sie separat verarbeitet und automatisch -> getMessage () als $ message weiterführt, wenn das \ Exception-Objekt als Nachricht übergeben wurde.
3) $ context ist ein optionaler Parameter, ein Array.
2. Die Eigenschaft $ this -> _ logger ist nicht in allen Klassen verfügbar.Präsentiert in: Block, Helfer, Modell, Sammlung usw.
Nicht verfügbar in: ResourceModel, Controller, Comand, Setup usw.
Erfahren Sie mehr über ResourceModel und Collection.ResourceModel verfügt über die Eigenschaft _logger, wird jedoch nicht im Konstruktor ausgefüllt. Es wird nur mit der privaten Methode getLogger in \ Magento \ Framework \ Model \ ResourceModel \ AbstractResource gefüllt. Die Methode wird nur im Fehlerfall beim Schreiben in die Datenbank (im catch-Block) innerhalb der commit () -Methode aufgerufen. Bis dahin verfügt das Ressourcenmodell nicht über einen Logger.
public function commit() { $this->getConnection()->commit(); if ($this->getConnection()->getTransactionLevel() === 0) { $callbacks = CallbackPool::get(spl_object_hash($this->getConnection())); try { foreach ($callbacks as $callback) { call_user_func($callback); } } catch (\Exception $e) { $this->getLogger()->critical($e); } } return $this; } … private function getLogger() { if (null === $this->_logger) { $this->_logger = ObjectManager::getInstance()->get(\Psr\Log\LoggerInterface::class); } return $this->_logger; }
Collection hat von Anfang an einen Logger. Es wird im Konstruktor \ Magento \ Framework \ Data \ Collection \ AbstractDb zugewiesen und später vererbt.
Es ist unmöglich, dies nicht zu sagen, aber in den Controllern gibt es eine Möglichkeit, den Logger mithilfe des ObjectManager abzurufen (über die Eigenschaft $ this -> _ objectManager). Dies ist natürlich nicht der richtigste Weg.
3. Der Standardlogger und die Liste der Handler.In der globalen Datei di.xml (app / etc / di.xml) finden Sie, dass \ Psr \ Log \ LoggerInterface von der Klasse \ Magento \ Framework \ Logger \ Monolog implementiert wird, die wiederum von \ Monolog \ Logger erbt. Der Loggername ist main. Dort sind auch mehrere Handler definiert.
… <preference for="Psr\Log\LoggerInterface" type="Magento\Framework\Logger\Monolog" /> ... <type name="Magento\Framework\Logger\Monolog"> <arguments> <argument name="name" xsi:type="string">main</argument> <argument name="handlers" xsi:type="array"> <item name="system" xsi:type="object">Magento\Framework\Logger\Handler\System</item> <item name="debug" xsi:type="object">Magento\Framework\Logger\Handler\Debug</item> <item name="syslog" xsi:type="object">Magento\Framework\Logger\Handler\Syslog</item> </argument> </arguments> </type> ...
Einige Klassen unterscheiden sich von den oben aufgeführten (da sie im Magento \ Developer-Modul neu definiert wurden):
1) Magento \ Framework \ Logger \ Handler \ System (
hört auf INFO)2) Magento \ Developer \ Model \ Logger \ Handler \ Debug (
hört DEBUG ab )
3) Magento \ Developer \ Model \ Logger \ Handler \ Syslog (
hört auf DEBUG )
In den angegebenen Klassen (Debug und Syslog)
wird die Möglichkeit zum Deaktivieren der Protokollierung (dev / debug / debug_logging bzw. dev / syslog / syslog_logging) hinzugefügt.
Beachten Sie, dass die Liste der Handler, die in exception.log schreiben, keinen Ausnahmebehandler enthält. Es wird im Systemhandler aufgerufen.
Magento \ Framework \ Logger \ Handler \ System ... public function write(array $record) { if (isset($record['context']['exception'])) { $this->exceptionHandler->handle($record); return; } $record['formatted'] = $this->getFormatter()->format($record); parent::write($record); } ...
Magento 2 bis 2.2 hatten ein Problem damit, dass der Logger nach dem ersten gefundenen nicht mehr zu einem anderen Handler springen konnte. Dieses Problem wurde durch die Tatsache verursacht, dass Monolog berechnete, dass alle Handler in einem Array mit digitalen Schlüsseln und mit alphabetischen Schlüsseln (['system' =>, 'debug' =>, ...]) darauf kamen. Magento-Entwickler haben die Situation später korrigiert - sie konvertieren den Hash in ein reguläres Array mit digitalen Schlüsseln, bevor sie ihn an Monolog übergeben. Monolog hat jetzt auch den Handler-Aufzählungsalgorithmus geändert und verwendet die next () -Methode.
4. Einführung Ihres Handlers in die Liste der vorhandenen.Wir kommen zu der interessantesten Sache, die den Eindruck der Implementierung in Magento 2 ein wenig beeinträchtigt. Sie können der Liste der vorhandenen mit di.xml keinen benutzerdefinierten Handler hinzufügen, ohne ... "zusätzliche Gesten". Dies ist auf das Prinzip der Zusammenführungskonfiguration zurückzuführen.
Es gibt mehrere
Konfigurationsbereiche :
1) Initial (app / etc / di.xml)
2) Global ({moduleDir} /etc/di.xml)
3) Bereichsspezifisch ({moduleDir} / etc / {area} /di.xml dh Frontend / adminhtml / crontab / webapi_soap / webapi_rest usw.)
Innerhalb von Level 1 werden Konfigurationen zusammengeführt, aber die nächste Ebene definiert sie beim Zusammenführen neu (wenn sie auch dort deklariert sind). Dies macht es unmöglich, Handler in ihren Modulen zur vorhandenen Liste hinzuzufügen, da diese im ursprünglichen Bereich deklariert ist.
Vielleicht sehen wir in Zukunft eine Implementierung, bei der das Hinzufügen von Handlern vom ursprünglichen Bereich in ein anderes Modul verschoben und dadurch in den globalen Bereich übertragen wird.Implementierung
Schauen wir uns die wichtigsten Methoden zum Aufzeichnen von Protokollen an, die für uns bei der Implementierung von Aufgaben hilfreich sein können.
1. Protokollierung mit dem Standardlogger
Mit dieser Methode können wir problemlos Protokolle in eines der Standardprotokolle (debug.log, system.log oder exception.log) schreiben.
class RandomClass { private $logger; public function __construct(\Psr\Log\LoggerInterface $logger) { $this->logger = $logger; } public function foo() { $this->logger->info('Something went wrong');
Alles wird noch einfacher, wenn in unserer Klasse bereits eine vererbte Logger-Abhängigkeit besteht.
… $this->_logger->info('Something went wrong');
2. Protokollierung mit einem Standardlogger mit einem benutzerdefinierten Kanal
Diese Methode unterscheidet sich von der vorherigen darin, dass ein Klon des Loggers erstellt und ihm ein anderer Kanal (Name) zugewiesen wird. Dies vereinfacht die Suche in der Protokolldatei.
class RandomClass { private $logger; public function __construct(\Psr\Log\LoggerInterface $logger) { $this->logger = $logger->withName('api');
Um nach den erforderlichen Protokollen zu suchen, reicht es jetzt aus, die Suche nach "api" (der Standardprotokollierer in Magento 2 heißt main) in den vorhandenen Dateien system.log, debug.log, exception.log zu verwenden. Kann verwenden
grep -rn 'api' var/log/system.log
3. Schreiben Sie mit Ihrem eigenen Handler in eine benutzerdefinierte Datei
Erstellen wir einen einfachen Handler, der alle Fehler der kritischen Ebene und höher in einer separaten Datei var / log / Critical.log protokolliert. Fügen Sie die Möglichkeit hinzu, alle anderen Handler für eine bestimmte Fehlerstufe und höher zu blockieren. Dadurch wird vermieden, dass Daten in den Dateien debug.log und system.log dupliziert werden.
<?php namespace Oxis\Log\Logger\Handler; use Magento\Framework\Filesystem\DriverInterface; use Magento\Framework\Logger\Handler\Base; use Monolog\Logger; class Test extends Base { protected $fileName = 'var/log/critical.log'; protected $loggerType = Logger::CRITICAL; public function __construct(DriverInterface $filesystem) { parent::__construct($filesystem,null,null); $this->bubble = false;
In Magento 2 2.2+ im Konstruktor \ Magento \ Framework \ Logger \ Handler \ Base hat sich die Art und Weise der Verarbeitung des Pfads zur Protokolldatei geändert
Daher finden Sie in älteren Handlern / am Anfang von $ fileName.
Zum Beispiel ist eine kleine Erklärung wert. Da Base es Ihnen nicht erlaubt, die Bubble-Eigenschaft über Konstruktorparameter festzulegen, müssten wir entweder einen Teil des Codes aus dem Base-Konstruktor wiederholen, um den Eingabeparameter korrekt an das übergeordnete Element der Base-Klasse zu übergeben (das übrigens einen Eingabeparameter zum Festlegen dieser Eigenschaft hat) oder verwenden ein solcher Ansatz. Ich habe die zweite Option gewählt.
use Oxis\Log\Logger\Handler\Test; use Psr\Log\LoggerInterface; class RandomClass { private $logger; public function __construct( LoggerInterface $logger, Test $handler ) { $logger->pushHandler($handler);
Diese Methode zum Hinzufügen eines Handlers ist nicht ideal, ermöglicht es Ihnen jedoch, den Konfigurationsbereich des Problems zu verlassen, sodass wir alle Logger in unserer di.xml duplizieren müssen. Wenn das Ziel darin besteht, alle Logger durch Ihre eigenen zu ersetzen, ist es viel besser, den virtualType-Ansatz zu verwenden, den wir weiter betrachten werden.
4. Schreiben in eine benutzerdefinierte Datei mit virtualType
Dieser Ansatz ermöglicht es uns, die Klasse, die wir zum Schreiben von Protokollen benötigen, mit di.xml in die angegebene Protokolldatei zu zwingen. Einen ähnlichen Ansatz finden Sie in den Modulen Magento \ Payment und Magento \ Shipping. Ich mache Sie darauf aufmerksam, dass dieser Ansatz ab Magento 2 2.2 und höher funktioniert.
In Magento 2 2.2+ wurde dem Konstruktor \ Magento \ Framework \ Logger \ Handler \ Base ein neuer Parameter hinzugefügt, mit dem Sie einen virtuellen Handler erstellen und über di.xml den relativen Pfad zur Datei zum Schreiben des Protokolls angeben können. Bisher war es erforderlich, entweder den vollständigen Pfad durch $ filePath anzugeben oder einen neuen Handler zu erstellen und den relativen Pfad in die geschützte Datei $ fileName-Eigenschaft zu schreiben.
Fügen Sie in der Datei di.xml unseres Moduls Folgendes hinzu
<virtualType name="ApiHandler" type="Magento\Framework\Logger\Handler\Base"> <arguments> <argument name="fileName" xsi:type="string">var/log/api.log</argument> </arguments> </virtualType> <virtualType name="ApiLogger" type="Magento\Framework\Logger\Monolog"> <arguments> <argument name="name" xsi:type="string">api</argument> <argument name="handlers" xsi:type="array"> <item name="default" xsi:type="object">ApiHandler</item> </argument> </arguments> </virtualType> <type name="Oxis\Log\Model\A"> <arguments> <argument name="logger" xsi:type="object">ApiLogger</argument> </arguments> </type>
Fügen Sie Oxis \ Log \ Model \ A eine Logger-Klasse hinzu.
namespace Oxis\Log\Model; class A { private $logger; public function __construct(\Psr\Log\LoggerInterface $logger) { $this->logger = $logger; } public function foo() { $this->logger->info('Something went wrong'); } }
Jetzt werden absolut alle Protokolle, die in unserer Klasse geschrieben werden, von unserer Version des Loggers verarbeitet, der mit unserem Handler Protokolle in die Datei var / log / api.log schreibt.
4.1. Wenn die Klasse den Logger über das $ context-Objekt und nicht über ihren Konstruktor empfängt.Dazu gehört \ Magento \ Catalog \ Model \ Product, dessen Abhängigkeiten nicht \ Psr \ Log \ LoggerInterface haben, aber es gibt \ Magento \ Framework \ Model \ Context, über das der Logger auf die Klasseneigenschaft festgelegt wird. In diesem Fall müssen wir die obige Option etwas komplizieren und den im $ context-Objekt befindlichen Logger ersetzen. Damit dies nicht das gesamte Magento betrifft, ersetzen wir $ context nur für unsere Klasse durch virtualType.
<virtualType name="ApiHandler" type="Magento\Framework\Logger\Handler\Base"> <arguments> <argument name="fileName" xsi:type="string">var/log/api.log</argument> </arguments> </virtualType> <virtualType name="ApiLogger" type="Magento\Framework\Logger\Monolog"> <arguments> <argument name="name" xsi:type="string">api</argument> <argument name="handlers" xsi:type="array"> <item name="default" xsi:type="object">ApiHandler</item> </argument> </arguments> </virtualType> <virtualType name="ApiLogContainingContext" type="Magento\Framework\Model\Context"> <arguments> <argument name="logger" xsi:type="object">ApiLogger</argument> </arguments> </virtualType> <type name="Oxis\Log\Model\A"> <arguments> <argument name="context" xsi:type="object">ApiLogContainingContext</argument> </arguments> </type>
5. Schnelle Datenprotokollierung
Es gibt Zeiten, in denen wir die Protokollierung schnell hinzufügen müssen. In den meisten Fällen kann dies entweder auf dem Produktionsserver oder für schnelle Tests erforderlich sein.
... $log = new \Monolog\Logger('custom', [new \Monolog\Handler\StreamHandler(BP.'/var/log/custom.log')]); $log->error('test'); ...
Vorteile dieses Ansatzes: Schreibt ein Datum, es gibt einen Kontext (Array), fügt am Ende automatisch \ n hinzuIm obigen Beispiel wird \ Monolog \ Logger speziell angewendet und nicht \ Magento \ Framework \ Logger \ Monolog, wodurch es erweitert wird. Tatsache ist, dass es bei dieser Verwendung keinen Unterschied gibt, sondern weniger zu schreiben (und sich leichter daran zu erinnern).
\ Monolog \ Handler \ StreamHandler wird wiederum anstelle von \ Magento \ Framework \ Logger \ Handler \ Base verwendet, da die Verwendung von Base als Snippet aufgrund zusätzlicher Abhängigkeiten von Klassen von Drittanbietern nicht sehr praktisch ist.
Ein anderer Ansatz, über den nicht gesprochen werden kann, sind die guten alten file_put_contents.
... file_put_contents(BP.'/var/log/custom.log', 'test',FILE_APPEND); ...
Vorteile dieses Ansatzes: Schreiben Sie relativ schnell und müssen Sie sich nicht an Klassen erinnern.In beiden Fällen spielt der konstante
BP die Hauptrolle. Sie zeigt immer auf den Ordner mit Magenta (1 Stufe höher als die Kneipe), was praktisch ist und uns immer hilft, Protokolle an die richtige Stelle zu schreiben.
Fazit
Ich hoffe, die obigen Informationen waren oder werden für Sie nützlich sein.