English 日本語
preview
Meistern der Log-Einträge (Teil 3): Erkunden von Handles zum Speichern von Protokollen

Meistern der Log-Einträge (Teil 3): Erkunden von Handles zum Speichern von Protokollen

MetaTrader 5Beispiele |
108 0
joaopedrodev
joaopedrodev

Einführung

Im ersten Artikel dieser Serie, „Meistern der Log-Einträge (Teil 1): Grundlegende Konzepte und erste Schritte in MQL5“ haben wir uns an die Erstellung einer Protokollbibliothek gemacht, die auf die Entwicklung von Expert Advisors (EAs) zugeschnitten ist. Darin haben wir die Motivation für die Entwicklung eines solchen wichtigen Tools untersucht: die Überwindung der Beschränkungen der nativen Protokolle von MetaTrader 5 und die Bereitstellung einer robusten, anpassbaren und leistungsstarken Lösung für das MQL5-Universum.

Um die wichtigsten behandelten Punkte zusammenzufassen, haben wir die Grundlage für unsere Bibliothek geschaffen, indem wir die folgenden grundlegenden Anforderungen festgelegt haben:

  1. Robuste Struktur unter Verwendung des Singleton-Musters, das die Konsistenz zwischen den Code-Komponenten gewährleistet.
  2. Erweiterte Persistenz für die Speicherung von Protokollen in Datenbanken, die eine nachvollziehbare Historie für eingehende Audits und Analysen bieten.
  3. Flexibilität bei den Ausgaben, sodass die Protokolle bequem gespeichert oder angezeigt werden können, sei es in der Konsole, in Dateien, im Terminal oder in einer Datenbank.
  4. Klassifizierung nach Protokollebenen, wobei informative Meldungen von kritischen Warnungen und Fehlern unterschieden werden.
  5. Anpassung des Ausgabeformats an die individuellen Bedürfnisse der einzelnen Entwickler oder Projekte.

Mit dieser gut etablierten Grundlage wurde klar, dass das von uns entwickelte Logging-Framework weit mehr sein wird als ein einfaches Ereignisprotokoll; es wird ein strategisches Tool zum Verstehen, Überwachen und Optimieren des Verhaltens von EAs in Echtzeit sein.

In diesem dritten Artikel werden wir nun einen entscheidenden Schritt tun: das Konzept der „Handler“ (Betreuer) verstehen. Wenn die Formatierer die Daten organisieren, sind die Handler dafür verantwortlich, zu entscheiden, wohin die Protokolle gehen sollen. Sie fungieren als „Betreuer“ und leiten Nachrichten an die entsprechenden Zielorte weiter, seien es Dateien, Konsolen, Datenbanken oder sogar Benachrichtigungssysteme. In diesem Artikel werden wir die Logik hinter den Handlern verstehen, praktische Beispiele für ihre Anwendung in verschiedenen Szenarien erstellen und ihre Integration mit Formatierern untersuchen. Am Ende werden Sie über alle notwendigen Werkzeuge verfügen, um hochgradig anpassbare und effiziente Log-Streams zu erstellen. Sollen wir loslegen?


Was sind Handler?

Handler sind grundlegende Komponenten, die festlegen, wohin Protokollmeldungen gesendet werden sollen. Stellen Sie sich diese als „Message Dispatcher“ vor, die Informationen vom Logger empfangen und sie an das entsprechende Ziel weiterleiten, sei es die Konsole, eine Datei, eine E-Mail oder sogar ein Remote-Server.

Stellen Sie sich vor, Sie leiten eine Fabrik. Produkte (Protokollnachrichten) müssen zu verschiedenen Zielen transportiert werden: einige gehen an das Lager, andere an den Versand und wieder andere werden als historische Aufzeichnungen gespeichert. Der Disponent ist derjenige, der entscheidet, wohin jedes Produkt geht, und diese Rolle wird von den Abfertigern übernommen.

Jeder Handler kann spezifische Einstellungen haben, wie z. B. Schweregrade (z. B. nur Fehlermeldungen senden), Ausgabeformate (z. B. Zeitstempel einschließen oder nicht) und Ziele.

Diese Komponenten spielen eine entscheidende Rolle bei der Trennung und intelligenten Weiterleitung von Protokollnachrichten, was besonders bei mittleren und großen Anwendungen wichtig ist. Handler ermöglichen Funktionen wie die Fehlersuche in Echtzeit in der Konsole, das Speichern detaillierter Protokolle für zukünftige Analysen, das Versenden wichtiger E-Mail-Warnungen, wenn etwas Dringendes passiert, oder das Weiterleiten von Überwachungsinformationen an einen zentralen Server. All dies kann gleichzeitig erreicht werden, ohne dass komplexe Konfigurationen erforderlich sind.


Wie Handler funktionieren

Um zu verstehen, wie Handler in der Praxis funktionieren, schauen wir uns ein Beispiel aus der Bibliothek an. Das nachstehende Diagramm zeigt den grundlegenden Ablauf der Nachrichtenprotokollierung:

Im aktuellen Ablauf ist die Hauptfunktion der Klasse CLogify die Methode Append, die für den Empfang von Protokolldaten, einschließlich Schweregrad, Nachricht, Quelle und Zeitpunkt des Protokolleintrags, verantwortlich ist. Mit diesen Daten erstellt die Append-Methode eine Variable vom Typ MqlLogifyModel, die über die systemeigene Funktion Print an die Terminalkonsole gesendet wird.

Dieser Ablauf ist funktional, hat aber seine Grenzen: Alle Protokolle können nur in der Konsole angezeigt werden, und es gibt keine Möglichkeit, diese Meldungen an anderer Stelle zu verarbeiten oder zu speichern.

Mit der Einführung der Handler wurde der Ablauf nun erheblich verbessert. Schauen Sie sich das neue Ablaufdiagramm an:

Das neue Ablaufdiagramm:

  1. Die Methode Append empfängt weiterhin die Protokollinformationen (Schweregrad, Meldung, Quelle, Zeit usw.).
  2. Es wird dieselbe Variable MqlLogifyModel zum Speichern der Daten erstellt.
  3. Anstatt es direkt an die Konsole zu senden, wird das Protokoll nun an eine Liste der Handler übergeben, die durch ein Array der Handler dargestellt wird.

Jeder Handler kann die Daten unabhängig voneinander verarbeiten, sodass die Protokollmeldungen an mehrere Ziele weitergeleitet werden können.

Hier sind drei grundlegende Beispiele für Handler, die in diesem System verwendet werden können:

  • HandlerTerminal: Zeigt Protokolldaten direkt im Terminal des MetaTrader 5 an, was für Echtzeit-Diagnosen und Debugging nützlich ist.
  • HandlerFile: Speichert Protokolle in einer Datei im .txt- oder .log-Format, ideal zum Speichern von Ausführungshistorien oder zum Erstellen detaillierter Berichte für zukünftige Analysen.
  • HandlerDatabase: Speichert Protokolle in einer Datenbank, z. B. SQLite, und ermöglicht so erweiterte Datenabfragen, einschließlich Trendanalysen oder komplexere Audits.

Ich werde Ihnen ein praktisches Beispiel für die Nützlichkeit der Handler geben. Stellen Sie sich vor, Sie haben einen Handelsroboter entwickelt, der ein effizientes Protokollsystem zur Überwachung seiner Ausführung benötigt. Sie können die Handler wie folgt konfigurieren:

  • Speichern nur der DEBUG- und INFO-Meldungen in einer Datei, um alle durchgeführten Operationen, einschließlich Markteintritte und -austritte, aufzuzeichnen.
  • Anzeige von Protokollen im Terminal mit WARN- und ERROR-Meldungen, sodass Sie Probleme in Echtzeit verfolgen können.
  • Speichern der kritischen Fehler in der Datenbank, um sicherzustellen, dass relevante Informationen später von Ingenieuren oder Analytikern abgerufen und analysiert werden können.

Diese Struktur gewährleistet ein robusteres, besser organisiertes und effizienteres Protokollsystem. Handler geben Ihnen die volle Kontrolle darüber, wie und wo Protokolle verarbeitet und gespeichert werden.


Implementierung der Basisklasse Handlers

Implementieren wir eine Basisklasse namens CLogifyHandler, die als gemeinsame Basis (Elternklasse) für alle später erstellten Handler dienen wird. Wir beginnen mit der Erstellung eines Ordners namens „Handlers“ im Stammverzeichnis der Bibliothek. In diesem neuen Ordner erstellen wir eine Datei namens LogifyHandler. In der erstellten Datei wird die Klasse CLogifyHandler definiert, die zunächst nur einen einfachen Konstruktor und Destruktor hat. Der erste Code sieht wie folgt aus:

//+------------------------------------------------------------------+
//|                                                LogifyHandler.mqh |
//|                                                     joaopedrodev |
//|                       https://www.mql5.com/en/users/joaopedrodev |
//+------------------------------------------------------------------+
#property copyright "joaopedrodev"
#property link      "https://www.mql5.com/en/users/joaopedrodev"
//+------------------------------------------------------------------+
//| class : CLogifyHandler                                           |
//|                                                                  |
//| [PROPERTY]                                                       |
//| Name        : CLogifyHandler                                     |
//| Heritage    : No heritage                                        |
//| Description : Base class for all log handlers.                   |
//|                                                                  |
//+------------------------------------------------------------------+
class CLogifyHandler
  {
public:
                     CLogifyHandler(void);
                    ~CLogifyHandler(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CLogifyHandler::CLogifyHandler(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogifyHandler::~CLogifyHandler(void)
  {
  }
//+------------------------------------------------------------------+

An dieser Stelle dient die Klasse CLogifyHandler als „Skelett“, das um weitere Funktionen erweitert werden soll.

Fügen wir nun die erforderlichen Importe hinzu, beginnend mit der Datei LogifyModel.mqh, die das Protokollierungsmodell definiert, das von den Handlern verwendet werden soll.

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "../LogifyModel.mqh"

Diese Datei enthält die Definition der Klasse oder Struktur MqlLogifyModel, die zur Kapselung der Daten für jede Protokollmeldung verwendet wird, z. B. Schweregrad, Meldung und Quelle.

Der nächste Schritt besteht darin, der Klasse zwei geschützte Attribute hinzuzufügen:

  • m_name: Speichert den Namen des Handlers, der für die Identifizierung bei der Fehlersuche oder für Berichte nützlich sein kann.
  • m_level: Legt den Schweregrad fest, den der Handler verarbeiten soll (z. B. DEBUG, INFO, ERROR).

Darüber hinaus werden wir öffentliche Methoden zum Setzen und Abrufen dieser Werte erstellen.

class CLogifyHandler
  {
protected:
   string            m_name;
   ENUM_LOG_LEVEL    m_level;
   
public:
   //--- Set/Get
   void              SetLevel(ENUM_LOG_LEVEL level);
   string            GetName(void);
   ENUM_LOG_LEVEL    GetLevel(void);
  };
//+------------------------------------------------------------------+
//| Set level                                                        |
//+------------------------------------------------------------------+
void CLogifyHandler::SetLevel(ENUM_LOG_LEVEL level)
  {
   m_level = level;
  }
//+------------------------------------------------------------------+
//| Get name                                                         |
//+------------------------------------------------------------------+
string CLogifyHandler::GetName(void)
  {
   return(m_name);
  }
//+------------------------------------------------------------------+
//| Get level                                                        |
//+------------------------------------------------------------------+
ENUM_LOG_LEVEL CLogifyHandler::GetLevel(void)
  {
   return(m_level);
  }
//+------------------------------------------------------------------+

Das Attribut m_name kann in abgeleiteten Klassen nur über deren Konstruktoren gesetzt werden, um Sicherheit und Kapselung zu erzwingen. Aus diesem Grund gibt es auch keine Methode SetName.

Es gibt drei grundlegende Methoden, die alle Handler implementieren müssen:

  1. Emit(MqlLogifyModel &data): Verarbeitet eine Protokollmeldung und sendet sie an das angegebene Ziel (Datei, Konsole, Datenbank usw.).
  2. Flush(): Beendet oder bereinigt alle offenen Vorgänge.
  3. Close(): Schließt den Handler und gibt alle zugehörigen Ressourcen frei.

Im Moment werden diese Funktionen als virtuell definiert, mit leeren Standardimplementierungen. Dadurch kann jede abgeleitete Klasse das Verhalten nach Bedarf anpassen.

class CLogifyHandler
  {
public:
   //--- Handler methods
   virtual void      Emit(MqlLogifyModel &data);         // Processes a log message and sends it to the specified destination
   virtual void      Flush(void);                        // Clears or completes any pending operations
   virtual void      Close(void);                        // Closes the handler and releases any resources
  };
//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandler::Emit(MqlLogifyModel &data)
  {
  }
//+------------------------------------------------------------------+
//| Clears or completes any pending operations                       |
//+------------------------------------------------------------------+
void CLogifyHandler::Flush(void)
  {
  }
//+------------------------------------------------------------------+
//| Closes the handler and releases any resources                    |
//+------------------------------------------------------------------+
void CLogifyHandler::Close(void)
  {
  }
//+------------------------------------------------------------------+

In diesem Stadium tun die Methoden nichts, da es den untergeordneten Klassen (z. B. HandlerFile, HandlerDatabase usw.) obliegt, das spezifische Verhalten der einzelnen Handler zu implementieren.


Implementierung der Handler

Nachdem die Basisklasse CLogifyHandler implementiert ist, können wir damit beginnen, spezielle Handler zu erstellen, die von ihr abgeleitet werden. Dieser Ansatz folgt den Grundprinzipien der objektorientierten Programmierung, wie Vererbung und Polymorphismus, und ermöglicht Modularität und Flexibilität im Code. Jeder spezialisierte Handler ist für die spezifische Behandlung von Protokollen zuständig, wobei er die Vorteile der gemeinsamen Struktur der Basisklasse nutzt, aber seine eigene Logik für die Methoden Emit, Flush und Close implementiert.

Bevor wir die Handler implementieren, sollten wir unser Projekt besser strukturieren. Wir erstellen drei neue Dateien im Ordner „Handlers“, und zwar LogifyHandlerConsole.mqh, LogifyHandlerDatabase.mqh, und LogifyHandlerFile.mqh. Die endgültige Struktur wird wie folgt aussehen:

In der Datei LogifyHandlerConsole.mqh erstellen wir die Klasse CLogifyHandlerConsole, die die Attribute und Methoden der Basisklasse CLogifyHandler erbt. Eine der ersten Änderungen betrifft den Wert der Variablen m_name, die im Klassenkonstruktor auf „console“ gesetzt wurde. Dies wird dazu beitragen, den Handler zur Laufzeit eindeutig zu identifizieren. Hier ist die anfängliche Definition der Klasse:

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "LogifyHandler.mqh"
//+------------------------------------------------------------------+
//| class : CLogifyHandlerConsole                                    |
//|                                                                  |
//| [PROPERTY]                                                       |
//| Name        : CLogifyHandlerConsole                              |
//| Heritage    : CLogifyHandler                                     |
//| Description : Log handler, inserts data into terminal window.    |
//|                                                                  |
//+------------------------------------------------------------------+
class CLogifyHandlerConsole : public CLogifyHandler
  {
public:
                     CLogifyHandlerConsole(void);
                    ~CLogifyHandlerConsole(void);
   
   virtual void      Emit(MqlLogifyModel &data);         // Processes a log message and sends it to the specified destination
   virtual void      Flush(void);                        // Clears or completes any pending operations
   virtual void      Close(void);                        // Closes the handler and releases any resources
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CLogifyHandlerConsole::CLogifyHandlerConsole(void)
  {
   m_name = "console";
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogifyHandlerConsole::~CLogifyHandlerConsole(void)
  {
  }
//+------------------------------------------------------------------+

Die Funktion Emit ist in erster Linie dafür zuständig, eine Protokollmeldung zu verarbeiten und an das entsprechende Ziel zu senden. Im Falle der Konsole bedeutet dies einfach, dass die formatierte Nachricht im MetaTrader-Terminal angezeigt wird. Hier ist die Implementierung der Methode:

//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerConsole::Emit(MqlLogifyModel &data)
  {
   if(data.level >= this.GetLevel())
     {
      Print("Console handler: ",data.formated);
     }
  }
//+------------------------------------------------------------------+

Bevor die Meldung angezeigt wird, wird geprüft, ob die Protokollstufe (data.level) mit der im Handler konfigurierten Stufe übereinstimmt. Dadurch wird sichergestellt, dass nur wichtige oder relevante Meldungen angezeigt werden.

Nach dem gleichen Prinzip wie beim Konsolen-Handler können wir weitere spezialisierte Handler erstellen, z. B. für Datenbanken und Dateien. Die zu ändernden Dateien sind LogifyHandlerDatabase.mqh und LogifyHandlerFile.mqh. Auch wenn diese Handler dieselbe Grundlogik haben, können ihre spezifischen Implementierungen von Emit variieren.

Dieser Handler ist so konzipiert, dass er die Protokolle in einer Datenbank speichert, obwohl wir zu Demonstrationszwecken zunächst eine Meldung auf der Konsole anzeigen. Der Code für die Funktion „Emit“ würde lauten:

//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerDatabase::Emit(MqlLogifyModel &data)
  {
   if(data.level >= this.GetLevel())
     {
      Print("Database handler: ",data.formated);
     }
  }
//+------------------------------------------------------------------+

Der LogifyHandlerFile-Handler wird verwendet, um Protokolle in eine bestimmte Datei zu schreiben. Hier ist eine erste Implementierung für Emit:

//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerFile::Emit(MqlLogifyModel &data)
  {
   if(data.level >= this.GetLevel())
     {
      Print("File handler: ",data.formated);
     }
  }
//+------------------------------------------------------------------+

Obwohl wir die Flush- und Close-Methoden in der Basisklasse definiert haben, müssen nicht alle Handler sie sofort implementieren.

  • Die Methode Flush kann in komplexeren Abläufen nützlich sein, z. B. bei Schreiboperationen in Dateien oder beim Echtzeit-Streaming.
  • Die Methode Close ist unerlässlich, um Ressourcen freizugeben, z. B. Datenbankverbindungen, oder Schreibströme zu schließen.

Im Falle des Handlers der Konsole bleiben diese Methoden leer, da keine zusätzlichen Operationen durchgeführt werden müssen. Denken Sie daran, dass ich hier nur Codefragmente zeige, die vollständige Version steht am Ende des Artikels zum Download bereit.


Hinzufügen von Handlern zur Klasse CLogify

Nachdem wir nun die Handler implementiert haben und sie isoliert arbeiten, ist es an der Zeit, sie in die Hauptklasse der Bibliothek namens CLogify zu integrieren. Dazu importieren wir zunächst die Basisklasse CLogifyHandler, die die notwendige Struktur für alle Handler enthält:

#include "Handlers/LogifyHandler.mqh"
#include "Handlers/LogifyHandlerConsole.mqh"
#include "Handlers/LogifyHandlerDatabase.mqh"
#include "Handlers/LogifyHandlerFile.mqh"

In der Implementierung der Klasse CLogify werden wir ein privates Attribut hinzufügen, um die zu verwendenden Handler zu speichern. Ich habe ein Array von Zeigern des Typs CLogifyHandler gewählt, da die Handler dynamisch verwaltet werden sollen.

Darüber hinaus verfügt die Klasse über spezifische Methoden zur Verwaltung der Handler:

  • AddHandler: Fügt einen neuen Handler in das Array ein.
  • HasHandler: Prüft, ob ein bestimmter Handler bereits in der Liste vorhanden ist, wobei sein Name als Kriterium dient.
  • GetHandler: Ruft einen Handler ab, entweder nach Name oder nach Index im Array.
  • SizeHandler: Gibt die Gesamtzahl der Handler in der Liste zurück.

Hier ist der aktualisierte Code für die Klasse CLogify, jetzt mit diesen Methoden:

class CLogify
  {
private:
   CLogifyHandler    *m_handlers[];
   
public:
   //--- Handler
   void              AddHandler(CLogifyHandler *handler);
   bool              HasHandler(string name);
   CLogifyHandler    *GetHandler(string name);
   CLogifyHandler    *GetHandler(int index);
   int               SizeHandlers(void);
  };
//+------------------------------------------------------------------+
//| Add handler to handlers array                                    |
//+------------------------------------------------------------------+
void CLogify::AddHandler(CLogifyHandler *handler)
  {
   int size = ArraySize(m_handlers);
   ArrayResize(m_handlers,size+1);
   m_handlers[size] = GetPointer(handler);
  }
//+------------------------------------------------------------------+
//| Checks if handler is already in the array by name                |
//+------------------------------------------------------------------+
bool CLogify::HasHandler(string name)
  {
   int size = ArraySize(m_handlers);
   for(int i=0;i<size;i++)
     {
      if(m_handlers[i].GetName() == name)
        {
         return(true);
        }
     }
   return(false);
  }
//+------------------------------------------------------------------+
//| Get handler by name                                              |
//+------------------------------------------------------------------+
CLogifyHandler *CLogify::GetHandler(string name)
  {
   int size = ArraySize(m_handlers);
   for(int i=0;i<size;i++)
     {
      if(m_handlers[i].GetName() == name)
        {
         return(m_handlers[i]);
        }
     }
   return(NULL);
  }
//+------------------------------------------------------------------+
//| Get handler by index                                             |
//+------------------------------------------------------------------+
CLogifyHandler *CLogify::GetHandler(int index)
  {
   return(m_handlers[index]);
  }
//+------------------------------------------------------------------+
//| Gets the total size of the handlers array                        |
//+------------------------------------------------------------------+
int CLogify::SizeHandlers(void)
  {
   return(ArraySize(m_handlers));
  }
//+------------------------------------------------------------------+

Nachdem die Methoden zur Verwaltung der Handler fertig sind, können wir die Funktion Append so anpassen, dass sie die Handler bei der Verarbeitung von Protokollen verwendet.

Die Append-Methode durchläuft nun alle verfügbaren Handler im Array und ruft die Emit-Methode jedes einzelnen auf, sodass das Protokoll an das entsprechende Ziel (z. B. Konsole, Datenbank usw.) gesendet wird.

Hier ist der aktualisierte Code für die Append-Methode:

//+------------------------------------------------------------------+
//| Generic method for adding logs                                   |
//+------------------------------------------------------------------+
bool CLogify::Append(ENUM_LOG_LEVEL level,string msg, string origin = "", string args = "",string filename="",string function="",int line=0)
  {
   //--- If the formatter is not configured, the log will not be recorded.
   if(m_formatter == NULL)
     {
      return(false);
     }
   
   //--- Textual name of the log level
   string levelStr = "";
   switch(level)
     {
      case LOG_LEVEL_DEBUG: levelStr = "DEBUG"; break;
      case LOG_LEVEL_INFOR: levelStr = "INFOR"; break;
      case LOG_LEVEL_ALERT: levelStr = "ALERT"; break;
      case LOG_LEVEL_ERROR: levelStr = "ERROR"; break;
      case LOG_LEVEL_FATAL: levelStr = "FATAL"; break;
     }
   
   //--- Creating a log template with detailed information
   datetime time_current = TimeCurrent();
   MqlLogifyModel data("",levelStr,msg,args,time_current,time_current,level,origin,filename,function,line);
   data.formated = m_formatter.FormatLog(data);
   
   //--- Call handlers
   int size = this.SizeHandlers();
   for(int i=0;i<size;i++)
     {
      m_handlers[i].Emit(data);
     }
   
   return(true);
  }
//+------------------------------------------------------------------+

Einige Beobachtungen:

  • Das formatierte Protokoll wird in data.formatted gespeichert, um sicherzustellen, dass die vollständigen Informationen für alle Bearbeiter verfügbar sind.
  • Jeder Handler verarbeitet das Protokoll unabhängig und ruft seine Emit-Methode auf.

Die letzte Anpassung, die ich vornehmen werde, besteht darin, die Array-Zeiger im Destruktor der Klasse zu löschen:

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogify::~CLogify()
  {
   //--- Delete formatter
   if(m_formatter != NULL)
     {
      delete m_formatter;
     }
   
   //--- Delete handlers
   int size_handlers = ArraySize(m_handlers);
   for(int i=0;i<size_handlers;i++)
     {
      delete m_handlers[i];
     }
  }
//+------------------------------------------------------------------+


Testen der Handler

In diesem Beispiel wird die bereits erwähnte Testdatei mit dem Namen LogifyTest.mq5 verwendet. Unser Ziel ist es, zu demonstrieren, wie zwei Log-Handler mit jeweils unterschiedlichen Log-Levels konfiguriert und verwendet werden und wie diese Logs auf der Grundlage der konfigurierten Filter aufgezeichnet werden.

Zunächst erstellen wir zwei Handler, die für die Aufzeichnung von Protokollen an verschiedenen Stellen und auf verschiedenen Ebenen zuständig sind:

  • Konsole: Dabei handelt es sich um eine Instanz von CLogifyHandlerConsole, die so konfiguriert ist, dass sie DEBUG-Meldungen aufzeichnet, d. h. alle Meldungen, von den ausführlichsten bis hin zu kritischen Fehlern.
  • Datei: Es handelt sich dabei um eine Instanz von CLogifyHandlerFile, die so konfiguriert ist, dass sie nur Meldungen der Stufe INFO erfasst und DEBUG-Meldungen herausfiltert.

Hier ist der Code zur Konfiguration dieser Handler:

int OnInit()
  {
   //--- Console
   CLogifyHandler *handler_console = new CLogifyHandlerConsole();
   handler_console.SetLevel(LOG_LEVEL_DEBUG);
   
   //--- File
   CLogifyHandler *handler_file = new CLogifyHandlerFile();
   handler_file.SetLevel(LOG_LEVEL_INFOR);
   
   return(INIT_SUCCEEDED);
  }

Nachdem wir die Handler konfiguriert haben, fügen wir sie zu unserer Basisklasse CLogify hinzu, die für die Verwaltung aller Anwendungsprotokolle zuständig ist.

Außerdem fügen wir einen Formatierer hinzu, um festzulegen, wie die Protokolle angezeigt werden sollen. Der Formatierer in diesem Beispiel formatiert die Protokolle nach dem Muster: “Stunde:Minute:Sekunde, [Protokollstufe], Meldung“.

Nachdem wir die Handler und den Formatierer konfiguriert haben, werden wir drei Protokollmeldungen mit unterschiedlichen Stufen ausgeben. Im Folgenden finden Sie den vollständigen Code für diesen Schritt:

int OnInit()
  {
   //--- Console
   CLogifyHandler *handler_console = new CLogifyHandlerConsole();
   handler_console.SetLevel(LOG_LEVEL_DEBUG);
   
   //--- File
   CLogifyHandler *handler_file = new CLogifyHandlerFile();
   handler_file.SetLevel(LOG_LEVEL_INFOR);
   
   //--- Config
   logify.SetFormatter(new CLogifyFormatter("hh:mm:ss","{date_time} [{levelname}] {msg}"));
   logify.AddHandler(handler_console);
   logify.AddHandler(handler_file);
   
   //--- Logs
   logify.Debug("Debug Message");
   logify.Infor("Information Message");
   logify.Error("Error Message");
   
   return(INIT_SUCCEEDED);
  }

Wenn wir den obigen Code ausführen, erhalten wir die folgende Ausgabe in der Konsole:

Console handler: 03:20:05 [DEBUG] Debug Message
Console handler: 03:20:05 [INFOR] Information Message
File handler: 03:20:05 [INFOR] Information Message
Console handler: 03:20:05 [ERROR] Error Message
File handler: 03:20:05 [ERROR] Error Message

Das Ergebnis verstehen:

  • Konsolen-Handler (handler_console): Dieser Handler erfasst alle Meldungen, von DEBUG bis ERROR. Daher wurden drei Einträge in der Konsole vorgenommen, einer für jedes ausgegebene Protokoll.
  • Datei-Handler (handler_file): Dieser Handler wiederum wurde so konfiguriert, dass er nur Meldungen der Stufe INFO oder höher aufzeichnet. Daher wurde das DEBUG-Protokoll ignoriert und nur INFO- und ERROR-Meldungen aufgezeichnet, was insgesamt zwei Einträge in der Protokolldatei ergab.


Schlussfolgerung

In diesem Artikel haben wir einen wichtigen Schritt beim Aufbau unserer MQL5-Protokollbibliothek getan. Wir haben das Konzept der Handler erforscht und ihre wesentliche Rolle als „Dirigenten“ von Protokollmeldungen an verschiedene Ziele verstanden. Wir haben gesehen, wie sie mit Formatierern zusammenarbeiten und ein kohärentes und modulares System für die Protokollverarbeitung bilden.

In der Praxis haben wir die Grundstruktur der Handler geschaffen, indem wir eine abstrakte Klasse definiert haben, die als Grundlage für alle zukünftigen Implementierungen dienen wird. Wir haben auch drei erste Handler entwickelt: Console, Database und File, die für die Weiterleitung von Protokollen an die Konsole, eine Datenbank bzw. Dateien zuständig sind. Obwohl sie im Moment alle die Print()-Funktion für die Simulation verwenden, erlaubt uns diese solide Grundlage, jede Klasse mit definitiven Funktionen in zukünftigen Artikeln zu erweitern und zu spezialisieren.

In den Tests haben wir die Integration zwischen Handlern und unserer Bibliothek validiert und gezeigt, wie sie flexibel hinzugefügt und verwendet werden können. Dabei zeigte sich das Potenzial von Handlern als modulare Komponenten, die an unterschiedliche Anforderungen bei der Protokollierung angepasst werden können.


Der gesamte in diesem Artikel verwendete Code ist unten angefügt. Hier finden Sie eine Tabelle mit der Beschreibung der einzelnen Dateien in der Bibliothek:

Datei Name
Beschreibung
Experts/Logify/LogiftTest.mq5
Datei, in der wir die Funktionen der Bibliothek testen, mit einem praktischen Beispiel
Include/Logify/Formatter/LogifyFormatter.mqh
Klasse, die für die Formatierung von Protokolldatensätzen zuständig ist, indem sie Platzhalter durch bestimmte Werte ersetzt
Include/Logify/Handlers/LogifyHandler.mqh
Basisklasse für die Verwaltung von Log-Handlern, einschließlich der Einstellung des Levels und des Versands von Logs
Include/Logify/Handlers/LogifyHandlerConsole.mqh
Log-Handler, der formatierte Logs direkt an die Terminal-Konsole im MetaTrader sendet
Include/Logify/Handlers/LogifyHandlerDatabase.mqh
Log-Handler, der formatierte Logs an eine Datenbank sendet (derzeit enthält er nur einen Ausdruck, aber bald werden wir ihn in einer echten Sqlite-Datenbank speichern)
Include/Logify/Handlers/LogifyHandlerFile.mqh
Log-Handler, der formatierte Logs in eine Datei sendet (derzeit enthält er nur einen Ausdruck, aber bald werden wir ihn in einer echten Datei speichern)
Include/Logify/Logify.mqh
Kernklasse für die Protokollverwaltung, die Ebenen, Modelle und Formatierung integriert
Include/Logify/LogifyLevel.mqh
Datei, die die Log-Ebenen der Logify-Bibliothek definiert und eine detaillierte Kontrolle ermöglicht
Include/Logify/LogifyModel.mqh
Struktur, die die Protokolleinträge modelliert, einschließlich Details wie Ebene, Nachricht, Zeitstempel und Kontext
Im nächsten Artikel werden wir jeden Handler vollständig implementieren und untersuchen, wie man Protokolle in Dateien speichert, Daten in Datenbanken speichert und bestimmte Ausgaben im Terminal konfiguriert.


Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/16866

Beigefügte Dateien |
LogifyfPart3a.zip (11.96 KB)
Hidden Markov Modelle für trendfolgende Volatilitätsprognosen Hidden Markov Modelle für trendfolgende Volatilitätsprognosen
Hidden Markov Modelle (HMM) sind leistungsstarke statistische Instrumente, die durch die Analyse beobachtbarer Kursbewegungen die zugrunde liegenden Marktzustände identifizieren. Im Handel verbessern HMM die Volatilitätsprognose und liefern Informationen für Trendfolgestrategien, indem sie Marktverschiebungen modellieren und antizipieren. In diesem Artikel stellen wir das vollständige Verfahren zur Entwicklung einer Trendfolgestrategie vor, die HMM zur Prognose der Volatilität als Filter einsetzt.
MQL5 Handels-Toolkit (Teil 7): Erweitern der History Management EX5-Bibliothek um die Funktionen für den zuletzt stornierten, schwebenden Auftrag MQL5 Handels-Toolkit (Teil 7): Erweitern der History Management EX5-Bibliothek um die Funktionen für den zuletzt stornierten, schwebenden Auftrag
Erfahren Sie, wie Sie das letzte Modul in der Bibliothek des History Manager EX5 erstellen, wobei Sie sich auf die Funktionen konzentrieren, die für die Bearbeitung des zuletzt stornierten, schwebenden Auftrags verantwortlich sind. Damit haben Sie die Möglichkeit, wichtige Details zu stornierten offenen Aufträgen mit MQL5 effizient abzurufen und zu speichern.
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 7): Der EA Signal Pulse Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 7): Der EA Signal Pulse
Nutzen Sie das Potenzial der Multi-Timeframe-Analyse mit „Signal Pulse“, einem MQL5 Expert Advisor, der Bollinger Bänder und den Stochastik Oszillator integriert, um präzise, hochwahrscheinliche Handelssignale zu liefern. Erfahren Sie, wie Sie diese Strategie umsetzen und Kauf- und Verkaufschancen mithilfe von nutzerdefinierten Pfeilen effektiv visualisieren können. Ideal für Händler, die ihr Urteilsvermögen durch automatisierte Analysen über mehrere Zeitrahmen hinweg verbessern möchten.
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 8): Metrics Board Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 8): Metrics Board
Als eines der leistungsstärksten Toolkits zur Analyse von Preisaktionen wurde das „Metrics Board“ entwickelt, um die Marktanalyse zu rationalisieren, indem es wichtige Marktmetriken mit nur einem Mausklick bereitstellt. Jede Schaltfläche dient einer bestimmten Funktion, sei es die Analyse von Hoch-/Tief-Trends, Volumen oder anderen Schlüsselindikatoren. Dieses Tool liefert genaue Daten in Echtzeit, wenn Sie sie am meisten brauchen. In diesem Artikel wollen wir uns die Funktionen genauer ansehen.