English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Encontrando erros e registrando

Encontrando erros e registrando

MetaTrader 5Exemplos | 5 fevereiro 2014, 12:45
2 343 0
Дмитрий Александрович
Дмитрий Александрович

Introdução

Olá, queridos leitores!

Neste artigo consideraremos várias maneiras de encontrar erros em Consultores/Scripts/Indicadores Especializado e métodos para registrá-los. Eu também oferecerei um pequeno programa para visualizar registros - LogMon.

Encontrar erros é parte integrante da programação. Enquanto você escreve um novo bloco de código, é necessário verificar se ele funciona corretamente e não tem erros lógicos. Você pode encontrar um erro em seu programa de três diferentes maneiras:

  1. Avaliando o resultado final
  2. Depuração passo a passo
  3. Escrevendo passos lógicos no registro

Considere cada maneira.

1. Avaliando o resultado final

Com este método, analisamos o resultado do trabalho do programa ou parte de seu código. Por exemplo, pegue um código simples, que contém um erro óbvio feito apenas para clareza:

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

  }

Compile e execute, e a tela exibirá "0". Por analisar os resultados nós esperamos o número "9", portanto, podemos concluir que nosso programa não está funcionando como deveria. Este método para encontrar erros é comum e não pode encontrar a localização do erro. Considerando a segunda maneira de encontrar erros, usaremos a depuração.

2. Depuração passo a passo

Este método permite a você encontrar exatamente o lugar, onde a lógica do programa foi violada. No MetaEditor coloque um ponto de interrupção dentro do loop 'for', inicie a depuração e observe pela variável i:

Debugging (Depuração)

Em seguida clique em "Continuar depuração" enquanto consideramos todo o processo de trabalho do programa. Vemos que enquanto a variável "i" tem o valor de "8", nós saímos do loop, portanto, concluímos que o erro está na linha:

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

Especificamente, quando o valor de i e o número 9 são comparados. Conserte a linha "i<9" "para "i<10" ou para "i<9", verifique os resultados. Obtemos o número 9 - exatamente o que esperávamos. Usando a depuração, aprendemos como o programa age em seu tempo de execução e fomos capazes de consertar o problema. Os contras deste método:

  1. Não é claro onde o erro ocorreu, por intuição.
  2. Você precisa adicionar variáveis para a lista de Observação e para visualiza-las após cada passo.
  3. Este método não pode detectar erros durante a execução de programas terminados, como um CE negociando em contas reais ou de demonstração.

Finalmente consideramos a terceira maneira de encontrar erros.

3. Escrevendo Passos Lógicos no Registro

Usando este método, nós registramos os passos significativos de nosso programa. Por exemplo, inicialização, fazer uma transação, cálculo de indicador, etc. Aprimore um script com uma linha de código. Especificamente, imprimiremos o valor da variável i em cada iteração:

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

  }

Execute e veja o registro de saída - números "0 1 2 3 4 5 6 7 8 0". Conclua por que pode ser desta forma e conserte o script, assim como da última vez.

Prós e contras deste método de encontrar erros:

  1. + Não precisa executar um programa passo a passo, assim poupando um bom tempo.
  2. + Frequentemente é óbvio onde está o erro.
  3. + Você pode manter registros enquanto o programa é executado.
  4. + Você pode salvar o registro para análise futura e comparação (Por exemplo, quando escrevendo em um arquivo. Veja abaixo).
  5. - O código-fonte cresce em tamanho devido aos operadores adicionados, que escrevem dados no registro.
  6. - Aumento do tempo de execução do programa (principalmente importante para otimização).

Resumo:

O primeiro caminho para encontrar erros não pode rastrear onde o erro está efetivamente localizado. O usamos primariamente por sua rapidez. A segunda maneira - depuração passo a passo, lhe permite encontrar a localização exata do erro, mas demora muito tempo. E se você passar do bloco de código desejado, terá de começar tudo de novo.

Finalmente, a terceira maneira - registrar passos lógicos no registro lhe permite rapidamente analisar o trabalho do programa e salvar o resultado. Enquanto escrever eventos de seus Consultores/Indicadores/Scripts Especializados no registro, você pode facilmente encontrar um erro e não precisa buscar pelas condições certas onde um erro ocorre, e não precisa depurar seu programa por horas e horas. Em seguida, consideraremos maneiras de registrar detalhes e compará-los. Também oferecerei a maneira que é mais conveniente e rápida.

Quando você precisa registrar?

Aqui estão algumas razões para registrar:
  1. Comportamento errôneo do programa.
  2. Tempo de execução do programa muito longo (otimização).
  3. Monitoramento do tempo de execução (imprimir notificações de abertura/fechamento de posições, ações executadas, etc.).
  4. Aprender MQL5, por exemplo - impressão de matrizes.
  5. Verificar Consultores Especialistas antes do campeonato, etc.

Métodos de Registro

Há muitas maneiras de escrever mensagens no registro, mas algumas são usadas por toda parte, enquanto outras são necessárias em casos especiais. Por exemplo, enviar registros por e-mail ou por ICQ não é sempre necessário.

Aqui está uma lista dos métodos mais comuns usados em programação MQL5:

  1. Usar a função Comment()
  2. Usar a função Alert()
  3. Usar a função Print()
  4. Escrever o registro no arquivo usando a função FileWrite()

Em seguida, darei exemplos de cada método com códigos-fonte e descreverei os recursos de cada método. Estes códigos-fonte são um tanto abstratos, então não iremos tão longe da essência.

Usando a função 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]);
  }

Então, no canto superior esquerdo vemos o valor atual para a variável "i":

Comment()

Assim, podemos monitorar o estado atual do programa em execução. Agora os prós e contras:

  1. + Você pode ver o valor imediatamente.
  2. - Restrição de saída.
  3. - Você não pode selecionar qualquer mensagem particular.
  4. - Você não precisa ver todo seu trabalho sobre o tempo de execução, apenas o estado atual.
  5. - Método relativamente lento.
  6. - Inadequado para contínuo monitoramento do trabalho, já que você sempre deve assistir a leitura.

A função Comment() é útil para exibir o estado atual do Consultor Especializado. Por exemplo, "Aberto a negócios" ou "comprar lote GBRUSD: 0.7".

Usando a função Alert()

Esta função exibe mensagens em uma janela separada com notificação sonora. O exemplo de 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");
  }
O resultado da execução de código:

Alert()

E agora estamos no sétimo céu, tudo é imediatamente óbvio até com som. Agora os prós e contras:

  1. + Todas mensagens são registradas consistentemente.
  2. + Notificação sonora.
  3. + Tudo é escrito no arquivo "Terminal_dir\MQL5\Logs\data.txt".
  4. - Todas as mensagens de Scripts/Consultores Especialistas/Indicadores são escritos em um registro.
  5. - Não funciona no Testador de Estratégia.
  6. - Quando chamado frequentemente pode congelar o terminal por um longo tempo (por exemplo, quando chamando em cada clique ou quando imprimindo a matriz em um loop).
  7. - Impossível de agrupar mensagens.
  8. - Visualização inconveniente do arquivo de registros.
  9. - Não pode salvar mensagens em pasta diferente da Pasta de Dados padrão.

O sexto ponto é crítico em negociações reais, especialmente quando cortando ou modificando o Parar Perdas. Há uma boa quantidade de contras e você pode encontrar outras, mas acho que são suficientes.

Usando a função Print()

Esta função escreve mensagens de registro em janela especial chamada "Especialistas". Aqui está o código:

void OnStart()
  {
//---
   Print("Старт скрипта");
   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 você pode ver, esta função é chamada assim como a função Alert(), mas agora todas as mensagens são escritas sem notificações na aba "Especialistas" e no arquivo "Terminal_dir\MQL5\Logs\data.txt". Considere os prós e contras deste método:

  1. + Todas mensagens são registradas consistentemente.
  2. + Tudo é escrito no arquivo "Terminal_dir\MQL5\Logs\data.txt".
  3. + Apropriado para contínuo registro do trabalho do programa.
  4. - Todas as mensagens de Scripts/Consultores Especialistas/Indicadores são escritos em um registro.
  5. - Impossível de agrupar mensagens.
  6. - Visualização inconveniente do arquivo de registros.
  7. - Não pode salvar mensagens em pasta diferente da Pasta de Dados padrão.

Este método é mais provavelmente usado pela maioria dos programadores em MQL5, é bem rápido e apropriado para um grande número de registros.

Escrevendo o registro em um arquivo

Considere esta a última forma de registro - escrever mensagens em arquivos. Este método é muito mais complicado que todos anteriores, mas com uma preparação apropriada garante uma boa velocidade de escrita e uma visualização confortável do registro, assim como as notificações. Aqui está o código mais simples para escrever o registro em um arquivo:

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
  }

Execute e navegue até a pasta "Terminal_dir\MQL5\Files" e abra o arquivo "log.txt" no editor de textos. Aqui estão os conteúdos:

Registro para Arquivo

Como você pode ver, a saída é consequente, sem mensagens extras, assim como escrevemos no arquivo. Considere prós e contras:

  1. + Rápido.
  2. + Escreve apenas o que queremos.
  3. + Você pode escrever mensagens de diferentes programas em diferentes arquivos, para que exclua a interseção de registros.
  4. - Sem notificações de novas mensagens no registro.
  5. - Impossível de distinguir uma mensagem específica ou categoria de mensagem.
  6. - Leva um longo tempo para abrir o registro, você deve navegar até a pasta para abrir o arquivo.

Resumo:

Todos os métodos mencionados acima tem suas desvantagens, mas você pode amenizar algumas delas. Os primeiros três métodos de registro não são flexíveis, nós quase não podemos influenciar seu comportamento. Mas este último método, Escrevendo o registro em arquivo, é o mais flexível, podemos decidir como e quando as mensagens são registradas. Se você quer exibir um único número, é claro que é mais fácil usar os três primeiros métodos. Mas se você tem um programa complicado com muitos códigos, será difícil usá-lo sem registrar.


Nova abordagem para registrar


Agora direi e mostrarei como você pode melhorar seu registro em um arquivo e dar uma ferramenta útil para visualizar registros. Este é o aplicativo para Windows, que eu escrevi em C++ e nomeei como LogMon.

Vamos começar escrevendo a classe, que fará todo o registro, nomeadamente:

  1. Manter a localização do arquivo, para o qual o registro e outras configurações de registro serão escritas.
  2. Criar arquivos de registro em nomes e datas/horários dados.
  3. Converter os parâmetros passados em linha de registro.
  4. Adicionar horário para a mensagem de registro.
  5. Adicionar cor de mensagem.
  6. Adicionada categoria de mensagem.
  7. Mensagens de cache e escrevê-las uma vez por cada n-segundos ou n-mensagens.

Já que MQL5 é uma linguagem orientada a objetos e não difere significativamente de C++ em sua velocidade, escreveremos uma classe especificamente para MQL5. Vamos iniciar.


Implementação da classe para escrever registros em arquivo

Colocaremos nossa classe em um arquivo incluir separado com a extensão mqh. Aqui está a estrutura geral da classe.

CLogger

Agora o código-fonte da classe com comentários detalhados:

//+------------------------------------------------------------------+
//|                                                      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
  }
//+------------------------------------------------------------------+

No MetaEditor crie o arquivo incluir (.mqh), e copie o código-fonte da classe e salve sob o nome CLogger.mqh". Agora vamos falar mais sobre cada método e como aplicar a classe.

Usando a classe CLogger

Para iniciar o registro de mensagens no registro usando esta classe, precisamos incluir o arquivo de classe em um Consultor Especialista/Indicador/Script:

#include <CLogger.mqh>

Em seguida, você tem que criar um objeto dessa classe:

CLogger logger;

Agora executaremos as ações com o objeto "logger". Agora ajustaremos as configurações chamando pelo método "SetSetting()". Neste método precisamos passar o nome do projeto e o nome do arquivo. Há também dois parâmetros opcionais - o nome da categoria padrão e o tempo de vida do cache (em segundos" durante o qual o cache é armazenado antes de ser escrito em arquivo. Se você especificar zero, todas as mensagens serão escritas uma 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
           );

Exemplo de chamada:

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

Como resultado, mensagens serão escritas no arquivo "Client_Terminal_dir\MQL5\Files\log\log_MyProject\log_myLog_date.txt", categoria padrão "Comment" e tempo de vida do cache de 60 segundos. Então você deve chamar o método init() para abrir/criar o arquivo de registro. O exemplo de chamada é simples, já que não é necessário passar parâmetros:

logger.init();

Este método gera o caminho e o nome do arquivo de registro, o abre e verifica se não excede o tamanho máximo. Se o tamanho excede o valor constante previamente definido, então outro arquivo é aberto, e 1 é concatenado a seu nome. Então, novamente, o tamanho é verificado até o arquivo com o tamanho correto ser aberto.

Então o ponteiro é movido para a posição no final do arquivo. Agora o objeto está pronto para escrever o registro. Nós sobrescrevemos o método de escrita. Graças a isto podemos definir diferentes estruturas de mensagens, exemplo de chamada de método de escrita e resultado no arquivo:

// 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__);

O arquivo de registro conterá as seguintes linhas:

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 você pode ver, tudo é muito simples. Chame o método write() com os parâmetros necessários em qualquer lugar e a mensagem será escrita no arquivo. No final do seu programa você precisa inserir a chamada de dois métodos - flush() e deinit().

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

Aqui está um exemplo simples de script que escreve números em um loop no 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
  }
//+------------------------------------------------------------------+

Script executado em três segundos e criando 2 arquivos:

Arquivos de registro

Conteúdos do arquivo:

Conteúdos do arquivo de registro

E então todas as 100000 mensagens. Como você pode ver, tudo funciona de forma rápida. Você pode modificar esta classe, adicionar novos recursos ou otimizá-la.

Nível de saída de mensagens


Enquanto você escreve um programa, você precisa exibir vários tipos de mensagens:

  1. Erros críticos (o programa não se comporta apropriadamente)
  2. Notificações de erros não críticos, operações de negociação, etc. (o programa está sofrendo erros temporários, ou o programa fez ação importante, que o usuário deve ser notificado).
  3. Informação de depuração (conteúdos de matrizes e variáveis, e outra informação que não seja necessária em trabalho real).

é também recomendável ser capaz de ajustar quais mensagens você quer imprimir sem mudar o código-fonte. Implementaremos este objetivo como uma função simples e não usaremos classes e métodos.

Declare o parâmetro variável que armazenará a saída de mensagens. Quanto maior o número em variável, mais categorias de mensagens serão exibidas. Se você quer desativar completamente a saída de mensagens, atribua com o valor "-1".

input int dLvl=2;

Abaixo está o código-fonte da função, que deve ser declarado após criar objeto da classe 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
   }
}

Agora um exemplo: especifique o nível "0" para as mensagens mais importantes, e qualquer número (em ordem crescente de zero) para as mais inúteis:

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

Visualização fácil usando o LogMon

OK, agora temos os arquivos de registro contendo milhares de linhas. Mas é um tanto difícil achar informações neles. Eles não estão divididos em categorias e não diferem uns dos outros. Tentei resolver este problema, nomeadamente, escrevi um programa para visualizar registros gerados pela classe CLogger. Agora brevemente introduzirei você ao programa LogMon, que é escrito em C++ usando WinAPI. Graças a isto é rápido e pequeno em tamanho. Este programa é absolutamente gratuito.

Para trabalhar com o programa você precisa de:

  1. Copiá-lo para a pasta "Client_Terminal_dir\MQL5\Files\" e executá-lo - no caso de modo normal.
  2. Copiá-lo para a pasta "Agents_dir\Agent\MQL5\Files\" e executá-lo - no caso de teste ou otimização.

A janela principal do programa se parece com isso:

Janela principal do LogMon

A janela principal contém a barra de ferramentas e a janela com a visualização em árvore. Para expandir um item, dê um clique duplo nele com o botão esquerdo do mouse. Pasta na lista - são os projetos, localizados na pasta "Client_Terminal_dir\MQL\Files\log\". Você define o nome do projeto na classe CLogger usando o método SetSetting(). Os arquivos na lista de pastas - são na verdade os arquivos de registro. Mensagens em arquivos de registro estão divididas em categorias que você especificou no método write(). Números em parênteses - são os números de mensagens naquela categoria.

Agora vamos considerar os botões na barra de ferramentas da esquerda para a direita.

Botão para deletar projeto ou arquivo de registro, assim como reinicializar a visualização de árvore

Quando você pressionar este botão, a seguinte janela aparece:

Apagar, descarregar

Se você pressionar o botão "Apagar e descarregar", todas as linhas de pesquisa de arquivos/pastas serão interrompidas, a visualização em árvore será reinicializada, e você será solicitado a apagar um arquivo selecionado ou projeto (simplesmente clicando um elemento para selecioná-lo - você não precisa selecionar a caixa de verificação!). O botão de "Reinicializar" parará todas as linhas de pesquisa de arquivos/pastas e limpar a visualização em árvore.

Botão para visualizar a caixa de diálogo "Sobre"

Mostra breve informação sobre o programa e seu autor.

Botão para mostrar a janela do programa sempre à frente

Coloca a janela do programa sobre todas as outras janelas.

Botão para ativar o monitoramento de novas mensagens em arquivos de registro

Este botão esconde a janela do programa para a bandeja de ícones Bandeja do sistema e ativa o monitoramento de novas mensagens em arquivos de registro. Para selecionar o projeto/arquivo/categoria a ser pesquisado, selecione marcar caixas próximo aos elementos necessários.

Se você marcar a caixa de seleção ao lado da categoria de mensagens, a notificação vai desencadear uma nova mensagem neste projeto/arquivo/categoria. Se você marcar a caixa de seleção ao lado do arquivo, a notificação vai desencadear uma nova mensagem neste arquivo para qualquer categoria. Finalmente, se você marcar a caixa de seleção ao lado do projeto, a notificação vai desencadear em um novo arquivo de log e mensagens neles.

Monitoramento

Se você ativou o monitoramento e a janela do programa é minimizada para a bandeja, então, quando uma nova mensagem aparecer em elementos selecionados, a janela principal do aplicativo será maximizada com notificação sonora. Para desativar notificações, clique em qualquer lugar na lista com o botão esquerdo do mouse. Para parar o monitoramento clique no ícone do programa na bandeja ícone do LogMon. Para mudar o som da notificação para uma personalizada, coloque um arquivo .wav com o nome "alert.wav" na mesma pasta que o arquivo executável do programa.

Visualizar categoria de registro

Para visualizar uma categoria específica simplesmente dê um duplo clique nela. Então você verá a caixa de diálogo:

Pesquisa LogMon

Nesta janela você pode pesquisar mensagens, fixar a janela para sempre à frente e alternar para auto rolagem. A cor de cada mensagem é definida individualmente usando o método write() da classe CLogger. O fundo da mensagem será destacado com a cor selecionada.

Quando você der um duplo-clique na mensagem, abrirá em uma janela separada. Será útil se a mensagem é muito longa e não cabe na caixa de diálogo.

Mensagem LogMon

Agora você tem uma ferramenta útil para visualizar e monitorar arquivos de registro. Esperançosamente, este programa irá ajudá-lo, enquanto você desenvolve e usa programas MQL5.

Conclusão

Registrar eventos em seu programa é muito útil, isto ajuda você a identificar erros escondidos e revela oportunidades de melhorar seu programa. Neste artigo descrevi métodos e programas para o mais simples registro em arquivo, monitoramento de registros e visualização.

Seus comentários e sugestões serão apreciados!

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/150

Arquivos anexados |
clogger.mqh (8.72 KB)
testlogger.mq5 (1.29 KB)
logmon_source_en.zip (119.02 KB)
logmonen.zip (88.78 KB)
Simulink: um guia para os desenvolvedores de Expert Advisors Simulink: um guia para os desenvolvedores de Expert Advisors
Não sou um programador profissional. E assim, o princípio de "ir do simples para o complexo" é de suma importância para mim quando trabalho com o desenvolvimento de um sistema de negócio. O que exatamente é simples para mim? Primeiramente, esta é a visualização do processo de criação do sistema e a lógica de seu funcionamento. Também, é um mínimo de código escrito à mão. Neste artigo, tentarei criar e testar o sistema de negócio, com base no pacote Matlab e, depois, escrever um Expert Advisor para o MetaTrader 5. Os dados do histórico do MetaTrader 5 serão usados para o processo de teste.
O uso de bibliotecas de classe de negócio padrão MQL5 ao escrever um Expert Advisor O uso de bibliotecas de classe de negócio padrão MQL5 ao escrever um Expert Advisor
Este artigo explica como usar as principais funcionalidades das Classes de negócio da biblioteca padrão do MQL5 ao escrever Expert Advisors que implementam o fechamento e modificação de posição, posicionamento de ordem pendente e exclusão e verificação de Margem antes de posicionar um negócio. Também demonstramos como as classes de negócio podem ser usadas para obter detalhes de ordens e transações.
Handler de evento "nova barra" Handler de evento "nova barra"
A linguagem de programação é capaz de resolver problemas em um nível completamente novo. Mesmo as tarefas que já tenham soluções, graças à programação orientada a objeto elas podem atingir um nível ainda maior. Neste artigo, consideramos um exemplo especialmente simples de verificação de uma nova barra em um gráfico, que foi transformado em uma ferramenta bastante poderosa e versátil. Qual ferramenta? Descubra neste artigo.
Usando a função TesterWithdrawal() para Modelar as Retiradas de Lucro Usando a função TesterWithdrawal() para Modelar as Retiradas de Lucro
Este artigo descreve a utilização da função TesterWithDrawal() para estimar riscos nos sistemas de negócio que implicam na remoção de uma determinada parte dos ativos durante sua operação. Além disso, ele descreve o efeito desta função no algoritmo de cálculo do rebaixamento de igualdade no Strategy tester. Esta função é útil quando otimizar parâmetros de seus Expert Advisors.