forked from Princeec13/mql5
311 lines
12 KiB
MQL5
311 lines
12 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| EALogger.mqh |
|
|
//| Copyright 2025, Your Company Name |
|
|
//| https://www.yoursite.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025, Your Company Name"
|
|
#property link "https://www.yoursite.com"
|
|
#property version "1.00"
|
|
#property strict
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Log levels |
|
|
//+------------------------------------------------------------------+
|
|
enum ENUM_LOG_LEVEL
|
|
{
|
|
LOG_LEVEL_ERROR = 0, // Error level
|
|
LOG_LEVEL_WARN, // Warning level
|
|
LOG_LEVEL_INFO, // Information level
|
|
LOG_LEVEL_DEBUG, // Debug level
|
|
LOG_LEVEL_TRACE // Trace level
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Logger class |
|
|
//+------------------------------------------------------------------+
|
|
class CEALogger
|
|
{
|
|
private:
|
|
string m_filename; // Log file name
|
|
int m_fileHandle; // File handle
|
|
ENUM_LOG_LEVEL m_logLevel; // Current log level
|
|
bool m_consoleOutput; // Enable console output
|
|
bool m_fileOutput; // Enable file output
|
|
|
|
// Log level names
|
|
static string m_levelNames[];
|
|
|
|
// Format log message
|
|
string FormatMessage(const string message, const ENUM_LOG_LEVEL level);
|
|
|
|
public:
|
|
CEALogger() : m_fileHandle(INVALID_HANDLE), m_logLevel(LOG_LEVEL_INFO),
|
|
m_consoleOutput(true), m_fileOutput(true) {}
|
|
~CEALogger() { Deinitialize(); }
|
|
|
|
// Initialization
|
|
bool Initialize(const string filename,
|
|
const ENUM_LOG_LEVEL level = LOG_LEVEL_INFO,
|
|
const bool consoleOutput = true,
|
|
const bool fileOutput = true);
|
|
void Deinitialize();
|
|
|
|
// Logging methods
|
|
void Log(const string message, const ENUM_LOG_LEVEL level = LOG_LEVEL_INFO);
|
|
void Error(const string message) { Log(message, LOG_LEVEL_ERROR); }
|
|
void Warning(const string message) { Log(message, LOG_LEVEL_WARN); }
|
|
void Info(const string message) { Log(message, LOG_LEVEL_INFO); }
|
|
void Debug(const string message) { Log(message, LOG_LEVEL_DEBUG); }
|
|
void Trace(const string message) { Log(message, LOG_LEVEL_TRACE); }
|
|
|
|
// Getters/Setters
|
|
ENUM_LOG_LEVEL GetLogLevel() const { return m_logLevel; }
|
|
void SetLogLevel(const ENUM_LOG_LEVEL level) { m_logLevel = level; }
|
|
|
|
// Helper methods
|
|
static string GetLastErrorText(const int errorCode = 0);
|
|
};
|
|
|
|
// Initialize static members
|
|
string CEALogger::m_levelNames[] = {"ERROR", "WARN ", "INFO ", "DEBUG", "TRACE"};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize logger |
|
|
//+------------------------------------------------------------------+
|
|
bool CEALger::Initialize(const string filename,
|
|
const ENUM_LOG_LEVEL level,
|
|
const bool consoleOutput,
|
|
const bool fileOutput)
|
|
{
|
|
// Close previous file if open
|
|
if (m_fileHandle != INVALID_HANDLE)
|
|
FileClose(m_fileHandle);
|
|
|
|
m_filename = filename;
|
|
m_logLevel = level;
|
|
m_consoleOutput = consoleOutput;
|
|
m_fileOutput = fileOutput;
|
|
|
|
// Open log file if file output is enabled
|
|
if (m_fileOutput)
|
|
{
|
|
m_fileHandle = FileOpen(m_filename, FILE_WRITE|FILE_TXT|FILE_ANSI|FILE_SHARE_READ|FILE_SHARE_WRITE);
|
|
if (m_fileHandle == INVALID_HANDLE)
|
|
{
|
|
// Try to create the directory if it doesn't exist
|
|
string path = "";
|
|
int pos = StringFind(m_filename, "\\");
|
|
if (pos > 0)
|
|
{
|
|
path = StringSubstr(m_filename, 0, pos);
|
|
if (!FolderCreate(path, 0))
|
|
{
|
|
Print("Failed to create log directory: ", path);
|
|
return false;
|
|
}
|
|
|
|
// Try opening the file again
|
|
m_fileHandle = FileOpen(m_filename, FILE_WRITE|FILE_TXT|FILE_ANSI|FILE_SHARE_READ|FILE_SHARE_WRITE);
|
|
if (m_fileHandle == INVALID_HANDLE)
|
|
{
|
|
Print("Failed to open log file: ", m_filename, ", error: ", GetLastError());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write log header
|
|
string header = StringFormat("=== Log started: %s ===\n", TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS));
|
|
FileWriteString(m_fileHandle, header);
|
|
FileFlush(m_fileHandle);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Deinitialize logger |
|
|
//+------------------------------------------------------------------+
|
|
void CEALogger::Deinitialize()
|
|
{
|
|
if (m_fileHandle != INVALID_HANDLE)
|
|
{
|
|
// Write log footer
|
|
string footer = StringFormat("\n=== Log ended: %s ===\n\n", TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS));
|
|
FileWriteString(m_fileHandle, footer);
|
|
|
|
// Close the file
|
|
FileClose(m_fileHandle);
|
|
m_fileHandle = INVALID_HANDLE;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Format log message |
|
|
//+------------------------------------------------------------------+
|
|
string CEALogger::FormatMessage(const string message, const ENUM_LOG_LEVEL level)
|
|
{
|
|
if (level < 0 || level >= ArraySize(m_levelNames))
|
|
return "";
|
|
|
|
datetime currentTime = TimeCurrent();
|
|
string timeStr = TimeToString(currentTime, TIME_DATE|TIME_SECONDS);
|
|
string levelStr = m_levelNames[level];
|
|
|
|
return StringFormat("%s [%s] %s", timeStr, levelStr, message);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Log a message |
|
|
//+------------------------------------------------------------------+
|
|
void CEALogger::Log(const string message, const ENUM_LOG_LEVEL level)
|
|
{
|
|
// Check log level
|
|
if (level > m_logLevel)
|
|
return;
|
|
|
|
// Format the message
|
|
string formattedMsg = FormatMessage(message, level);
|
|
if (formattedMsg == "")
|
|
return;
|
|
|
|
// Output to console
|
|
if (m_consoleOutput)
|
|
{
|
|
switch (level)
|
|
{
|
|
case LOG_LEVEL_ERROR:
|
|
Print("ERROR: ", message);
|
|
break;
|
|
case LOG_LEVEL_WARN:
|
|
Print("WARN: ", message);
|
|
break;
|
|
case LOG_LEVEL_INFO:
|
|
Print("INFO: ", message);
|
|
break;
|
|
case LOG_LEVEL_DEBUG:
|
|
Print("DEBUG: ", message);
|
|
break;
|
|
case LOG_LEVEL_TRACE:
|
|
Print("TRACE: ", message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Output to file
|
|
if (m_fileOutput && m_fileHandle != INVALID_HANDLE)
|
|
{
|
|
FileWriteString(m_fileHandle, formattedMsg + "\n");
|
|
FileFlush(m_fileHandle);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get last error text |
|
|
//+------------------------------------------------------------------+
|
|
string CEALogger::GetLastErrorText(const int errorCode = 0)
|
|
{
|
|
int code = (errorCode == 0) ? GetLastError() : errorCode;
|
|
|
|
switch (code)
|
|
{
|
|
// Common errors
|
|
case 0: return "No error";
|
|
case 1: return "No error returned, but the result is unknown";
|
|
case 2: return "Common error";
|
|
case 3: return "Invalid trade parameters";
|
|
case 4: return "Trade server is busy";
|
|
case 5: return "Old version of the client terminal";
|
|
case 6: return "No connection with trade server";
|
|
case 7: return "Not enough rights";
|
|
case 8: return "Too frequent requests";
|
|
case 9: return "Malfunctional trade operation";
|
|
case 64: return "Account disabled";
|
|
case 65: return "Invalid account";
|
|
case 128: return "Trade timeout";
|
|
case 129: return "Invalid price";
|
|
case 130: return "Invalid stops";
|
|
case 131: return "Invalid trade volume";
|
|
case 132: return "Market is closed";
|
|
case 133: return "Trade is disabled";
|
|
case 134: return "Not enough money";
|
|
case 135: return "Price changed";
|
|
case 136: return "Off quotes";
|
|
case 137: return "Broker is busy";
|
|
case 138: return "Requote";
|
|
case 139: return "Order is locked";
|
|
case 140: return "Long positions only allowed";
|
|
case 141: return "Too many requests";
|
|
case 145: return "Modification denied because order is too close to market";
|
|
case 146: return "Trade context is busy";
|
|
case 147: return "Expirations are denied by broker";
|
|
case 148: return "Too many open and pending orders";
|
|
case 149: return "Hedging is prohibited";
|
|
case 150: return "Prohibited by FIFO rules";
|
|
|
|
// Custom errors
|
|
case 4000: return "No error (the operation completed successfully)";
|
|
case 4001: return "Wrong function pointer";
|
|
case 4002: return "Array index is out of range";
|
|
case 4003: return "No memory for function call stack";
|
|
case 4004: return "Recursive stack overflow";
|
|
case 4005: return "Not enough stack for parameter";
|
|
case 4006: return "No memory for parameter string";
|
|
case 4007: return "No memory for temp string";
|
|
case 4008: return "Not initialized string";
|
|
case 4009: return "Invalid string parameter";
|
|
case 4010: return "Invalid string position";
|
|
case 4011: return "String parameter too long";
|
|
case 4012: return "Invalid array";
|
|
case 4013: return "Array resize error";
|
|
case 4014: return "String resize error";
|
|
case 4015: return "Not initialized array";
|
|
case 4016: return "Invalid array size";
|
|
case 4017: return "Abnormal program termination";
|
|
case 4018: return "Unhandled exception";
|
|
case 4019: return "Invalid pointer";
|
|
case 4020: return "Invalid variable type";
|
|
case 4021: return "Function is not allowed in testing mode";
|
|
case 4022: return "Function is not confirmed";
|
|
case 4050: return "Invalid function parameters count";
|
|
case 4051: return "Invalid function parameter value";
|
|
case 4052: return "String function internal error";
|
|
case 4053: return "Some array error";
|
|
case 4054: return "Incorrect series array using";
|
|
case 4055: return "Custom indicator error";
|
|
case 4056: return "Arrays are incompatible";
|
|
case 4057: return "Global variables processing error";
|
|
case 4058: return "Global variable not found";
|
|
case 4059: return "Function is not allowed in testing mode";
|
|
case 4060: return "Function is not confirmed";
|
|
case 4061: return "Send mail error";
|
|
case 4062: return "String parameter expected";
|
|
case 4063: return "Integer parameter expected";
|
|
case 4064: return "Double parameter expected";
|
|
case 4065: return "Array as parameter expected";
|
|
case 4066: return "Requested history not found";
|
|
case 4067: return "Some error in trade operation";
|
|
case 4099: return "End of file";
|
|
case 4100: return "Some file error";
|
|
case 4101: return "Wrong file name";
|
|
case 4102: return "Too many opened files";
|
|
case 4103: return "Cannot open file";
|
|
case 4104: return "Incompatible access to a file";
|
|
case 4105: return "No order selected";
|
|
case 4106: return "Unknown symbol";
|
|
case 4107: return "Invalid price";
|
|
case 4108: return "Invalid ticket";
|
|
case 4109: return "Trade is not allowed";
|
|
case 4110: return "Longs are not allowed";
|
|
case 4111: return "Shorts are not allowed";
|
|
case 4200: return "Object already exists";
|
|
case 4201: return "Unknown object property";
|
|
case 4202: return "Object does not exist";
|
|
case 4203: return "Unknown object type";
|
|
case 4204: return "No object name";
|
|
case 4205: return "Object coordinates error";
|
|
case 4206: return "No specified subwindow";
|
|
case 4207: return "Some error in object function";
|
|
default: return "Unknown error";
|
|
}
|
|
}
|