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