English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Hataları Bulma ve Günlüğe Kaydetme

Hataları Bulma ve Günlüğe Kaydetme

MetaTrader 5Örnekler | 15 Aralık 2021, 10:59
86 0
Дмитрий Александрович
Дмитрий Александрович

Giriş

Merhaba, sevgili okuyucu!

Bu makalede, Expert Advisor'larda/Script Dosyalarında/Göstergelerde hata bulmanın birkaç yolunu ve günlük tutma yöntemlerini ele alacağız. Ayrıca size günlükleri görüntülemek için küçük bir program sunacağım - LogMon.

Hataları bulmak, programlamanın ayrılmaz bir parçasıdır. Yeni bir kod bloğu yazarken, doğru çalışıp çalışmadığını ve mantık hatası olmadığını kontrol etmek gerekir. Programınızda bir hatayı üç farklı şekilde bulabilirsiniz:

  1. Nihai sonucu değerlendirme  
  2. Adım adım hata ayıklama  
  3. Günlük içine mantıksal adımlar yazma

Her yolu göz önünde bulundurun.

1. Nihai Sonucu Değerlendirme

Bu yöntemle, programın çalışmasının sonucunu veya kodunun bir kısmını analiz ederiz. Örneğin, yalnızca netlik için yapılan bariz bir hatayı içeren basit bir kod alın:

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

  }

Derleyin ve çalıştırın; ekranda "0" görüntülenecektir. Sonuçları analiz ederek "9" sayısını bekliyoruz; dolayısıyla programımızın olması gerektiği gibi çalışmadığı sonucuna varıyoruz. Bu hata bulma yöntemi yaygındır ve hata konumunu bulamaz. Hata bulmanın ikinci yolunu göz önünde bulundurursak, hata ayıklamayı kullanacağız.  

2. Adım Adım Hata Ayıklama

Bu yöntem, program mantığının ihlal edildiği yeri tam olarak bulmanızı sağlar. MetaEditor'da 'for' döngüsünün içine bir kesme noktası koyun, hata ayıklamaya başlayın ve i değişkeni için watch ekleyin:

Hata Ayıklama

Ardından, program çalışması sürecinin tamamını göz önünde bulundurduğumuz sürece "Hata ayıklamaya devam et" seçeneğine tıklayın. "i" değişkeni "8" değerine sahip olduğu için döngüden çıktığımızı görüyoruz; dolayısıyla hatanın satırda olduğu sonucuna varıyoruz:

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

Yani, i değeri ve 9 sayısı karşılaştırıldığında. "i<9" " satırını "i<10" veya "i<=9" olarak sabitleyin, sonuçları kontrol edin. Tam olarak beklediğimiz gibi 9 sayısını elde ediyoruz. Hata ayıklamayı kullanarak programın çalışma zamanında nasıl hareket ettiğini öğrendik ve sorunu çözebildik. Bu yöntemin dezavantajları:

  1. Sezgisel olarak hatanın nerede meydana geldiği açık değildir.
  2. İzleme listesine değişkenler eklemeniz ve her adımdan sonra bunları görüntülemeniz gerekir.
  3. Bu yöntem, gerçek veya demo hesapta EA alım satımı gibi bitmiş programın yürütülmesi sırasında hataları tespit edemez.

Son olarak, hataları bulmanın üçüncü yolunu göz önünde bulundurun.

3. Günlüğe Mantıksal Adımlar Yazma

Bu yöntemi kullanarak programımızın önemli adımlarını kaydediyoruz. Örneğin: Başlatma, yatırım yapma, gösterge hesaplama vb. Script dosyamızı bir kod satırıyla yükseltin. Yani, her yinelemede i değişken değerini yazdıracağız:

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

  }

Çalıştırın ve günlük çıktısına bakın - "0 1 2 3 4 5 6 7 8 0" sayıları. Neden böyle olabileceğine ilişkin bir sonuca varın ve script dosyasını son seferki gibi düzeltin.

Bu hata bulma yönteminin avantajları ve dezavantajları şunlardır:

  1. + Programı adım adım çalıştırmaya gerek yoktur; bu nedenle çok zaman kazandırır.
  2. + Genellikle hatanın nerede olduğu açıktır.
  3. + Program çalışırken günlüğe kaydetmeye devam edebilirsiniz.
  4. + Daha sonra analiz ve karşılaştırma için günlüğü kaydedebilirsiniz (Örneğin, bir dosyaya yazarken. Aşağıya bakınız.).
  5. - Kaynak kodun boyutu, günlüğe veri yazan eklenen işleçler nedeniyle büyür.
  6. - Artan program çalışma süresi (özellikle optimizasyon için önemlidir).

Özet:

İlk hata bulma yolu, hatanın gerçekte nerede olduğunu izleyemez. Bunu, öncelikle hızı için kullanıyoruz. İkinci yol olan adım adım hata ayıklama, hatanın tam konumunu bulmanızı sağlar, ancak bu, çok zaman alır. Ve istediğiniz kod bloğunu atlarsanız her şeye yeniden başlamanız gerekir.

Son olarak, üçüncü yol olan mantıksal adımları günlüğe kaydetme, programın çalışmasını hızlı bir şekilde analiz etmenize ve sonucu kaydetmenize olanak tanır. Expert Advisor'larınızın/Göstergelerinizin/Script Dosyalarınızın olaylarını günlüğe yazarken, bir hatayı kolayca bulabilirsiniz, ayrıca bir hatanın oluştuğu doğru koşulları aramanız ve programınızda saatlerce hata ayıklamanız gerekmez. Ardından, günlüğe kaydetme yöntemlerini ayrıntılı olarak ele alacağız ve karşılaştıracağız. Ayrıca size en uygun ve hızlı yolu sunacağım.

Ne zaman günlüğe kaydetmeniz gerekir?

Günlüğe kaydetmenin bazı nedenleri şunlardır:
  1. Programın hatalı davranışı.
  2. Çok uzun program çalışma süresi (optimizasyon).
  3. Çalışma zamanını izleme (açma/kapama pozisyonları, yürütülen eylemler vb. ile ilgili bildirimleri yazdırma).  
  4. MQL5'i öğrenme; örneğin - dizileri yazdırma.  
  5. Şampiyona öncesi Expert Advisor'ları kontrol etme vb.

Günlüğe Kaydetme Yöntemleri

Günlüğe mesaj yazmanın birçok yolu vardır, ancak bazıları tamamen kullanılırken, diğerlerine özel durumlarda ihtiyaç duyulur. Örneğin, e-posta veya ICQ yoluyla günlük göndermek her zaman gerekli değildir.  

MQL5 programlamada kullanılan en yaygın yöntemlerin listesi şu şekildedir:

  1. Comment() işlevini kullanma
  2. Alert() işlevini kullanma
  3. Print() işlevini kullanma
  4. FileWrite() işlevini kullanarak günlüğü dosyaya yazın

Daha sonra kaynak kodlarıyla birlikte her bir yönteme örnekler vereceğim ve her bir yöntemin özelliklerini anlatacağım. Bu kaynak kodları oldukça soyuttur; bu nedenle özden çok uzaklaşmayacağız.

Comment() İşlevini Kullanma

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

Böylece, sol üst köşede "i" değişkeninin mevcut değerini görüyoruz:

Comment()

Dolayısıyla, çalışan programın mevcut durumunu izleyebiliyoruz. Şimdi avantajlarını ve dezavantajlarını ele alalım:

  1. + Değeri hemen görebilirsiniz.
  2. - Çıktı kısıtlaması.
  3. - Belirli bir mesajı seçemezsiniz.
  4. - Çalışma zamanı boyunca çalışmasını görmezsiniz, yalnızca mevcut durumu görürsünüz.
  5. - Nispeten yavaş yöntem.
  6. - Okumayı her zaman izlemeniz gerektiği için çalışmanın sürekli izlenmesi için uygun değildir.

Comment() işlevi, Expert Advisor'ın mevcut durumunu görüntülemek için kullanışlıdır. Örneğin, "2 yatırım aç" veya "GBRUSD lotu satın al: 0.7".  

Alert() İşlevini Kullanma

Bu işlev, mesajları sesli bildirimle ayrı bir pencerede görüntüler. Kod örneği:

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");
  }
Kod yürütmenin sonucu:  

Alert()

Şimdi içimiz içimize sığmıyor, sesle dahi her şey hemen aşikar oluyor. Ama şimdi avantajlarını ve dezavantajlarını ele alalım:

  1. + Tüm mesajlar tutarlı bir şekilde kaydedilir.
  2. + Sesli bildirim.
  3. + Her şey "Terminal_dir\MQL5\Logs\data.txt" dosyasına yazılır.
  4. - Tüm Script Dosyalarından/Expert Advisor'lardan/Göstergelerden gelen mesajların tamamı tek bir günlüğe yazılır.
  5. - Strateji Test Cihazı'nda çalışmaz.
  6. - Sık çağrıldığında terminali uzun süre dondurabilir (örneğin, her tick çağrılırken veya döngüde dizi yazdırırken).
  7. - Mesajları gruplandırmak imkansızdır.
  8. - Günlük dosyasını uygunsuz şekilde görüntüler.
  9. - Mesajlar, standart Veri Klasöründen farklı bir klasöre kaydedilemez.

Altıncı nokta, gerçek alım satım işleminde, özellikle Scalping veya Zararı Durdur'u değiştirirken çok kritiktir. Oldukça fazla dezavantajı var ve başkalarını da bulabilirsiniz, ancak bu kadarının yeterli olduğunu düşünüyorum.  

Print() İşlevini Kullanma

Bu işlev, günlük mesajlarını "Expert'ler" adlı özel pencereye yazar. Kod şu şekildedir:

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

Gördüğünüz gibi, bu işlev tıpkı Alert() işlevi gibi çağrılmaktadır, ancak artık tüm mesajlar "Experts" sekmesine ve "Terminal_dir\MQL5\Logs\data.txt" dosyasına bildirim yapılmadan yazılmaktadır. Bu yöntemin avantajlarını ve dezavantajlarını göz önünde bulundurun:  

  1. + Tüm mesajlar tutarlı bir şekilde kaydedilir.
  2. + Her şey "Terminal_dir\MQL5\Logs\data.txt" dosyasına yazılır.
  3. + Programın çalışma sürecinin sürekli olarak günlüğe kaydedilmesi için uygundur.
  4. - Tüm Script Dosyalarından/Expert Advisor'lardan/Göstergelerden gelen mesajların tamamı tek bir günlüğe yazılır.
  5. - Mesajları gruplandırmak imkansızdır.
  6. - Günlük dosyasını uygunsuz şekilde görüntüler.
  7. - Mesajlar, standart Veri Klasöründen farklı bir klasöre kaydedilemez.

Bu yöntem muhtemelen çoğu MQL5 programcısı tarafından kullanılmaktadır, oldukça hızlıdır ve çok sayıda günlük kaydı için son derece uygundur.

Günlüğü Dosyaya Yazma

Günlüğe kaydetmenin son yolunu düşünün; mesajları dosyalara yazmak. Bu yöntem öncekilerin hepsinden çok daha karmaşıktır, ancak uygun hazırlık ile iyi bir yazma hızı ve günlüklerin yanı sıra bildirimlerin rahat bir şekilde görüntülenmesini sağlar. Bir dosyaya günlük yazmanın en basit kodu şu şekildedir:

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
  }

Çalıştırın ve "Terminal_dir\MQL5\Files" klasörüne gidin ve "log.txt" dosyasını metin düzenleyicide açın. İçerikler şu şekildedir:

Günlüğü Dosyaya Kaydet

Gördüğünüz gibi, çıktı sonuçtur, dosyaya yazdıklarımız dışında ekstra mesaj yoktur. Avantajlarını ve dezavantajlarını ele alalım:

  1. + Hızlıdır.
  2. + Yalnızca yazmak istediklerimizi yazar.
  3. + Farklı programlardan mesajları farklı dosyalara yazabilirsiniz; böylece günlüklerin kesişimi hariç tutulur.
  4. - Günlükte yeni mesaj bildirimi yoktur.
  5. - Belirli bir mesajı veya mesaj kategorisini ayırt etmek imkansızdır.
  6. - Günlüğü açmak uzun zaman alır, dosyayı klasöre gidip açmalısınız.

Özet:

Yukarıda bahsedilen yöntemlerin tümünün dezavantajları vardır, ancak bazılarını değiştirebilirsiniz. İlk üç günlüğe kaydetme yöntemi esnek değildir, davranışlarını neredeyse etkileyemeyiz. Ancak ikinci yöntem, Dosyaya Günlük Yazma, en esnek olan yöntemdir, mesajların nasıl ve ne zaman günlüğe kaydedileceğine biz karar verebiliriz. Tek bir sayı görüntülemek istiyorsanız elbette ilk üç yöntemi kullanmak daha kolaydır. Ancak çok sayıda kod içeren karmaşık bir programınız varsa günlüğe kaydetme işlemi yapmadan kullanmak zor olacaktır.


Günlüğe Kaydetmeye Yeni Yaklaşım


Şimdi size bir dosyaya günlüğe kaydetmeyi nasıl iyileştirebileceğinizi anlatacak ve göstereceğim ve günlükleri görüntülemek için kullanışlı bir araç vereceğim. Bu, C++ ile yazdığım ve LogMon adını verdiğim Windows uygulamasıdır.

Tüm günlüğe kaydetme işlemini yapacak olan sınıfı yazmaya başlayalım; yani:

  1. Günlük ve diğer günlük ayarlarının yazılacağı dosyanın konumunu saklayın.
  2. Verilen ada ve tarihe/saate bağlı olarak günlük dosyaları oluşturun.
  3. İletilen parametreleri günlük satırına dönüştürün.
  4. Günlük mesajına zaman ekleyin.
  5. Mesaj rengi ekleyin.
  6. Mesaj kategorisi ekleyin.
  7. Mesajları önbelleğe alın ve bunları her n-saniyede bir veya her n-mesajda bir yazın.

MQL5 nesne yönelimli bir dil olduğu ve hızında C++'dan önemli ölçüde farklı olmadığı için özellikle MQL5 için bir sınıf yazacağız. O halde başlayalım.


Dosyaya Günlük Yazma Sınıfını Uygulama

Sınıfımızı mqh uzantılı ayrı bir içerme dosyasına koyacağız. Sınıfın genel yapısı şu şekildedir.

CLogger

Ayrıntılı yorumlar içeren sınıfın kaynak kodu:

//+------------------------------------------------------------------+
//|                                                      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'da içerme dosyasını (.mqh) oluşturun ve sınıfın kaynak kodunu kopyalayın ve "CLogger.mqh" adı altında kaydedin. Şimdi her bir yöntem ve sınıfın nasıl uygulanacağından daha fazla bahsedelim.

CLogger sınıfını kullanma

Bu sınıfı kullanarak mesajları günlüğe kaydetmeye başlamak için sınıf dosyasını Expert Advisor/Gösterge/Script Dosyası içine eklememiz gerekir:

#include <CLogger.mqh>

Ardından, bu sınıftan bir nesne oluşturmalısınız:

CLogger logger;

Tüm işlemleri "günlük kaydedici" nesnesi ile gerçekleştireceğiz. Şimdi "SetSetting()" yöntemini çağırarak ayarları yapmamız gerekiyor. Bu yönteme proje adını ve dosya adını iletmemiz gerekiyor. Ayrıca iki isteğe bağlı parametre vardır - Varsayılan kategorinin adı ve önbelleğin dosyaya yazılmadan önce depolandığı önbellek ömrü (saniye cinsinden). Sıfır belirtirseniz tüm mesajlar bir kez yazılacaktır.

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

Çağrı örneği:

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

Sonuç olarak, mesajlar "Client_Terminal_dir\MQL5\Files\log\log_MyProject\log_myLog_date.txt" dosyasına yazılacaktır; varsayılan kategori "Yorum" ve önbellek ömrü 60 saniyedir. Ardından günlük dosyasını açmak/oluşturmak için init() yöntemini çağırmanız gerekir. Parametreleri iletmeniz gerekmediği için çağrı örneği basittir:  

logger.init();

Bu yöntem, günlük dosyasının yolunu ve adını oluşturur, açar ve maksimum boyutu aşıp aşmadığını kontrol eder. Boyut önceden ayarlanmış sabit değeri aşarsa başka bir dosya açılır ve adına 1 birleştirilir. Ayrıca, doğru boyutta dosya açılana kadar boyut kontrol edilir.

Ardından işaretçi dosyanın sonundaki pozisyona taşınır. Artık nesne günlüğü yazmaya hazırdır. Yazma yöntemini geçersiz kıldık. Bu sayede farklı mesaj yapıları ayarlayabiliriz; örneğin yazma yöntemini çağırma örneği ve dosyadaki sonuç:

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

Günlük dosyası şu satırları içerecektir:

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

Gördüğünüz gibi, her şey çok basit. Herhangi bir yerde gerekli parametrelerle write() yöntemini çağırın; mesaj dosyaya yazılacaktır. Programınızın sonuna iki yöntemin çağrısını eklemeniz gerekir - flush() ve deinit().

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

Günlüğe bir döngüde sayılar yazan basit bir script dosyası örneği:

//+------------------------------------------------------------------+
//|                                                   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 dosyası üç saniyede yürütüldü ve 2 dosya oluşturuldu:

Günlük dosyaları

Dosya içerikleri:

GÜnlük dosyası içerikleri

Ve böylece tüm 100,000 mesaj. Gördüğünüz gibi, her şey oldukça hızlı çalışıyor. Bu sınıfı değiştirebilir, yeni özellikler ekleyebilir veya optimize edebilirsiniz.

Mesaj Çıktıları Düzeyi


Bir program yazarken, birkaç tür mesaj görüntülemeniz gerekir:

  1. Kritik hatalar (program düzgün çalışmıyor)
  2. Kritik olmayan hataların, alım satım işlemlerinin vb.'nin bildirimleri (programda geçici hatalar yaşanıyor veya program önemli bir işlem yaptı; bu, kullanıcıya bildirilmelidir)
  3. Hata ayıklama bilgileri (dizilerin ve değişkenlerin içeriği ve gerçek çalışmada ihtiyaç duyulmayan diğer bilgiler).

Ayrıca kaynak kodunu değiştirmeden hangi mesajları yazdırmak istediğinizi ayarlayabilmeniz önerilir. Bu hedefi basit bir işlev olarak uygulayacağız ve sınıfları ve yöntemleri kullanmayacağız.

Mesajlar çıktısının düzeyini saklayacak değişken parametresini bildirin. Değişkendeki sayı ne kadar büyük olursa, o kadar fazla mesaj kategorisi görüntülenecektir. Mesajlar çıktısını tamamen devre dışı bırakmak istiyorsanız "-1" değeri ile atayın.

input int dLvl=2;

CLogger sınıfının nesnesi oluşturulduktan sonra bildirilmesi gereken işlevin kaynak kodu aşağıdadır.

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

Şimdi bir örnek verelim: En önemli mesajlar için "0" düzeyini ve en kullanışsız olanlara kadar herhangi bir sayıyı (sıfırdan artan sırada) belirtin:

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 Kullanarak Kolay Günlük Görüntüleme

Tamam, şimdi binlerce satır içeren günlük dosyalarımız var. Ancak içlerinde bilgi bulmak oldukça zordur. Kategorilere ayrılmazlar ve birbirlerinden farklılık göstermezler. Bu sorunu çözmeye çalıştım; yani CLogger sınıfı tarafından oluşturulan günlükleri görüntülemek için bir program yazdım. Şimdi size kısaca WinAPI kullanılarak C++ ile yazılmış LogMon programını tanıtacağım. Bu nedenle hızlı ve küçük boyutludur. Program tamamen ücretsizdir.

Programla çalışmak için yapmanız gerekenler şu şekildedir:

  1. Normal mod olması durumunda "Client_Terminal_dir\MQL5\Files\" klasörüne kopyalayın ve çalıştırın.
  2. Test veya optimizasyon durumunda, "Agents_dir\Agent\MQL5\Files\" klasörüne kopyalayın ve çalıştırın.

Programın ana penceresi şöyle görünür:

LogMon ana penceresi

Ana pencere, araç çubuğunu ve ağaç görünümüne sahip pencereyi içerir. Bir öğeyi genişletmek için sol fare düğmesiyle çift tıklayın. Listedeki klasör "Client_Terminal_dir\MQL\Files\log\" klasöründe bulunan projelerdir. SetSetting() yöntemini kullanarak CLogger sınıfında projenin adını ayarlarsınız. Klasörler listesindeki dosyalar aslında günlük dosyalarıdır. Günlük dosyalarındaki mesajlar, write() yönteminde belirttiğiniz kategorilere ayrılır. Parantez içindeki sayılar o kategorideki mesajların sayılarıdır.

Şimdi araç çubuğundaki düğmeleri soldan sağa doğru inceleyelim.

Ağaç görünümünü sıfırlamanın yanı sıra proje veya günlük dosyasını silme düğmesi

Bu düğmeye bastığınızda şu pencere açılır:

Sil, Temizle

"Sil ve Boşalt" düğmesine basarsanız, tüm dosya/klasör tarama ileti dizileri durdurulacak, ağaç görünümü sıfırlanacak ve seçili dosyayı veya projeyi silmeniz istenecektir (bir öğeyi seçmek için tıklamanız yeterlidir, onay kutusunu seçmeniz gerekmez!). "Sıfırla" düğmesi, tüm dosya/klasör tarama ileti dizilerini durduracak ve ağaç görünümünü temizleyecektir.

"Hakkında" iletişim kutusunu görüntüleme düğmesi

Program ve yazarı hakkında kısa bilgi gösterir.

Program penceresini her zaman üstte gösterme düğmesi

Program penceresini diğer tüm pencerelerin üzerine yerleştirir.

Günlük dosyalarındaki yeni mesajların izlenmesini etkinleştirme düğmesi

Bu düğme, program penceresini Sistem Tepsisi tepsisine gizler ve günlük dosyalarındaki yeni mesajların izlenmesini etkinleştirir. Taranacak proje/dosya/kategoriyi seçmek için gerekli öğelerin yanındaki onay kutularını seçin.

Mesaj kategorisinin yanındaki onay kutusunu seçerseniz bildirim bu proje/dosya/kategorideki yeni bir mesajda tetiklenecektir. Dosyanın yanındaki onay kutusunu seçerseniz bildirim herhangi bir kategori için bu dosyadaki yeni bir mesajda tetiklenecektir. Son olarak, projenin yanındaki onay kutusunu seçerseniz bildirim yeni günlük dosyalarında ve içlerindeki mesajlarda tetiklenecektir.

İzleme

İzlemeyi etkinleştirdiyseniz ve program penceresi tepsiye küçültülürse seçilen öğelerde yeni mesaj göründüğünde, ana uygulama penceresi sesli bildirimle büyütülür. Bildirimleri devre dışı bırakmak için sol fare düğmesiyle listede herhangi bir yere tıklayın. İzlemeyi durdurmak için LogMon simgesi tepsisindeki program simgesine tıklayın. Bildirim sesini kendinize göre değiştirmek için "alert.wav" adlı .wav dosyasını programın yürütülebilir dosyasıyla aynı klasöre yerleştirin.  

Günlük Kategorisini Görüntüleme

Belirli bir kategoriyi görüntülemek için üzerine çift tıklamanız yeterlidir. Ardından mesaj kutusunu göreceksiniz:

LogMon arama

Bu pencerede mesajları arayabilir, pencereyi her zaman üstte sabitleyebilir ve otomatik kaydırmayı değiştirebilirsiniz. Her mesajın rengi, CLogger sınıfının write() yöntemi kullanılarak ayrı ayrı ayarlanır. Mesajın arka planı seçilen renkle vurgulanacaktır.

Bir mesaja çift tıkladığınızda, ayrı bir pencerede açılacaktır. Mesaj çok uzunsa ve iletişim kutusuna sığmıyorsa kullanışlı olacaktır:  

LogMon mesajı

Artık günlük dosyalarını görüntülemek ve izlemek için kullanışlı bir araca sahipsiniz. Bu programın, MQL5 programlarını geliştirirken ve kullanırken size yardımcı olacağını umuyorum.

Sonuç

Programınızdaki olayları günlüğe kaydetmek çok faydalıdır; gizli hataları belirlemenize ve programınızı iyileştirme fırsatlarını ortaya çıkarmanıza yardımcı olur. Bu makalemde en basit şekilde dosyaya günlüğe kaydetme, günlük izleme ve görüntüleme için yöntem ve programları anlattım.

Yorumlarınız ve önerileriniz değerlendirilecektir!

MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/150

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

Bu yazarın diğer makaleleri

Simulink: Expert Advisor'ların Geliştiricileri için Bir Kılavuz Simulink: Expert Advisor'ların Geliştiricileri için Bir Kılavuz
Profesyonel bir programcı değilim. Ve bu nedenle, alım satım sistemi geliştirme üzerinde çalışırken "basitten karmaşığa gitmek" prensibi benim için birincil öneme sahiptir. Benim için basit olan tam olarak nedir? Her şeyden önce, sistemi oluşturma sürecinin ve çalışmasının mantığının görselleştirilmesidir. Ayrıca, minimum elle yazılmış koddur. Bu makalemde, bir Matlab paketine dayalı alım satım sistemini oluşturup test etmeye çalışacak ve ardından MetaTrader 5 için bir Expert Advisor yazacağım. MetaTrader 5'in geçmiş verileri, test süreci için kullanılacaktır.
Otomatik Alım Satım Şampiyonası 2010 için Bir Expert Advisor Nasıl Hızlı Bir Şekilde Oluşturulur? Otomatik Alım Satım Şampiyonası 2010 için Bir Expert Advisor Nasıl Hızlı Bir Şekilde Oluşturulur?
2010 Otomatik Alım Satım Şampiyonası'na katılacak bir expert geliştirmek için hazır bir expert advisor şablonu kullanalım. Acemi bir MQL5 programcısı dahi bu görevi yerine getirebilir; zira stratejileriniz için temel sınıflar, işlevler, şablonlar zaten geliştirildi. Alım satım fikrinizi uygulamak için minimum miktarda kod yazmanız yeterlidir.
"Yeni Çubuk" Olay İşleyicisi "Yeni Çubuk" Olay İşleyicisi
MQL5 programlama dili, sorunları yepyeni bir düzeyde çözme kapasitesine sahiptir. Halihazırda bu tür çözümlere sahip olan görevler dahi, nesne yönelimli programlama sayesinde bir üst düzeye çıkabiliyor. Bu makalede, oldukça güçlü ve çok yönlü bir araca dönüştürülmüş bir grafikteki yeni çubuğu kontrol etmenin özellikle basit bir örneğini ele alıyoruz. Peki bu araç ne? Bu makalede bunu öğreneceğiz.
Expert Advisor'ın Çalışması Sırasında Denge Eğrisinin Eğimini Kontrol Etme Expert Advisor'ın Çalışması Sırasında Denge Eğrisinin Eğimini Kontrol Etme
Bir alım satım sistemi için kurallar bulmak ve bunları bir Expert Advisor'da programlamak işin yarısıdır. Bir şekilde, alım satım işleminin sonuçlarını biriktirdiği için Expert Advisor'ın çalışmasını düzeltmeniz gerekir. Bu makalede, denge eğrisinin eğimini ölçen bir geri bildirim oluşturmak yoluyla bir Expert Advisor'ın performansını artırmaya olanak tanıyan yaklaşımlardan biri açıklanmaktadır.