mql5/Include/Experts/Core/EALogger.mqh
2025-08-16 12:30:04 -04:00

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