//+------------------------------------------------------------------+ //| Logs.mqh | //| Copyright 2015, Vasiliy Sokolov. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, Vasiliy Sokolov." #property link "http://www.mql5.com" #include #include #include "Message.mqh" //+------------------------------------------------------------------+ //| The class implements logging of messages as Singleton | //+------------------------------------------------------------------+ class CLog { private: static CLog* m_log; // A pointer to the global static sample CArrayObj m_messages; // The list of saved messages bool m_terminal_enable; // True if you need to print the received message to the trading terminal bool m_push_enable; // True if a Push notification is sent ENUM_MESSAGE_TYPE m_push_priority; // Contains the specified priority of message display in the terminal window. ENUM_MESSAGE_TYPE m_terminal_priority; // Contains the specified priority of sending pushes to mobile devices. bool m_recursive; // A flag indicating the recursive call of the destructor. bool SendPush(CMessage *msg); void CheckMessage(CMessage *msg); CLog(void); // Private constructor string GetName(void); void DeleteOldLogs(int day_history=30); void DeleteOldLog(string file_name,int day_history); ~CLog(void){;} public: static CLog *GetLog(void); // The method to receive a static object bool AddMessage(CMessage *msg); void Clear(void); bool Save(string path); CMessage *MessageAt(int index)const; int Total(void); void TerminalEnable(bool enable); bool TerminalEnable(void); void PushEnable(bool enable); bool PushEnable(void); void PushPriority(ENUM_MESSAGE_TYPE type); ENUM_MESSAGE_TYPE PushPriority(void); void TerminalPriority(ENUM_MESSAGE_TYPE type); ENUM_MESSAGE_TYPE TerminalPriority(void); bool SaveToFile(void); static bool DeleteLog(void); }; CLog *CLog::m_log; //+------------------------------------------------------------------+ //| Constructor of the global object | //+------------------------------------------------------------------+ CLog::CLog(void) : m_terminal_enable(true), m_push_enable(false), m_recursive(false) { DeleteOldLogs(); } //+------------------------------------------------------------------+ //| Returns the logger object | //+------------------------------------------------------------------+ static CLog *CLog::GetLog() { if(CheckPointer(m_log)==POINTER_INVALID) m_log=new CLog(); return m_log; } //+------------------------------------------------------------------+ //| Deletes the logger object | //+------------------------------------------------------------------+ bool CLog::DeleteLog(void) { bool res=CheckPointer(m_log)!=POINTER_INVALID; if(res) delete m_log; return res; } //+------------------------------------------------------------------+ //| Returns a message with the specified index 'index' | //+------------------------------------------------------------------+ CMessage *CLog::MessageAt(int index)const { CMessage *msg=m_messages.At(index); return msg; } //+------------------------------------------------------------------+ //| Returns the total number of logs. | //+------------------------------------------------------------------+ int CLog::Total(void) { return m_messages.Total(); } //+------------------------------------------------------------------+ //| Saves a message to a CSV file. | //+------------------------------------------------------------------+ bool CLog::Save(string path) { return false; } //+------------------------------------------------------------------+ //| Cleans the logger. | //+------------------------------------------------------------------+ void CLog::Clear(void) { m_messages.Clear(); } //+------------------------------------------------------------------+ //| Adds a new message to the list. | //+------------------------------------------------------------------+ bool CLog::AddMessage(CMessage *msg) { CheckMessage(msg); if(m_terminal_enable) printf(msg.ToConsoleType()); if(m_push_enable) SendPush(msg); return m_messages.Add(msg); } //+------------------------------------------------------------------+ //| Sets a flag indicating whether it's necessary to display | //| the passed message to platform window. | //+------------------------------------------------------------------+ void CLog::TerminalEnable(bool enable) { m_terminal_enable=enable; } //+------------------------------------------------------------------+ //| Returns the flag indicating whether the received message is | //| displayed in the trading platform window (Experts tab) | //+------------------------------------------------------------------+ bool CLog::TerminalEnable(void) { return m_terminal_enable; } //+------------------------------------------------------------------+ //| Sets a flag indicating whether it's necessary to send | //| the passed message to receivers' mobile devices. | //+------------------------------------------------------------------+ void CLog::PushEnable(bool enable) { m_push_enable=enable; } //+------------------------------------------------------------------+ //| Returns the flag indicating whether the received message is | //| sent as pushes to mobile devices. | //+------------------------------------------------------------------+ bool CLog::PushEnable(void) { return m_push_enable; } //+------------------------------------------------------------------+ //| Sends a passed message as a pushe to mobile devices to specified | //| recipients. Details: | //| https://www.mql5.com/en/docs/common/sendnotification | //| RETURN: | //| True if successfully sent, false if otherwise | //| | //+------------------------------------------------------------------+ bool CLog::SendPush(CMessage *msg) { string d="\t"; string stype= EnumToString(msg.Type()); string date = TimeToString(msg.TimeServer(), TIME_DATE|TIME_MINUTES|TIME_SECONDS); string line = stype + d + date + d + msg.Source() + d + msg.Text(); if(StringLen(line)>255) line=StringSubstr(line,0,255); bool res=SendNotification(line); return res; } //+------------------------------------------------------------------+ //| Sets the priority of sending push notifications to mobiles of | //| recipients For example, if priority = MESSAGE_ERROR, all messages| //| of type MESSAGE_INFO и MESSAGE_WARNING will not be sent. If | //| priority = MESSAGE_WARNING, it will send messages of types | //| MESSAGE_WARNING and MESSAGE_ERROR. | //+------------------------------------------------------------------+ void CLog::PushPriority(ENUM_MESSAGE_TYPE priority) { m_push_priority=priority; } //+------------------------------------------------------------------+ //| Returns priority of push-notification sending | //+------------------------------------------------------------------+ ENUM_MESSAGE_TYPE CLog::PushPriority(void) { return m_push_priority; } //+------------------------------------------------------------------+ //| Sets the priority of sending messages to the terminal | //| For example, if priority = MESSAGE_ERROR, all messages of type | //| MESSAGE_INFO and MESSAGE_WARNING aren't sent to terminal. If | //| priority = MESSAGE_WARNING, it will send messages | //| MESSAGE_WARNING and MESSAGE_ERROR. | //+------------------------------------------------------------------+ void CLog::TerminalPriority(ENUM_MESSAGE_TYPE priority) { m_terminal_priority=priority; } //+------------------------------------------------------------------+ //| Returns the priority of terminal message sending. | //+------------------------------------------------------------------+ ENUM_MESSAGE_TYPE CLog::TerminalPriority(void) { return m_terminal_priority; } //+------------------------------------------------------------------+ //| Checks if the sent message contains source and the text of the | //| message. If it doesn't, creates a new warning | //| message. | //+------------------------------------------------------------------+ void CLog::CheckMessage(CMessage *msg) { if(msg.Source()==UNKNOW_SOURCE || msg.Source()==NULL || msg.Source()=="") { string text="The passed message does not contain its source of origin."; CMessage *msg_info=new CMessage(MESSAGE_INFO,"CLog::AddMessage",text); AddMessage(msg_info); } if(msg.Text()==NULL || msg.Text()=="") { string text="The passed message does not contain message text."; CMessage *msg_info=new CMessage(MESSAGE_INFO,"CLog::AddMessage",text); AddMessage(msg_info); } } //+------------------------------------------------------------------+ //| Saves messages to a file, writing them to the end. | //+------------------------------------------------------------------+ bool CLog::SaveToFile(void) { int h=INVALID_HANDLE; // Make 3 attempts to open the file with a delay of 200 ms. for(int i=0; i<3; i++) { h=FileOpen(GetName(),FILE_TXT|FILE_READ|FILE_WRITE|FILE_COMMON); if(h!=INVALID_HANDLE)break; Sleep(200); } if(h==INVALID_HANDLE) { string text="Unable to open file "+GetName()+" Reason: "+(string)GetLastError(); CMessage *msg=new CMessage(MESSAGE_ERROR,__FUNCTION__,text); m_log.AddMessage(msg); return false; } FileSeek(h,0,SEEK_END); for(int i=0; i= day_history) FileDelete(file_name,FILE_COMMON); } //+------------------------------------------------------------------+