English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Búsqueda de errores y registros

Búsqueda de errores y registros

MetaTrader 5Ejemplos | 11 marzo 2014, 11:48
1 573 0
Дмитрий Александрович
Дмитрий Александрович

Introducción

¡Hola, apreciado lector!

En este artículo vamos examinar las formas de búsqueda de errores en los Expert Advisors/Scripts/Indicadores y los métodos de registro. Además le voy a proporcionar un pequeño programa para visualizar los registros - LogMon.

La búsqueda de errores es una parte esencial de la programación. Al escribir un nuevo bloque de código, es necesario comprobar si funciona correctamente y si no tiene errores lógicos. Puede encontrar un error en su programa mediante tres métodos distintos:

  1. Evaluación del resultado final  
  2. Depuración paso a paso  
  3. Escritura de los pasos lógicos en el registro

Vamos a ver cada método:

1. Evaluación del resultado final

Con este método, analizamos el funcionamiento del programa o parte de su método. Por ejemplo, cogemos un código sencillo, y que tenga un error evidente, solo para verlo mejor:

void OnStart()
  {
//---
   int intArray[10];
   for(int i=0;i<9;i++)
     {
      intArray[i]=i;
     }
   Alert(intArray[9]);

  }

Compilamos y ejecutamos, y la pantalla mostrará "0". Mediante el análisis de los resultados, esperamos un "9", por lo que llegamos a la conclusión de que el programa no está funcionando correctamente. Este método de búsqueda de errores es muy común, pero no puede encontrar la ubicación del error. Vamos a ver el segundo método de búsqueda de errores, utilizando la depuración.  

2. Depuración paso a paso

Este método permite encontrar la ubicación exacta del error lógico del programa. Ponga un punto de interrupción dentro del bucle "for", comience con la depuración y observe la variable i:

Depuración

A continuación haga clic en "Reanudar depuración" las veces que considere necesarias para procesar por completo el funcionamiento del programa. Observamos que cuando la variable i tiene el valor "8", salimos del bucle, por lo tanto, concluimos que el error está en esta línea:

for(int i=0;i<9;i++)

Es decir, cuando se compara el valor de i con el número 9. Cambiamos la línea "i<9" " por "i<10" o "i<=9", y comprobamos los resultados. Obtenemos el número 9, exactamente lo que esperábamos. Mediante la depuración, hemos aprendido cómo funciona el programa durante su ejecución y pudimos solucionar el problema. Las desventajas de este método:

  1. La ubicación del error no es intuitiva.
  2. Tenemos que añadir variables a la lista de observación (Watch) y visualizarlas después de cada paso.
  3. Este método no puede detectar errores durante la ejecución de un programa completo, como si fuera un EA haciendo trading con una cuenta real o de prueba.

Finalmente, vamos a considerar el tercer método de búsqueda de errores.

3. Escritura de los paso lógicos en el registro

Mediante este método, guardamos un registro de los pasos más relevantes de nuestro programa. Por ejemplo: inicialización, hacer una transacción, cálculos del indicador, etc. Actualizamos nuestro script con una línea de código. Es decir, vamos a mostrar el valor de la variable i a cada iteración:

void OnStart()
  {
//---
   int intArray[10];
   for(int i=0;i<9;i++)
     {
      intArray[i]=i;
      Alert(i);
     }
   Alert(intArray[9]);

  }

Ejecutamos y observamos en la salida del registro los números "0 1 2 3 4 5 6 7 8 0". Nos damos cuenta a qué se debe esto y corregimos el script, al igual que en el caso anterior.

Los pros y los contras de este método de búsqueda de errores:

  1. + No hay necesidad de ejecutar el programa paso a paso, por lo tanto de ahorra tiempo.
  2. + La ubicación del error, suele ser obvia.
  3. + Podemos seguir registrando durante la ejecución del programa.
  4. + Podemos guardar el registro para el análisis y las comparaciones posteriores (por ejemplo, durante la escritura en un archivo. Ver más abajo).
  5. - Aumenta el tamaño del código fuente, debido a los operadores añadidos, que escriben los datos en el registro.
  6. - Mayor tiempo de ejecución del programa (especialmente importante para la optimización).

Resumen:

El primer método de búsqueda de errores no puede rastrear la ubicación real del error. Lo utilizamos principalmente por su rapidez. El segundo método, la depuración paso a paso, nos permite encontrar la ubicación exacta del error, requiere mucho tiempo. Y si nos perdemos en el bloque del código deseado, tendremos que empezar de nuevo.

Finalmente, el tercer método, guardar pasos lógicos en un registro, nos permite analizar rápidamente el funcionamiento del programa y guardar el resultado. Mientras escribe los eventos de su Expert Advisor/Indicadores/Scripts en el registro, puede encontrar un error fácilmente y no tiene que buscar las condiciones adecuadas para que ocurra un error, y tampoco tiene que depurar su programa durante horas y horas. A continuación, vamos a ver en detalle los métodos de registro y compararlos. También voy a proporcionar el método más cómodo y rápido.

¿Cuando tiene que registrar?

Estos son algunos motivos por los que hay que registrar:
  1. Comportamiento erróneo del programa.
  2. Tiempo de ejecución del programa demasiado largo (optimización).
  3. Seguimiento del tiempo de ejecución (mostrar notificaciones de apertura/cierre de posiciones, operaciones ejecutadas, etc.)  
  4. Aprendizaje de MQL5, por ejemplo, mostrar matrices.  
  5. Comprobación de los Expert Advisors antes del Campeonato, etc.

Métodos de registro

Hay muchas maneras de escribir mensajes en un registro, algunas se utilizan en todos los casos, mientras que otras se necesitan en casos especiales. Por ejemplo, enviar el registro por email o ICQ no es siempre necesario.  

Esta es la lista de los métodos más comunes utilizados en la programación con MQL5:

  1. Mediante la función Comment()
  2. Mediante la función Alert()
  3. Mediante la función Print()
  4. Escribir el registro en un archivo mediante la función FileWrite()

A continuación voy a proporcionar algunos ejemplos de cada método con códigos fuente y describiré las características de cada método. Estos códigos fuente son bastante abstractos, así que no nos alejaremos mucho de la esencia.

Mediante la función Comment()

void OnStart()
  {
//---
   int intArray[10];
   for(int i=0;i<10;i++)
     {
      intArray[i]=i;
      Comment("Variable i: ",i);
      Sleep(5000);
     }
   Alert(intArray[9]);
  }

Como resultado, vemos el valor actual de la variable "i" en la esquina superior izquierda:

Comment()

De modo que podemos hacer un seguimiento del estado actual de programa en ejecución. Ahora los pros y los contras:

  1. + Podemos ver el valor inmediatamente.
  2. - Restricción de la salida.
  3. - No podemos seleccionar ningún mensaje específico.
  4. - Solo podemos ver el funcionamiento en el estado actual, no durante todo el tiempo de ejecución.
  5. - Método relativamente lento.
  6. - No es muy adecuado para hacer el seguimiento del funcionamiento, ya que siempre hay que estar observando las lecturas.

La función Comment() es muy útil para mostrar el estado actual del Expert Advisor. Por ejemplo, "Open 2 deal" o "buy GBRUSD lot: 0.7".  

Mediante la función Alert()

Esta función muestra los mensajes en ventanas separadas con una notificación sonora. Un ejemplo del código:

void OnStart()
  {
//---
   Alert("Start script");
   int intArray[10];
   for(int i=0;i<10;i++)
     {
      intArray[i]=i;
      Alert("Variable i:", I);
      Sleep(1000);
     }
   Alert(intArray[9]);
   Alert("Stop script");
  }

El resultado de la ejecución del código:  

Alert()

Ahora estamos en el séptimo cielo, todo es obvio de inmediato, e incluso con sonido. Pero ahora los pros y los contras:

  1. + Todos los mensajes se registran de forma coherente.
  2. + Notificación sonora.
  3. + Todo se escribe en el archivo "Terminal_dir\MQL5\Logs\data.txt".
  4. - Todos los mensajes de los Scripts/Expert Advisors/Indicadores se escriben en un solo registro.
  5. - No funciona en el Probador de Estrategias.
  6. - Al ser llamado con frecuencia, se puede congelar el terminal durante mucho tiempo (por ejemplo, al llamar con cada tick o cuando se muestra la matriz en el bucle).
  7. - No se pueden agrupar los mensajes.
  8. - Visualización incomoda del archivo de registro.
  9. - No puede guardar los mensajes en una carpeta distinta a la carpeta de datos estándar.

El sexto punto es muy crítico en el trading real, sobre todo durante la especulación o la modificación del Loss Stop. Hay bastantes más pros y contras y puede encontrar otros, pero creo que es suficiente.  

Mediante la función Print()

Esta función escribe los mensajes del registro en una ventana especial llamada "Experts". Este es el código:

void OnStart()
  {
//---
   Print("Script start");
   int intArray[10];
   for(int i=0;i<10;i++)
     {
      intArray[i]=i;
      Print("Variable i: ",i);
     }
   Print(intArray[9]);
   Print("Stop script");
  }

Print()

Como puede ver, se llama a esta función de la misma manera que la función Alert(), pero ahora, todos los mensajes se escriben sin notificaciones en la pestaña "Experts" y el archivo "Terminal_dir\MQL5\Logs\data.txt". Vamos a ver los pros y los contras de este método:  

  1. + Todos los mensajes se registran de forma coherente.
  2. + Todo se escribe en el archivo "Terminal_dir\MQL5\Logs\data.txt".
  3. + Adecuado para el registro continuo del funcionamiento del programa.
  4. - Todos los mensajes de los Scripts/Expert Advisors/Indicadores se escriben en un solo registro.
  5. - No se pueden agrupar los mensajes.
  6. - Visualización incomoda del archivo de registro.
  7. - No puede guardar los mensajes en una carpeta distinta a la carpeta de datos estándar.

Este es probablemente el método utilizado por la mayoría de los programadores en MQL5, es bastante rápido y muy adecuado para la grabación de un gran número de registros.

Escritura del registro en un archivo

Vamos a considerar el último método de registro -la escritura del registro en archivos. Este método es mucho más complicado que todos los anteriores, sin embargo, con un diseño adecuado, garantiza una buena velocidad y una visualización cómoda del registro y de las notificaciones. Este es el más sencillo código para la escritura del registro en un archivo:

void OnStart()
  {
//--- Open log file
   int fileHandle=FileOpen("log.txt",FILE_WRITE|FILE_TXT|FILE_SHARE_READ|FILE_UNICODE); 
   FileWrite(fileHandle,"Start script");
   int intArray[10];
   for(int i=0;i<10;i++)
     {
      intArray[i]=i;
      FileWrite(fileHandle,"Variable i: ",i);
      // Sleep(1000);
     }
   FileWrite(fileHandle,intArray[9]);
   FileWrite(FileHandle,"Stop script");
   FileClose(fileHandle); // close log file
  }

Ejecutamos y nos vamos a la carpeta "Terminal_dir\MQL5\Files" y abrimos el archivo "log.txt" en el editor de texto. Estos son los contenidos:

Registro en archivo

Como puede observar, la salida es coherente, ningún mensaje extra, solo lo que hemos escrito en el archivo. Vamos a ver los pros y los contras:

  1. + Rápido.
  2. + Escribe solo lo que queremos.
  3. + Podemos escribir mensajes a partir de distintos programas en distintos archivos, excluyendo así la intersección de registros.
  4. - No hay notificaciones de nuevos mensajes en el registro.
  5. - No se puede distinguir entre un mensaje específico y la categoría del mensaje.
  6. - Requiere mucho tiempo para abrir el registro, tenemos que ir a la carpeta y abrir el archivo.

Resumen:

Todos los métodos mencionados antes tienen sus inconvenientes, pero algunos de ellos se pueden corregir. Los tres primeros métodos no son lo suficientemente flexibles, y casi no podemos influir en su comportamiento. Sin embargo, el último método, Escritura del registro en un archivo, es más flexible, podemos decidir cómo y cuando se registran los mensajes.. Si lo que queremos es mostrar un solo número, por supuesto que es más fácil utilizar los tres primeros métodos. Pero si tenemos un programa complicado con un montón de código, será difícil utilizarlo sin registro.


Un nuevo enfoque para registrar


Ahora le voy comentar y enseñar cómo puede mejorar el registro en un archivo y le voy a proporcionar una herramienta muy práctica para visualizar los registros. Es una aplicación para Windows, que he implementado en C++ y la he llamado LogMon.

Vamos a empezar con la implementación de la clase que va a realizar todos los registros, a saber:

  1. Guardar la ubicación del archivo en el cual se escribirá el registro y otras configuraciones del registro.
  2. Crear archivos de registro en función del nombre y la fecha/hora.
  3. Convertir los parámetros enviados en línea de registro.
  4. Añadir el tiempo a los mensajes de registro.
  5. Añadir color del mensaje.
  6. Añadir categoría del mensaje.
  7. Poner los mensajes en el caché y escribirlos una vez cada n segundos o cada n mensajes.

Puesto que MQL5 es un lenguaje orientado a objetos y su velocidad no es muy distinta a la de C++, escribiremos una clase específica para MQL5. Comencemos:


Implementación de la clase de escritura del registro en un archivo

Pondremos nuestra clase en un archivo de inclusión independiente y con extensión mqh. Esta es la estructura general de la clase.

CLogger

Y ahora el código fuente de la clase con comentarios detallados:

//+------------------------------------------------------------------+
//|                                                      Clogger.mqh |
//|                                                             ProF |
//|                                                          http:// |
//+------------------------------------------------------------------+
#property copyright "ProF"
#property link      "http://"

// Max size of cache (quantity)
#define MAX_CACHE_SIZE   10000
// Max file size in megabytes
#define MAX_FILE_SIZEMB 10
//+------------------------------------------------------------------+
//|   Logger                                                         |
//+------------------------------------------------------------------+
class CLogger
  {
private:
   string            project,file;             // Name of project and log file
   string            logCache[MAX_CACHE_SIZE]; // Cache max size
   int               sizeCache;                // Cache counter
   int               cacheTimeLimit;           // Caching time
   datetime          cacheTime;                // Time of cache last flush into file
   int               handleFile;               // Handle of log file
   string            defCategory;              // Default category
   void              writeLog(string log_msg); // Writing message into log or file, and flushing cache
public:
   void              CLogger(void){cacheTimeLimit=0; cacheTime=0; sizeCache=0;};    // Constructor
   void             ~CLogger(void){};                                               // Destructor
   void              SetSetting(string project,string file_name,
                                string default_category="",int cache_time_limit=0); // Settings
   void              init();                   // Initialization, open file for writing
   void              deinit();                 // Deinitialization, closing file
   void              write(string msg,string category="");                                         // Generating message
   void              write(string msg,string category,color colorOfMsg,string file="",int line=0); // Generating message
   void              write(string msg,string category,uchar red,uchar green,uchar blue,
                           string file="",int line=0);                                             // Generating message
   void              flush(void);              // Flushing cache into file

  };
//+------------------------------------------------------------------+
//|  Settings                                                        |
//+------------------------------------------------------------------+
void CLogger::SetSetting(string project_name,string file_name,
                        string default_category="",int cache_time_limit=0)
  {
   project=project_name;             // Project name
   file=file_name;                   // File name
   cacheTimeLimit=cache_time_limit;  // Caching time
   if(default_category=="")          // Setting default category
     {  defCategory="Comment";   }
     else
     {defCategory = default_category;}
  }
//+------------------------------------------------------------------+
//|  Initialization                                                  |
//+------------------------------------------------------------------+
void CLogger::init(void)
  {
   string path;
   MqlDateTime date;
   int i=0;
   TimeToStruct(TimeCurrent(),date);                            // Get current time
   StringConcatenate(path,"log\\log_",project,"\\log_",file,"_",
                     date.year,date.mon,date.day);              // Generate path and file name
   handleFile=FileOpen(path+".txt",FILE_WRITE|FILE_READ|
                       FILE_UNICODE|FILE_TXT|FILE_SHARE_READ);  // Open or create new file
   while(FileSize(handleFile)>(MAX_FILE_SIZEMB*1000000))        // Check file size
     {
      // Open or create new log file
      i++;
      FileClose(handleFile);
      handleFile=FileOpen(path+"_"+(string)i+".txt",
                          FILE_WRITE|FILE_READ|FILE_UNICODE|FILE_TXT|FILE_SHARE_READ);
     }
   FileSeek(handleFile,0,SEEK_END);                             // Set pointer to the end of file
  }
//+------------------------------------------------------------------+
//|   Deinitialization                                               |
//+------------------------------------------------------------------+
void CLogger::deinit(void)
  {
   FileClose(handleFile); // Close file
  }
//+------------------------------------------------------------------+
//|   Write message into file or cache                               |
//+------------------------------------------------------------------+
void CLogger::writeLog(string log_msg)
  {
   if(cacheTimeLimit!=0)  // Check if cache is enabled
     {
      if((sizeCache<MAX_CACHE_SIZE-1 && TimeCurrent()-cacheTime<cacheTimeLimit)
         || sizeCache==0) // Check if cache time is out or if cache limit is reached
        {
         // Write message into cache
         logCache[sizeCache++]=log_msg;
        }
      else
        {
         // Write message into cache and flush cache into file
         logCache[sizeCache++]=log_msg;
         flush();
        }

     }
   else
     {
      // Cache is disabled, immediately write into file
      FileWrite(handleFile,log_msg);
     }
   if(FileTell(handleFile)>(MAX_FILE_SIZEMB*1000000)) // Check current file size
     {
      // File size exceeds allowed limit, close current file and open new
      deinit();
      init();
     }
  }
//+------------------------------------------------------------------+
//|   Generate message and write into log                            |
//+------------------------------------------------------------------+
void CLogger::write(string msg,string category="")
  {
   string msg_log;
   if(category=="")                // Check if passed category exists
     {   category=defCategory;   } // Set default category

// Generate line and call method of writing message
   StringConcatenate(msg_log,category,":|:",TimeToString(TimeCurrent(),TIME_SECONDS),"    ",msg);
   writeLog(msg_log);
  }
//+------------------------------------------------------------------+
//|    Generate message and write into log                           |
//+------------------------------------------------------------------+
void CLogger::write(string msg,string category,color colorOfMsg,string file="",int line=0)
  {
   string msg_log;
   int red,green,blue;
   red=(colorOfMsg  &Red);           // Select red color from constant
   green=(colorOfMsg  &0x00FF00)>>8; // Select green color from constant
   blue=(colorOfMsg  &Blue)>>16;     // Select blue color from constant
                                     // Check if file or line are passed, generate line and call method of writing message
   if(file!="" && line!=0)
     {
      StringConcatenate(msg_log,category,":|:",red,",",green,",",blue,
                        ":|:",TimeToString(TimeCurrent(),TIME_SECONDS),"    ",
                        "file: ",file,"   line: ",line,"   ",msg);
     }
   else
     {
      StringConcatenate(msg_log,category,":|:",red,",",green,",",blue,
                        ":|:",TimeToString(TimeCurrent(),TIME_SECONDS),"    ",msg);
     }
   writeLog(msg_log);
  }
//+------------------------------------------------------------------+
//|    Generate message and write into log                           |
//+------------------------------------------------------------------+
void CLogger::write(string msg,string category,uchar red,uchar green,uchar blue,string file="",int line=0)
  {
   string msg_log;

// Check if file or line are passed, generate line and call method of writing message
   if(file!="" && line!=0)
     {
      StringConcatenate(msg_log,category,":|:",red,",",green,",",blue,
                        ":|:",TimeToString(TimeCurrent(),TIME_SECONDS),"    ",
                        "file: ",file,"   line: ",line,"   ",msg);
     }
   else
     {
      StringConcatenate(msg_log,category,":|:",red,",",green,",",blue,
                        ":|:",TimeToString(TimeCurrent(),TIME_SECONDS),"    ",msg);
     }
   writeLog(msg_log);
  }
//+------------------------------------------------------------------+
//|    Flush cache into file                                         |
//+------------------------------------------------------------------+
void CLogger::flush(void)
  {
   for(int i=0;i<sizeCache;i++) // In loop write all messages into file
     {
      FileWrite(handleFile,logCache[i]);
     }
   sizeCache=0; // Reset cache counter
   cacheTime=TimeCurrent(); // Set time of reseting cache
  }
//+------------------------------------------------------------------+

En MetaEditor creamos un archivo de inclusión (.mqh), y copiamos el código fuente de la clase y lo guardamos con el nombre "CLogger.mqh". Vamos a hablar ahora de cada método y de cómo utilizar la clase.

Utilización de la clase CLogger

Para empezar a guardar los mensajes en el registro mediante esta clase, tenemos que incluir el archivo de la clase en Expert Advisor/Indicator/Script:

#include <CLogger.mqh>

A continuación, tenemos que crear un objeto de esta clase:

CLogger logger;

Vamos a realizar todas las operaciones con el objeto "logger". Ahora tenemos que ajustar la configuración llamando al método "SetSetting()". Tenemos que pasar el nombre del proyecto y el nombre del archivo en este método. También hay dos parámetros opcionales -el nombre de la categoría por defecto y el período de vida del caché (en segundos) durante el cual se almacena el caché antes de ser escrito en un archivo. Si especificamos cero, se escribirán todos los mensajes una vez.

SetSetting(string project,             // Project name
           string file_name,           // Log file name
           string default_category="", // Default category
           int cache_time_limit=0      // Cache lifetime in seconds
           );

Ejemplos de llamada:

logger.SetSetting("MyProject","myLog","Comment",60);

Como resultado, se escribirán los mensajes en el archivo "Client_Terminal_dir\MQL5\Files\log\log_MyProject\log_myLog_date.txt", la categoría por defecto es "Comment" y la duración del caché es de 60 segundos. Después tenemos que llamar al método init() para abrir/crear el archivo de registro. El ejemplo de la llamada es muy sencillo, ya que no hay que pasar ningún parámetro:  

logger.init();

Este método genera la ruta y el nombre del archivo de registro, lo abre y comprueba si no excede el tamaño máximo. Si el tamaño excede el valor de la constante establecida previamente, entonces se abre otro archivo y se concatena 1 a su nombre. A continuación, se comprueba otra vez el tamaño hasta que se abra el archivo con el tamaño correcto.

Luego se mueve el puntero a la posición del final del archivo. El objeto ya está listo para escribir el registro. Hemos reemplazado el método escrito. Gracias a esto, podemos establecer distintas estructuras de mensajes, ejemplo de una llamada al método de escritura y el resultado en el archivo:

// Write message with default caegory
logger.write("Test message");
// Write message with "Errors" category
logger.write("Test message", "Errors");
// Write message with "Errors" category, that will be highlighted with red color in LogMon
logger.write("Test message", "Errors",Red);
// Write message with "Errors" category, that will be highlighted with red color in LogMon
// Also message will contain current file name and current line
logger.write("Test message", "Errors",Red,__FILE__,__LINE__);
// Write message with "Errors" category, that will be highlighted with GreenYellow color in LogMon
// But now we specify each color independently as: red, green, blue. 0-black, 255 - white
logger.write("Test message", "Errors",173,255,47);
// Write message with "Errors" category, that will be highlighted with GreenYellow color in LogMon
// But now we specify each color independently as: red, green, blue. 0-black, 255 - white
// Also message will contain current file name and current line
logger.write("Test message", "Errors",173,255,47,__FILE__,__LINE__);

El archivo de registro tendrá las siguientes líneas:

Comment:|:23:13:12    Test message
Errors:|:23:13:12    Test message
Errors:|:255,0,0:|:23:13:12    Test message
Errors:|:255,0,0:|:23:13:12    file: testLogger.mq5   line: 27   Test message
Errors:|:173,255,47:|:23:13:12    Test message
Errors:|:173,255,47:|:23:13:12    file: testLogger.mq5   line: 29   Test message

Como puede ver, todo es sencillo. Llamamos al método write() con los parámetros necesarios desde cualquier lugar y se escribe el mensaje en el archivo. Al final de su programa, tiene que insertar la llamada de dos métodos, flush() y deinit().

logger.flush();  // Forcibly flush cache to hard disk
logger.deinit(); // Close the log file

A continuación, se muestra un ejemplo sencillo del script que escribe los números en un bucle del registro:

//+------------------------------------------------------------------+
//|                                                   testLogger.mq5 |
//|                                                             ProF |
//|                                                          http:// |
//+------------------------------------------------------------------+
#property copyright "ProF"
#property link      "http://"
#property version   "1.00"
#include <Сlogger.mqh>
CLogger logger;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+

void OnStart()
  {
//---
   logger.SetSetting("proj","lfile");      // Settings
   logger.init();                          // Initialization
   logger.write("Start script","system");  
   for(int i=0;i<100000;i++)               // Write 100000 messages to the log
     {
      logger.write("log: "+(string)i,"Comment",100,222,100,__FILE__,__LINE__);
     }
   logger.write("Stop script","system"); 
   logger.flush();                         // Flush buffer
   logger.deinit();                        // Deinitialization
  }
//+------------------------------------------------------------------+

Se ejecuta el script en tres segundos y crea dos archivos:

Archivos de registro

Contenidos de los archivos:

Contenidos de los archivos de registro

Y así, los 100.000 mensajes. Como se puede observar, todo funciona muy rápido. Se puede modificar esta clase, añadir nuevas funciones u optimizarla.

Nivel de salida de los mensajes


Ahora que hemos escrito un programa, tenemos que ser capaces de mostrar distintos tipos de mensajes:

  1. Errores críticos (el programa no funciona correctamente)
  2. Notificaciones de errores no críticos, operaciones de trading, etc. (el programa está experimentando errores temporales, o el programa ha realizado una operación importante, y hay que notificárselo al usuario).
  3. Depuración de la información (contenidos de las matrices y variables, y otros datos necesarios para el trabajo real).

También se recomienda poder configurar qué mensajes se desea mostrar, sin modificar el código fuente. Vamos a implementar este planteamiento como una función y no utilizaremos ni clases ni métodos.

Declaramos el parámetro de la variable que almacenará el nivel de salida de los mensajes. Cuanto mayor es el número en la variable, más categorías de mensajes se mostrarán. Para desactivar la salida de mensajes del todo, se le asigna el valor "-1.

input int dLvl=2;

A continuación, está el código fuente de la función que hay que declarar después de crear el objeto de la clase CLogger.

void debug(string debugMsg,             // Message text
          int lvl        )              // Message level
{
   if (lvl<=dLvl)                       // Compare message level with level of messages output
   {
       if (lvl==0)                      // If message is critical (level = 0)
       {logger.write(debugMsg,"",Red);} // mark it with red color
       else
       {logger.write(debugMsg);}        // Else print it with default color
   }
}

Ahora un ejemplo: asignamos el valor "0" a los mensajes más importantes, y cualquier número (en orden ascendente desde cero) a los menos útiles:

debug("Error in Expert Advisor!",0);      // Critical error
debug("Stop-Loss execution",1);      // Notification
int i = 99;
debug("Variable i:"+(string)i,2); // Debugging information, variable contents

Visualización fácil de registros mediante LogMon

Bueno, ahora tenemos archivos de registro que contienen miles de líneas. Pero es bastante complicado buscar información en los mismos. No están divididos en categorías y no se diferencian los unos de los otros. He tratado de resolver este problema, a saber, escribiendo un programa para visualizar los registros generados por la clase CLogger. Ahora le ofrezco una breve introducción al programa LogMon, que está escrito en C++ mediante WinAPI. Por esto, es rápido y de tamaño reducido. El programa es absolutamente gratuito.

Para poder trabajar con el programa, hace falta:

  1. Copiarlo en la carpeta "Client_Terminal_dir\MQL5\Files\" y ejecutarlo -en el caso del modo normal.
  2. Copiarlo en la carpeta "Agents_dir\Agent\MQL5\Files\" y ejecutarlo -en el caso del modo de prueba o de optimización.

La ventana principal del programa se ve a continuación:

Ventana principal de LogMon

La ventana principal contiene la barra de herramientas y la ventana con la vista de árbol. Para expandir un elemento, hacemos doble clic en el mismo con el botón izquierdo del ratón. Los repertorios en la lista son proyectos, ubicados en la carpeta "Client_Terminal_dir\MQL\Files\log\". Establezcamos el nombre del proyecto en la clase CLogger mediante el método SetSetting(). Los archivos en la lista del repertorio son los archivos de registro. Los mensajes en los archivos de registro están divididos en categorías que hemos especificado en el método write (). Los números entre paréntesis representan los números de mensajes en esta categoría.

Vamos a examinar ahora los botones en la barra de herramientas de izquierda a derecha.

Botón para borrar un proyecto o un archivo de registro, así como para restablecer la vista de árbol

Al pulsar este botón, aparece la siguiente ventana:

Delete, Flush

Al pulsar el botón "Delete and Flush", se detendrán todas las cadenas de escaneo de archivos/carpetas, se reiniciará el árbol, y se le pregunta si quiere eliminar el archivo o proyecto (simplemente haga clic en un elemento para seleccionarlo -no tiene que marcar la casilla!). El botón "Reset" detendrá todas los cadenas de escaneo de archivos/carpetas y vaciará la vista de árbol.

Botón para ver la ventana de diálogo "About" (Acerca de)

Muestra una breve información acerca del programa y su autor.

Botón para mostrar la ventana del programa siempre en primer plano

Coloca la ventana del programa encima de todas las demás ventanas.

Botón para habilitar el seguimiento de nuevos mensajes en los archivos de registro

Este botón oculta la ventana del programa en la bandeja del sistema Bandeja del sistema y habilita el seguimiento de nuevos mensajes en los archivos de registro. Para elegir el proyecto/archivo/categoría a escanear, marque la casilla junto al elemento en cuestión.

Si marca la casilla situada al lado de la categoría de mensajes, la notificación activará un nuevo mensaje en este proyecto/archivo/categoría. Si marca la casilla situada al lado del archivo, la notificación activará un nuevo mensaje en este archivo para cualquier categoría. Finalmente, si marca la casilla situada al lado del proyecto, la notificación activará un nuevo archivo de registro y mensajes en el mismo.

Seguimiento

Si ha activado el seguimiento y la ventana del programa está minimizada en la bandeja del sistema, entonces al aparecer un nuevo mensaje en los elementos seleccionados, se maximizará la ventana principal de la aplicación con una notificación sonora. Para desactivar las notificaciones, haga clic en cualquier sitio de la lista con el botón izquierdo del ratón. Para detener el seguimiento, haga clic en el icono del programa en la bandeja del sistema Icono de LogMon. Para personalizar la notificación, coloque un archivo .wav con el nombre "alert.wav" en la misma carpeta con el archivo ejecutable del programa.  

Visualizar la categoría de registro

Para visualizar una categoría determinada, simplemente haga doble clic sobre la misma. Entonces verá el cuadro de mensajes:

Búsqueda de LogMon

En esta ventana puede buscar mensajes, ponga la ventana siempre en primer plano y active el desplazamiento automático. Se establece el color de cada mensaje de forma separada, mediante el método write() de la clase CLogger. Se puede resaltar el fondo del mensaje con un color seleccionado.

Al hacer un doble clic en el mensaje, se abrirá una ventana por separado. Esto sería muy útil si el mensaje es demasiado largo y no cabe en el cuadro de diálogo:  

Mensaje LogMon

Ahora dispone de una herramienta muy útil para visualizar y hacer el seguimiento de los archivos de registro. Espero que este programa le ayude, a medida que vaya desarrollando y utilizando los programas MQL5.

Conclusión

Registrar los eventos en su programa es muy útil, le ayuda a identificar errores ocultos y desvela posibilidades para que mejore su programa. En este artículo, he descrito los métodos y programas para los casos más sencillos de archivos de registro, seguimiento de registros y visualización.

¡Sus comentarios y sugerencias serán bienvenidos!

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/150

Archivos adjuntos |
clogger.mqh (8.72 KB)
testlogger.mq5 (1.29 KB)
logmon_source_en.zip (119.02 KB)
logmonen.zip (88.78 KB)

Otros artículos del autor

Simulink: una guía para desarrolladores de asesores expertos Simulink: una guía para desarrolladores de asesores expertos
No soy un programador profesional. Y por ello, el principio "ir de lo simple a lo complejo" es muy importante para mí cuando trabajo en el desarrollo de sistemas de trading. ¿Qué es exactamente simple para mí? En primer lugar, es la visualización del proceso de creación del sistema y la lógica de su funcionamiento. También es un mínimo de código escrito manualmente. En este artículo intentaré crear y probar el sistema de trading basado en un paquete de Matlab, y a continuación escribiré un Expert Advisor para MetaTrader 5. Los datos históricos de MetaTrader 5 se usarán en el proceso de prueba.
Cómo crear rápidamente un Expert Advisor para el Campeonato de Trading Automatizado 2010 Cómo crear rápidamente un Expert Advisor para el Campeonato de Trading Automatizado 2010
Con el fin de desarrollar un Expert Advisor para participar en el Automated Trading Championship 2010 (Campeonato de Trading Automatizado 2010), vamos a utilizar una plantilla de Expert Advisor preparada. Incluso los programadores principiantes en MQL5 serán capaces de realizar esta tarea, puesto que las clases básicas, funciones y plantillas ya están listas para sus estrategias. Es suficiente para escribir el mínimo de código para implementar su idea de trading.
Gestor de evento "Nueva barra" Gestor de evento "Nueva barra"
El lenguaje de programación MQL5 es capaz de resolver problemas a un nuevo nivel. Incluso aquellas tareas que ya tienen soluciones, gracias a la programación orientada a objetos pueden subir a un nivel superior. En este artículo veremos un sencillo ejemplo sobre la comprobación de una nueva barra en un gráfico, transformada en una herramienta más potente y versátil. ¿Qué herramienta? Lo veremos en este artículo.
Control de la pendiente de la curva de balance durante el funcionamiento de un Expert Advisor Control de la pendiente de la curva de balance durante el funcionamiento de un Expert Advisor
Encontrar reglas para un sistema de trading y programarlas en un Expert Advisor es la mitad del trabajo. De algún modo, hay que corregir el funcionamiento del Expert Advisor, ya que acumula los resultados del trading. En este artículo se describe una de las metodologías que permite mejorar el rendimiento de un Expert Advisor a través de una retroalimentación que mide la pendiente de la curva de balance.