English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
오류 찾기 및 로깅

오류 찾기 및 로깅

MetaTrader 5 | 5 7월 2021, 16:49
183 0
Дмитрий Александрович
Дмитрий Александрович

소개

안녕하세요, 독자 여러분!

이 기사에서는 Expert Advisors/Scripts/Indicators에서 오류를 찾는 몇 가지 방법과 로깅 방법을 고려할 것입니다. 또한 로그를 볼 수 있는 작은 프로그램인 LogMon을 제공합니다.

오류 찾기는 프로그래밍의 일부이자 부분입니다. 새 코드 블록을 작성할 때 올바르게 작동하고 논리 오류가 없는지 확인해야 합니다. 다음 세 가지 방법으로 프로그램에서 오류를 찾을 수 있습니다.

  1. 최종 결과 평가  
  2. 단계별 디버깅  
  3. 로그에 논리적 단계 작성

각 방법을 고려하십시오.

1. 최종 결과 평가

이 방법으로 우리는 프로그램의 작업 결과 또는 코드의 일부를 분석합니다. 예를 들어, 명확성을 위해 만든 명백한 실수가 포함 된 간단한 코드를 살펴보겠습니다.

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

  }

컴파일하고 실행하면 화면에 "0"이 표시됩니다. 결과를 분석하면 숫자 "9"가 예상되므로 프로그램이 제대로 작동하지 않는다는 결론을 내립니다. 이 오류 찾기 방법은 일반적이며 오류 포지션을 찾을 수 없습니다. 오류를 찾는 두 번째 방법을 고려하여 디버깅을 사용합니다.  

2. 단계별 디버깅

이 방법을 사용하면 프로그램 논리가 위반된 포지션을 정확하게 찾을 수 있습니다. MetaEditor에서 'for'루프 안에 중단 점을 넣고 디버깅을 시작하고 i 변수에 대한 감시를 추가합니다.

디버깅

다음으로 프로그램 작업의 전체 프로세스를 고려하는 한 "디버깅 다시 시작"을 클릭합니다. "i"변수의 값이 "8"이면 루프를 종료하므로 오류가 줄에 있다고 결론을 내립니다.

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

즉, i와 9의 값을 비교할 때 줄 "i<9" " 를 "i<10" 또는 "i<= 9"로 수정하고 결과를 확인합니다. 우리는 정확히 우리가 기대했던 숫자 9를 얻습니다. 디버깅을 사용하여 프로그램이 런타임에 어떻게 작동하는지 배웠고 문제를 해결할 수 있었습니다. 이 방법의 단점:

  1. 직감적으로 오류가 발생한 위치는 명확하지 않습니다.
  2. 감시 목록에 변수를 추가하고 각 단계 후에 변수를 확인해야 합니다.
  3. 이 방법은 실제 또는 데모 계정에서 EA 거래와 같은 완료된 프로그램 실행 중 오류를 감지할 수 없습니다.

마지막으로 오류를 찾는 세 번째 방법을 고려하십시오.

3. 로그에 논리적 단계 작성

이 방법을 사용하여 프로그램의 중요한 단계를 기록합니다. 예: 초기화, 거래, 인디케이터 계산 등 한 줄의 코드로 스크립트를 업그레이드 하십시오. 즉, 각 반복마다 i 변수 값을 인쇄합니다.

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

  }

실행하고 로그 출력을 확인합니다 - 숫자 "0 1 2 3 4 5 6 7 8 0". 왜 그렇게 될 수 있는지 결론을 내리고 지난번처럼 스크립트를 수정하십시오.

이 오류 찾기 방법의 장단점:

  1. + 프로그램을 단계별로 실행할 필요가 없으므로 많은 시간이 절약됩니다.
  2. + 종종 오류가 있는 곳이 분명합니다.
  3. + 프로그램이 실행되는 동안 로깅을 계속할 수 있습니다.
  4. + 나중에 분석 및 비교하기 위해 로그를 저장할 수 있습니다 (예 : 파일에 쓸 때 아래를 참조하십시오.).
  5. - 로그에 데이터를 쓰는 추가 연산자로 인해 소스 코드의 크기가 커집니다.
  6. - 증가된 프로그램 런타임 (주로 최적화에 중요).

요약:

오류를 찾는 첫 번째 방법은 오류가 실제로 어디에 있는지 추적할 수 없습니다. 주로 속도 때문에 사용합니다. 두 번째 방법 - 단계별 디버깅을 통해 정확한 오류 포지션을 찾을 수 있지만 시간이 많이 걸립니다. 그리고 원하는 코드 블록을 지나치면 다시 시작해야 합니다.

마지막으로, 세 번째 방법 - 논리적 단계를 로그에 기록하면 프로그램 작업을 빠르게 분석하고 결과를 저장할 수 있습니다. Expert Advisors/Indicators/Scripts의 이벤트를 로그에 기록하는 동안 오류를 쉽게 찾을 수 있으며 오류가 발생하는 올바른 조건을 찾을 필요가 없습니다. 몇 시간 동안 프로그램을 디버깅해야 합니다. 다음으로 로그인 세부 사항을 고려하고 비교해보겠습니다. 또한 가장 편리하고 빠른 방법을 제공하겠습니다.

언제 기록해야 합니까?

로깅하는 몇 가지 이유는 다음과 같습니다.
  1. 프로그램의 잘못된 동작.
  2. 너무 긴 프로그램 런타임 (최적화).
  3. 런타임 모니터링 (개방/폐쇄 위치, 실행된 작업 등에 대한 인쇄 알림).  
  4. 예를 들어 MQL5 학습 - 배열 인쇄.  
  5. 대회 전 Expert Advisor 확인 등.

로깅 방법

로그에 메시지를 작성하는 방법은 여러 가지가 있지만 일부는 전체적으로 사용되고 다른 일부는 특별한 경우에 필요합니다. 예를 들어, 이메일이나 ICQ를 통해 로그를 보내는 것이 항상 필요한 것은 아닙니다.  

다음은 MQL5 프로그래밍에서 사용되는 가장 일반적인 방법 목록입니다.

  1. Comment() 함수 사용
  2. Alert() 함수 사용
  3. Print() 함수 사용
  4. FileWrite() 함수를 사용하여 파일에 로그 쓰기

다음으로 소스 코드와 함께 각 방법의 예를 제공하고 각 방법의 특징을 설명합니다. 이 소스 코드는 다소 추상적이므로 본질에서 멀지 않을 것입니다.

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]);
  }

따라서 왼쪽 상단에서 "i" 변수의 현재 값을 볼 수 있습니다.

Comment()

따라서 현재 실행중인 프로그램 상태를 모니터링 할 수 있습니다. 이제 장단점:

  1. + 즉시 값을 볼 수 있습니다.
  2. - 출력 제한.
  3. - 특정 메시지를 선택할 수 없습니다.
  4. - 런타임 전체에서 작업이 아니라 현재 상태만 볼 수 있습니다.
  5. - 비교적 느린 방법.
  6. - 항상 판독 값을 확인해야하므로 작업의 지속적인 모니터링에 적합하지 않습니다.

Comment() 함수는 Expert Advisor의 현재 상태를 표시하는 데 유용합니다. 예, "Open 2 deal" 또는 "buy GBRUSD lot : 0.7".  

Alert() 함수 사용

이 기능은 소리 알림과 함께 별도의 창에 메시지를 표시합니다. 코드의 예:

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");
  }
코드 실행 결과:  

Alert()

그리고 지금 우리는 일곱 번째 천국에 있습니다. 소리만으로도 모든 것이 즉시 분명합니다. 그러나 이제 장단점:

  1. + 모든 메시지가 일관되게 기록됩니다.
  2. + 소리 알림.
  3. + 모든 것이 "Terminal_dir\MQL5\Logs\data.txt"파일에 기록됩니다.
  4. - 모든 Scripts/Expert Advisors/Indicator의 모든 메시지는 하나의 로그에 기록됩니다.
  5. - 전략 테스터에서 작동하지 않습니다.
  6. - 자주 호출하면 터미널이 오랫동안 멈출 수 있습니다 (예 : 모든 틱을 호출하거나 루프에서 배열을 인쇄 할 때).
  7. - 메시지를 그룹화 할 수 없습니다.
  8. - 로그 파일보기가 불편합니다.
  9. - 표준 데이터 폴더와 다른 폴더에 메시지를 저장할 수 없습니다.

여섯 번째 포인트는 특히 손절매를 조정하거나 수정할 때 실제 거래에서 매우 중요합니다. 많은 단점이 있고 다른 것을 찾을 수 있지만 그 정도면 충분하다고 생각합니다.  

Print() 함수 사용

이 함수는 "Experts"라는 특수 창에 로그 메시지를 기록합니다. 다음은 코드입니다.

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()

보시다시피이 함수는 Alert() 함수처럼 호출되지만 이제 모든 메시지는 알림없이 "Experts"탭과 "Terminal_dir\MQL5\Logs\data.txt"파일에 기록됩니다. 이 방법의 장단점을 고려하십시오.  

  1. + 모든 메시지가 일관되게 기록됩니다.
  2. + 모든 것이 "Terminal_dir\MQL5\Logs\data.txt"파일에 기록됩니다.
  3. + 프로그램 작업의 연속 로깅에 적합합니다.
  4. - 모든 Scripts/Expert Advisors/Indicator의 모든 메시지는 하나의 로그에 기록됩니다.
  5. - 메시지를 그룹화 할 수 없습니다.
  6. - 로그 파일보기가 불편합니다.
  7. - 표준 데이터 폴더와 다른 폴더에 메시지를 저장할 수 없습니다.

이 방법은 대부분의 MQL5 프로그래머가 사용할 가능성이 높으며 매우 빠르고 많은 수의 로그 레코드에 적합합니다.

파일에 로그 쓰기

마지막 로깅 방법 (파일에 메시지 쓰기)을 고려하십시오. 이 방법은 이전의 모든 방법보다 훨씬 복잡하지만 적절한 준비를 통해 쓰기 속도가 좋고 로그 및 알림을 편안하게 볼 수 있습니다. 다음은 로그를 파일에 쓰는 가장 간단한 코드입니다.

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
  }

"Terminal_dir\MQL5\Files"폴더를 실행하고 찾아보고 텍스트 편집기에서 "log.txt"파일을 엽니다. 내용은 다음과 같습니다.

파일에 기록

보시다시피 결과적으로 출력은 추가 메시지가 아니라 파일에 기록한 것입니다. 장단점 고려:

  1. + 빠른.
  2. + 원하는 것만 씁니다.
  3. + 서로 다른 프로그램의 메시지를 서로 다른 파일에 기록하여 로그 교차를 제외할 수 있습니다.
  4. - 로그에 새 메시지 알림이 없습니다.
  5. - 특정 메시지 또는 메시지 범주를 구분할 수 없습니다.
  6. - 로그를 여는 데 시간이 오래 걸리므로 폴더를 찾아 파일을 열어야 합니다.

요약:

위에서 언급한 모든 방법에는 단점이 있지만 일부를 수정할 수 있습니다. 처음 세 가지 로깅 방법은 유연하지 않으며 행동에 거의 영향을 줄 수 없습니다. 그러나 후자의 방법인 파일에 로그 작성이 가장 유연하므로 메시지를 기록하는 방법과 시기를 결정할 수 있습니다. 단일 숫자를 표시하려면 물론 처음 세 가지 방법을 사용하는 것이 더 쉽습니다. 그러나 코드가 많은 복잡한 프로그램이 있다면 로깅없이 사용하기가 어려울 것입니다.


로깅에 대한 새로운 접근 방식


이제 파일에 대한 로그인을 개선하고 로그를 볼 수있는 편리한 도구를 제공하는 방법을 설명하고 보여 드리겠습니다. 이것은 내가 C++로 작성하고 LogMon이라고 부르는 Windows 용 애플리케이션입니다.

모든 로깅을 수행하는 클래스 작성부터 시작하겠습니다.

  1. 로그 및 기타 로그 설정이 기록될 파일의 ​​위치를 ​​유지하십시오.
  2. 주어진 이름과 날짜/시간에 따라 로그 파일을 만듭니다.
  3. 전달된 매개 변수를 로그 행으로 변환하십시오.
  4. 로그 메시지에 시간을 추가하십시오.
  5. 메시지 색상을 추가합니다.
  6. 메시지 카테고리를 추가합니다.
  7. 메시지를 캐시하고 n- 초당 한 번 또는 n- 메시지마다 기록합니다.

MQL5는 객체 지향 언어이고 속도면에서 C++와 크게 다르지 않으므로 MQL5 전용 클래스를 작성합니다. 시작하자.


파일에 로그 쓰기 클래스 구현

mqh 확장자를 가진 별도의 포함 파일에 클래스를 넣습니다. 다음은 클래스의 일반적인 구조입니다.

CLogger

이제 자세한 설명이 있는 클래스의 소스 코드:

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

MetaEditor에서 include 파일 (.mqh)을 생성하고 클래스의 소스 코드를 복사하여 "CLogger.mqh"이름으로 저장합니다. 이제 각 방법과 클래스 적용 방법에 대해 자세히 알아 보겠습니다.

CLogger 클래스 사용

이 클래스를 사용하여 로그에 메시지 기록을 시작하려면 클래스 파일을 Expert Advisor/Indicator/ Script에 포함해야 합니다.

#include <CLogger.mqh>

다음으로 이 클래스의 객체를 만들어야 합니다.

CLogger logger;

"로거" 개체를 사용하여 모든 작업을 수행합니다. 이제 "SetSetting()" 메소드를 호출하여 설정을 조정해야 합니다. 이 메소드에 프로젝트 이름과 파일 이름을 전달해야 합니다. 또한 두 개의 선택적 매개 변수가 있습니다. 기본 범주 이름과 캐시가 파일에 기록되기 전에 저장되는 캐시 수명(초)입니다. 0을 지정하면 모든 메시지가 한 번만 기록됩니다.

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

통화 예:

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

결과적으로 메시지는 "Client_Terminal_dir\MQL5\Files\log\log_MyProject\log_myLog_date.txt"파일에 기록되며 기본 범주는 "Comment"이며 캐시 수명은 60 초입니다. 그런 다음 init() 메소드를 호출하여 로그 파일을 열거나 만들어야 합니다. 매개 변수를 전달할 필요가 없기 때문에 호출의 예는 간단합니다.  

logger.init();

이 메소드는 로그 파일의 경로와 이름을 생성하고 파일을 열고 최대 크기를 초과하지 않는지 확인합니다. 크기가 이전에 설정된 상수 값을 초과하면 다른 파일이 열리고 이름에 1이 연결됩니다. 그런 다음 올바른 크기의 파일이 열릴 때까지 크기를 확인합니다.

그런 다음 포인터가 파일 끝의 위치로 이동합니다. 이제 개체가 로그를 작성할 준비가 되었습니다. write 메소드를 재정의했습니다. 이 덕분에 우리는 다른 메시지 구조를 설정할 수 있습니다. 쓰기 메소드를 호출하는 예와 파일의 결과 :

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

로그 파일에는 다음 행이 포함됩니다.

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

보시다시피 모든 것이 매우 간단합니다. 필요한 매개 변수와 함께 write() 메소드를 어디에서나 호출하면 메시지가 파일에 기록됩니다. 프로그램 끝에 flush() 와 deinit()의 두 메소드 호출을 삽입해야 합니다.

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

다음은 루프의 숫자를 로그에 쓰는 스크립트의 간단한 예입니다.

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

스크립트가 3 초 만에 실행되고 2 개의 파일이 생성되었습니다.

로그 파일

파일 내용:

로그 파일 내용

그래서 모든 100000 개의 메시지. 보시다시피 모든 것이 매우 빠르게 작동합니다. 이 클래스를 수정하거나 새 기능을 추가하거나 최적화 할 수 있습니다.

메시지 출력 수준


프로그램을 작성할 때 여러 유형의 메시지를 표시해야 합니다.

  1. 심각한 오류 (프로그램이 제대로 작동하지 않음)
  2. 중요하지 않은 오류, 거래 작업 등에 대한 알림 (프로그램에 일시적 오류가 발생했거나 프로그램이 중요한 조치를 취했으므로 사용자에게 알려야 함)
  3. 디버깅 정보 (배열 및 변수의 내용 및 실제 작업에 필요하지 않은 기타 정보).

또한 소스 코드를 변경하지 않고 인쇄할 메시지를 조정할 수 있는 것이 좋습니다. 우리는 이 목표를 간단한 함수로 구현하고 클래스와 메소드를 사용하지 않을 것입니다.

메시지 출력 레벨을 저장할 변수 매개 변수를 선언하십시오. 변수의 수가 클수록 더 많은 범주의 메시지가 표시됩니다. 메시지 출력을 완전히 비활성화하려면 "-1" 값을 지정하십시오.

input int dLvl=2;

다음은 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
   }
}

이제 예 : 가장 중요한 메시지에는 수준 "0"을 지정하고 가장 쓸모없는 메시지에는 임의의 숫자 (0부터 오름차순)를 지정합니다.

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

LogMon을 이용한 간편한 로그 보기

이제 수천 줄이 포함된 로그 파일이 있습니다. 그러나 정보를 찾는 것은 다소 어렵습니다. 그들은 카테고리로 나뉘지 않고 서로 다르지 않습니다. 이 문제를 해결하려고 노력했습니다. 즉, CLogger 클래스에서 생성 된 로그를 보는 프로그램을 작성했습니다. 이제 WinAPI를 사용하여 C++로 작성된 LogMon 프로그램에 대해 간략하게 소개하겠습니다. 이로 인해 크기가 빠르고 작습니다. 이 프로그램은 완전 무료입니다.

프로그램으로 작업하려면 다음이 필요합니다.

  1. "Client_Terminal_dir\MQL5\Files\" 폴더에 복사하여 실행하십시오 - 일반 모드의 경우.
  2. 테스트 또는 최적화의 경우 "Agents_dir\Agent\MQL5\Files\"폴더에 복사하고 실행합니다.

프로그램 기본 창은 다음과 같습니다.

LogMon 기본 창

메인 창에는 툴바와 트리뷰가 있는 창이 있습니다. 항목을 확장하려면 마우스 왼쪽 버튼으로 더블 클릭합니다. 목록의 폴더 - "Client_Terminal_dir\MQL\Files\log\"폴더에 있는 프로젝트입니다. SetSetting() 메소드를 사용하여 CLogger 클래스에서 프로젝트 이름을 설정합니다. 폴더 목록의 파일은 실제로 로그 파일입니다. 로그 파일의 메시지는 write() 메소드에서 지정한 범주로 나뉩니다. 괄호 안의 숫자 - 해당 카테고리의 메시지 수입니다.

이제 도구 모음의 버튼을 왼쪽에서 오른쪽으로 살펴 보겠습니다.

프로젝트 또는 로그 파일을 삭제하고 트리보기를 재설정하는 버튼

이 버튼을 누르면 다음 창이 나타납니다.

Delete, Flush

"삭제 및 비우기"버튼을 누르면 스캔 파일/폴더의 모든 스레드가 중지되고 트리보기가 재설정되며 선택한 파일 또는 프로젝트를 삭제하라는 메시지가 표시됩니다 (단순히 요소를 클릭하여 선택합니다. 체크 박스를 선택하지 않아도됩니다!) "재설정" 버튼은 파일/폴더 스캔의 모든 스레드를 중지하고 트리보기를 지웁니다.

"정보" 대화 상자를 보기 위한 버튼

프로그램 및 작성자에 대한 간략한 정보를 표시합니다.

프로그램 창을 항상 맨 위에 표시하는 버튼

프로그램 창을 다른 모든 창 위에 놓습니다.

로그 파일의 새 메시지 모니터링을 활성화하는 버튼

이 버튼은 프로그램 창을 시스템 트레이 트레이에 숨기고 로그 파일의 새 메시지 모니터링을 활성화합니다. 스캔 할 프로젝트/파일/카테고리를 선택하려면 필요한 요소 옆에 있는 확인란을 선택합니다.

메시지 범주 옆에 있는 확인란을 선택하면이 프로젝트/파일/범주에 있는 새 메시지에 대해 알림이 트리거됩니다. 파일 옆에있는 확인란을 선택하면 이 파일의 모든 범주에 대한 새 메시지에 대해 알림이 트리거됩니다. 마지막으로 프로젝트 옆에 있는 확인란을 선택하면 새 로그 파일과 그 안의 메시지에 대해 알림이 트리거됩니다.

모니터링

모니터링을 활성화하고 프로그램 창을 트레이로 최소화한 경우 선택한 요소에 새 메시지가 표시되면 알림음과 함께 메인 애플리케이션 창이 최대화됩니다. 알림을 비활성화 하려면 마우스 왼쪽 버튼으로 목록의 아무 곳이나 클릭합니다. 모니터링을 중지하려면 트레이 LogMon 아이콘에서 프로그램 아이콘을 클릭하세요. 알림 음을 자신의 것으로 변경하려면 프로그램 실행 파일과 같은 폴더에 "alert.wav"라는 이름의 .wav 파일을 저장하십시오.  

로그 범주보기

특정 카테고리를 보려면 단순히 더블 클릭하십시오. 그러면 메시지 상자가 나타납니다.

LogMon 검색

이 창에서 메시지를 검색하고 항상 창을 상단에 고정하고 자동 스크롤을 전환 할 수 있습니다. 각 메시지의 색상은 CLogger 클래스의 write() 메소드를 사용하여 개별적으로 설정됩니다. 메시지의 배경이 선택한 색상으로 강조 표시됩니다.

메시지를 두 번 클릭하면 별도의 창에서 열립니다. 메시지가 너무 길고 대화 상자에 맞지 않는 경우 유용합니다.  

LogMon 메시지

이제 로그 파일을 보고 모니터링 할 수 있는 편리한 도구가 있습니다. 이 프로그램이 MQL5 프로그램을 개발하고 사용할 때 도움이 되기를 바랍니다.

결론

프로그램에 이벤트를 기록하는 것은 매우 유용하며 숨겨진 오류를 식별하고 프로그램을 개선할 기회를 찾는데 도움이 됩니다. 이 기사에서는 가장 간단한 파일 로그인, 로그 모니터링 및 보기를 위한 방법과 프로그램을 설명해드렸습니다.

의견과 제안을 주시면 감사하겠습니다!

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/150

파일 첨부됨 |
clogger.mqh (8.72 KB)
testlogger.mq5 (1.29 KB)
logmon_source_en.zip (119.02 KB)
logmonen.zip (88.78 KB)
Simulink: Expert Advisor 개발자를 위한 가이드 Simulink: Expert Advisor 개발자를 위한 가이드
저는 전문 프로그래머가 아닙니다. 따라서 거래 시스템 개발 작업을 할 때 "단순한 것에서 복잡한 것으로가는 것"의 원칙이 가장 중요합니다. 나에게 정확히 무엇이 간단합니까? 우선 그것은 시스템을 만드는 과정과 그 작업의 논리를 시각화하는 것입니다. 또한 최소한의 수기 코드입니다. 이 기사에서는 Matlab 패키지를 기반으로 거래 시스템을 만들고 테스트한 다음 MetaTrader 5에 대한 Expert Advisor를 작성하려고 합니다. MetaTrader 5의 과거 데이터는 테스트 프로세스에 사용됩니다.
자동 거래 챔피언십 2010을 위한 Expert Advisor를 신속하게 만드는 방법을 알아보십시오. 자동 거래 챔피언십 2010을 위한 Expert Advisor를 신속하게 만드는 방법을 알아보십시오.
Automated Trading Championship 2010에 참여할 전문가를 양성하기 위해 준비된 Expert Advisor의 템플릿을 사용해 보겠습니다. 기본 클래스, 기능, 템플릿이 이미 개발되었기 때문에 초보 MQL5 프로그래머도 이 작업을 수행할 수 있습니다. 거래 아이디어를 구현하기 위한 최소한의 코드만으로도 충분합니다.
"New Bar" 이벤트 핸들러 "New Bar" 이벤트 핸들러
MQL5 프로그래밍 언어는 새로운 수준에서 문제를 해결할 수 있습니다. 객체 지향 프로그래밍 덕분에 이미 그러한 솔루션이있는 작업조차도 더 높은 수준으로 올라갈 수 있습니다. 이 기사에서는 차트에서 새로운 바를 확인하는 매우 간단한 예를 살펴 보겠습니다. 이는 다소 강력하고 다양한 도구로 변환되었습니다. 어떤 도구? 이 기사에서 알아보십시오.
Expert Advisor 작업 중 균형 곡선의 기울기 조절 Expert Advisor 작업 중 균형 곡선의 기울기 조절
무역 시스템에 대한 규칙을 찾고 Expert Advisor에서 프로그래밍하는 것은 작업의 절반입니다. 어쨋든 거래 결과가 누적되므로 Expert Advisor의 운영을 수정해야 합니다. 이 기사에서는 균형 곡선의 기울기를 측정하는 피드백을 생성하여 Expert Advisor의 성능을 향상시킬 수있는 접근 방식 중 하나를 설명합니다.