//+------------------------------------------------------------------+ //| SimpleLogger.hpp | //| Copyright 2026, Niquel Mendoza. | //| https://www.mql5.com/es/users/nique_372 | //+------------------------------------------------------------------+ #pragma once //+------------------------------------------------------------------+ //| Includes | //+------------------------------------------------------------------+ #include #include #include #include //+------------------------------------------------------------------+ //| Defines | //+------------------------------------------------------------------+ namespace TSN { #ifdef _MSC_VER #define FUNC_NAME __FUNCTION__ #define FORCE_INLINE __forceinline #else #define FUNC_NAME __func__ #define FORCE_INLINE inline __attribute__((always_inline)) #endif //--- Log Flags const constexpr unsigned char LOG_LEVEL_ERROR = 1; // 0001 - Critical errors const constexpr unsigned char LOG_LEVEL_WARNING = 2; // 0010 - Warnings const constexpr unsigned char LOG_LEVEL_INFO = 4; // 0100 - General information const constexpr unsigned char LOG_LEVEL_CAUTION = 8; // 1000 - Cautions const constexpr unsigned char LOG_ALL = LOG_LEVEL_CAUTION | LOG_LEVEL_INFO | LOG_LEVEL_WARNING; //--- enum class ENUM_VERBOSE_LOG_LEVEL { VERBOSE_LOG_LEVEL_ERROR_ONLY = LOG_LEVEL_ERROR, // Only Errors VERBOSE_LOG_LEVEL_WARNINGS = LOG_LEVEL_WARNING, // Warnings + Errors VERBOSE_LOG_LEVEL_INFO = LOG_LEVEL_INFO, // Info + Errors VERBOSE_LOG_LEVEL_CAUTION = LOG_LEVEL_CAUTION, // Caution + Errors VERBOSE_LOG_LEVEL_WARNINGS_INFO = LOG_LEVEL_WARNING | LOG_LEVEL_INFO, // Warnings + Info + Errors VERBOSE_LOG_LEVEL_WARNINGS_CAUTION = LOG_LEVEL_WARNING | LOG_LEVEL_CAUTION, // Warnings + Caution + Errors VERBOSE_LOG_LEVEL_INFO_CAUTION = LOG_LEVEL_INFO | LOG_LEVEL_CAUTION, // Info + Caution + Errors VERBOSE_LOG_LEVEL_ALL = (LOG_LEVEL_WARNING | LOG_LEVEL_INFO | LOG_LEVEL_CAUTION) // All: Warnings + Info + Caution + Errors }; constexpr std::string_view WARNING_TEXT = "WARNING"; constexpr std::string_view CAUTION_TEXT = "CAUTION"; constexpr std::string_view INFO_TEXT = "INFO"; constexpr std::string_view ERROR_TEXT = "ERROR"; constexpr std::string_view CRITICAL_ERROR_TEXT = "CRITICAL ERROR"; constexpr std::string_view FATAL_ERROR_TEXT = "FATAL ERROR"; //+------------------------------------------------------------------+ //| Base Logging Class with Flag System | //+------------------------------------------------------------------+ class CLoggerBase { private: unsigned char m_log_flags; // Active logging flags unsigned char m_last_flags; // Last active logging flags //--- Main logging method with flags FORCE_INLINE void RemoveFlag(unsigned char flags) noexcept { m_log_flags &= ~flags; } protected: //--- Flag-specific methods FORCE_INLINE void LogWarning(const std::string& message, const std::string& function) const noexcept; FORCE_INLINE void LogInfo(const std::string& message, const std::string& function) const noexcept; FORCE_INLINE void LogCaution(const std::string& message, const std::string& function) const noexcept; public: CLoggerBase() noexcept; virtual ~CLoggerBase() noexcept {} //--- void ForceRemoveLogErrors() noexcept; static FORCE_INLINE void LogError(const std::string& message, const std::string& function) noexcept; static FORCE_INLINE void LogFatalError(const std::string& message, const std::string& function) noexcept; static FORCE_INLINE void LogCriticalError(const std::string& message, const std::string& function) noexcept; //--- Methods to check if a flag is active FORCE_INLINE bool IsWarningLogEnabled() const noexcept { return (m_log_flags & LOG_LEVEL_WARNING) != 0; } FORCE_INLINE bool IsInfoLogEnabled() const noexcept { return (m_log_flags & LOG_LEVEL_INFO) != 0; } FORCE_INLINE bool IsCautionLogEnabled() const noexcept { return (m_log_flags & LOG_LEVEL_CAUTION) != 0; } //--- Getters FORCE_INLINE unsigned char LogFlags() const noexcept { return m_log_flags; } inline bool IsLogEnabled(unsigned char flag) const noexcept { return (m_log_flags & flag) != 0; } //--- Main configuration using flags virtual inline void AddLogFlags(const unsigned char flags) noexcept; virtual void RemoveLogFlags(const unsigned char flags) noexcept; inline void ResetLastStateFlags() noexcept { m_log_flags = m_last_flags; } //--- Disbale and Enable all logs virtual void EnableAllLogs() noexcept; virtual void DisableAllLogs() noexcept; }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CLoggerBase::CLoggerBase(void) noexcept { m_log_flags = LOG_LEVEL_ERROR; m_last_flags = LOG_LEVEL_ERROR; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CLoggerBase::ForceRemoveLogErrors(void) noexcept { m_last_flags = m_log_flags; RemoveFlag(LOG_LEVEL_ERROR); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CLoggerBase::DisableAllLogs() noexcept { m_log_flags = LOG_LEVEL_ERROR; } //+------------------------------------------------------------------+ void CLoggerBase::EnableAllLogs(void) noexcept { m_log_flags |= LOG_ALL; } //+------------------------------------------------------------------+ inline void CLoggerBase::AddLogFlags(const unsigned char flags) noexcept { m_last_flags = m_log_flags; m_log_flags |= flags; } //+------------------------------------------------------------------+ void CLoggerBase::RemoveLogFlags(const unsigned char flags) noexcept { m_last_flags = m_log_flags; unsigned char safe_flags = flags & ~LOG_LEVEL_ERROR; RemoveFlag(safe_flags); m_log_flags |= LOG_LEVEL_ERROR; } //+------------------------------------------------------------------+ //| Método principal de logging | //+------------------------------------------------------------------+ #define FastLog(function, class_info, message) std::cerr << "[" << class_info << "] " << function << " | " << message << std::endl //+------------------------------------------------------------------+ //| Métodos específicos por flag | //+------------------------------------------------------------------+ FORCE_INLINE void CLoggerBase::LogError(const std::string& message, const std::string& function) noexcept { FastLog(function, ERROR_TEXT, message); } //+------------------------------------------------------------------+ FORCE_INLINE void CLoggerBase::LogCriticalError(const std::string& message, const std::string& function) noexcept { FastLog(function, CRITICAL_ERROR_TEXT, message); } //+------------------------------------------------------------------+ FORCE_INLINE void CLoggerBase::LogFatalError(const std::string& message, const std::string& function) noexcept { FastLog(function, FATAL_ERROR_TEXT, message); } //+------------------------------------------------------------------+ FORCE_INLINE void CLoggerBase::LogWarning(const std::string& message, const std::string& function) const noexcept { if((m_log_flags & LOG_LEVEL_WARNING) != 0) FastLog(function, WARNING_TEXT, message); } //+------------------------------------------------------------------+ FORCE_INLINE void CLoggerBase::LogInfo(const std::string& message, const std::string& function) const noexcept { if((m_log_flags & LOG_LEVEL_INFO) != 0) FastLog(function, INFO_TEXT, message); } //+------------------------------------------------------------------+ FORCE_INLINE void CLoggerBase::LogCaution(const std::string& message, const std::string& function) const noexcept { if((m_log_flags & LOG_LEVEL_CAUTION) != 0) FastLog(function, CAUTION_TEXT, message); } //+------------------------------------------------------------------+ //| Specialized Logger Manager Class port of MQL5 | //+------------------------------------------------------------------+ //| Manages multiple loggers and propagates flags to all of them. | //| This is like an evolution of CLoggerBase that can manage | //| multiple child loggers and synchronize their logging levels. | //+------------------------------------------------------------------+ class CSpecializedManager : public CLoggerBase { protected: std::vector m_loggers; // Vector of logger pointers //--- Propagate flags to all loggers void PropagateFlags(const unsigned char flags, bool enable) noexcept; //--- Remove logger by pointer bool RemoveLogger(CLoggerBase* logger) noexcept; //--- Delete logger if already exists void DeleteLoggerAlreadyExist(CLoggerBase* logger) noexcept; //--- Add logger (pointer version) void AddLogger(CLoggerBase* logger) noexcept; //--- Add logger (reference version) FORCE_INLINE void AddLogger(CLoggerBase& logger) noexcept { AddLogger(&logger); } //--- Clean loggers (with or without deletion) virtual void CleanLoggers(bool delete_ptrs = true) noexcept; public: CSpecializedManager() noexcept; virtual ~CSpecializedManager() noexcept {} //--- Override flag methods to propagate void AddLogFlags(const unsigned char flags) noexcept override; void RemoveLogFlags(const unsigned char flags) noexcept override; void EnableAllLogs() noexcept override; void DisableAllLogs() noexcept override; }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSpecializedManager::CSpecializedManager() noexcept : CLoggerBase() { m_loggers.clear(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSpecializedManager::AddLogger(CLoggerBase* logger) noexcept { if (!logger) { LogFatalError("Logger pointer is invalid", FUNC_NAME); return; } m_loggers.push_back(logger); logger->AddLogFlags(LogFlags()); LogInfo("Logger added. Total: " + std::to_string(m_loggers.size()), FUNC_NAME); } //+------------------------------------------------------------------+ bool CSpecializedManager::RemoveLogger(CLoggerBase* logger) noexcept { if (!logger) { LogWarning("Removing a nullptr logger", FUNC_NAME); return false; } for (size_t i = 0; i < m_loggers.size(); i++) { if (m_loggers[i] == logger) { m_loggers.erase(m_loggers.begin() + i); LogInfo("Logger removed. Total: " + std::to_string(m_loggers.size()), FUNC_NAME); return true; } } LogWarning("Logger not found for removal", FUNC_NAME); return false; } //+------------------------------------------------------------------+ void CSpecializedManager::DeleteLoggerAlreadyExist(CLoggerBase* logger) noexcept { if (!logger) return; for (size_t i = 0; i < m_loggers.size(); i++) { if (m_loggers[i] == logger) { delete m_loggers[i]; m_loggers.erase(m_loggers.begin() + i); LogInfo("Logger deleted. Total: " + std::to_string(m_loggers.size()), FUNC_NAME); return; } } LogWarning("Logger not found for deletion", FUNC_NAME); } //+------------------------------------------------------------------+ void CSpecializedManager::PropagateFlags(const unsigned char flags, bool enable) noexcept { for (auto logger : m_loggers) { if (logger) { if (enable) logger->AddLogFlags(flags); else logger->RemoveLogFlags(flags); } } } //+------------------------------------------------------------------+ void CSpecializedManager::AddLogFlags(const unsigned char flags) noexcept { CLoggerBase::AddLogFlags(flags); PropagateFlags(flags, true); } //+------------------------------------------------------------------+ void CSpecializedManager::RemoveLogFlags(const unsigned char flags) noexcept { CLoggerBase::RemoveLogFlags(flags); PropagateFlags(flags, false); } //+------------------------------------------------------------------+ void CSpecializedManager::EnableAllLogs() noexcept { CLoggerBase::EnableAllLogs(); for (auto logger : m_loggers) { if (logger) logger->EnableAllLogs(); } } //+------------------------------------------------------------------+ void CSpecializedManager::DisableAllLogs() noexcept { CLoggerBase::DisableAllLogs(); for (auto logger : m_loggers) { if (logger) logger->DisableAllLogs(); } } //+------------------------------------------------------------------+ void CSpecializedManager::CleanLoggers(bool delete_ptrs) noexcept { if (delete_ptrs) { for (auto logger : m_loggers) { if (logger) { delete logger; logger = nullptr; } } } m_loggers.clear(); LogInfo("All loggers cleaned", FUNC_NAME); } }