//+------------------------------------------------------------------+ //| LogifySuppression.mqh | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "../LogifyModel.mqh" //+------------------------------------------------------------------+ //| Validation constants | //+------------------------------------------------------------------+ #define MAX_SUPPRESSION_MODE 255 // Maximum valid mode combination #define MIN_THROTTLE_SECONDS 1 // Minimum interval between messages #define MIN_REPEAT_COUNT 1 // Minimum number of repetitions //+------------------------------------------------------------------+ //| ENUMS Suppression modes that can be combined using bitwise OR | //+------------------------------------------------------------------+ enum ENUM_LOG_SUPRESSION_MODE { LOG_SUPRESSION_MODE_NONE = 0, // No suppression LOG_SUPRESSION_MODE_CONSECUTIVE = 1 << 0, // Identical consecutive messages LOG_SUPRESSION_MODE_THROTTLE_TIME = 1 << 1, // Same message within X seconds LOG_SUPRESSION_MODE_BY_REPEAT_COUNT = 1 << 2, // After N repetitions LOG_SUPRESSION_MODE_BY_ORIGIN = 1 << 3, // Based on message origin LOG_SUPRESSION_MODE_BY_FILENAME = 1 << 4, // Based on source filename }; //+------------------------------------------------------------------+ //| Struct: MqlLogifySuppressionConfig | //+------------------------------------------------------------------+ struct MqlLogifySuppressionConfig { // Basic configuration int mode; // Combination of suppression modes int throttle_seconds; // Seconds between messages int max_repeat_count; // Max repetitions before suppression // Origin whitelist/blacklist string allowed_origins[]; // If not empty, only these are allowed string blocked_origins[]; // Always blocked // Filename whitelist/blacklist string allowed_filenames[]; // If not empty, only these are allowed string blocked_filenames[]; // Always blocked //--- Default constructor MqlLogifySuppressionConfig(void) { mode = LOG_SUPRESSION_MODE_THROTTLE_TIME | LOG_SUPRESSION_MODE_CONSECUTIVE | LOG_SUPRESSION_MODE_BY_REPEAT_COUNT; throttle_seconds = 5; max_repeat_count = 15; ArrayResize(allowed_origins, 0); ArrayResize(blocked_origins, 0); ArrayResize(allowed_filenames, 0); ArrayResize(blocked_filenames, 0); } //--- Destructor ~MqlLogifySuppressionConfig(void) { } //--- Helper methods for array configuration void AddAllowedOrigin(string origin) { int size = ArraySize(allowed_origins); ArrayResize(allowed_origins, size + 1); allowed_origins[size] = origin; } void AddBlockedOrigin(string origin) { int size = ArraySize(blocked_origins); ArrayResize(blocked_origins, size + 1); blocked_origins[size] = origin; } void AddAllowedFilename(string filename) { int size = ArraySize(allowed_filenames); ArrayResize(allowed_filenames, size + 1); allowed_filenames[size] = filename; } void AddBlockedFilename(string filename) { int size = ArraySize(blocked_filenames); ArrayResize(blocked_filenames, size + 1); blocked_filenames[size] = filename; } //--- Validates configuration parameters bool ValidateConfig(string &error_message) { if(throttle_seconds < MIN_THROTTLE_SECONDS) { error_message = "throttle_seconds must be greater than or equal to " + (string)MIN_THROTTLE_SECONDS; return false; } if(max_repeat_count < MIN_REPEAT_COUNT) { error_message = "max_repeat_count must be greater than or equal to " + (string)MIN_REPEAT_COUNT; return false; } if(mode < LOG_SUPRESSION_MODE_NONE || mode > MAX_SUPPRESSION_MODE) { error_message = "invalid suppression mode"; return false; } return true; } }; //+------------------------------------------------------------------+ //| class : CLogifySuppression | //| | //| [PROPERTY] | //| Name : CLogifySuppression | //| Heritage : No heritage | //| Description : Class responsible for log message suppression, | //| supports multiple suppression modes that can be combined: | //| | //| Suppression Modes | //| - Consecutive : Blocks identical sequential messages | //| - Throttle : Blocks messages within time window | //| - RepeatCount : Blocks after N repetitions | //| - Origin : Blocks based on message origin (white/blacklist)| //| - Filename : Blocks based on source file (white/blacklist) | //+------------------------------------------------------------------+ class CLogifySuppression { private: //--- Configuration MqlLogifySuppressionConfig m_config; //--- State tracking string m_last_message; ENUM_LOG_LEVEL m_last_level; int m_repeat_count; datetime m_last_time; //--- Helper methods for string comparison bool StringContainsIgnoreCase(string text, string search_term); public: CLogifySuppression(void); ~CLogifySuppression(void); //--- Configuration management void SetConfig(MqlLogifySuppressionConfig &config); MqlLogifySuppressionConfig GetConfig(void) const; //--- State management void Reset(void); //--- Monitoring getters int GetRepeatCount(void) const { return m_repeat_count; } datetime GetLastMessageTime(void) const { return m_last_time; } string GetLastMessage(void) const { return m_last_message; } ENUM_LOG_LEVEL GetLastLevel(void) const { return m_last_level; } //--- Main suppression logic bool ShouldSuppress(MqlLogifyModel &data); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CLogifySuppression::CLogifySuppression(void) { Reset(); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CLogifySuppression::~CLogifySuppression(void) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CLogifySuppression::StringContainsIgnoreCase(string text, string search_term) { string text_lower = text; string term_lower = search_term; StringToLower(text_lower); StringToLower(term_lower); return StringFind(term_lower, text_lower) >= 0; } //+------------------------------------------------------------------+ //| Resets all internal state tracking | //+------------------------------------------------------------------+ void CLogifySuppression::Reset(void) { m_last_message = ""; m_repeat_count = 0; m_last_time = 0; m_last_level = LOG_LEVEL_INFO; } //+------------------------------------------------------------------+ //| Updates suppression configuration | //+------------------------------------------------------------------+ void CLogifySuppression::SetConfig(MqlLogifySuppressionConfig &config) { m_config = config; string err_msg = ""; if(!m_config.ValidateConfig(err_msg)) { Print("[ERROR] ["+TimeToString(TimeCurrent())+"] Log system error: "+err_msg); } } //+------------------------------------------------------------------+ //| Returns current configuration | //+------------------------------------------------------------------+ MqlLogifySuppressionConfig CLogifySuppression::GetConfig(void) const { return m_config; } //+------------------------------------------------------------------+ //| Checks if a message should be suppressed based on active modes | //+------------------------------------------------------------------+ bool CLogifySuppression::ShouldSuppress(MqlLogifyModel &data) { datetime now = data.date_time; //--- Check origin-based suppression if((m_config.mode & LOG_SUPRESSION_MODE_BY_ORIGIN) == LOG_SUPRESSION_MODE_BY_ORIGIN) { //--- Check blacklist first if(ArraySize(m_config.blocked_origins) > 0) { for(int i = 0; i < ArraySize(m_config.blocked_origins); i++) { if(StringContainsIgnoreCase(data.origin, m_config.blocked_origins[i])) { return true; } } } //--- Then check whitelist if(ArraySize(m_config.allowed_origins) > 0) { bool origin_allowed = false; for(int i = 0; i < ArraySize(m_config.allowed_origins); i++) { if(StringContainsIgnoreCase(data.origin, m_config.allowed_origins[i])) { origin_allowed = true; break; } } if(!origin_allowed) { return true; } } } //--- Check filename-based suppression if((m_config.mode & LOG_SUPRESSION_MODE_BY_FILENAME) == LOG_SUPRESSION_MODE_BY_FILENAME) { //--- Check blacklist first if(ArraySize(m_config.blocked_filenames) > 0) { for(int i = 0; i < ArraySize(m_config.blocked_filenames); i++) { if(StringContainsIgnoreCase(data.filename, m_config.blocked_filenames[i])) { return true; } } } //--- Then check whitelist if(ArraySize(m_config.allowed_filenames) > 0) { bool filename_allowed = false; for(int i = 0; i < ArraySize(m_config.allowed_filenames); i++) { if(StringContainsIgnoreCase(data.filename, m_config.allowed_filenames[i])) { filename_allowed = true; break; } } if(!filename_allowed) { return true; } } } //--- Reset counters if message or level changed if(data.msg != m_last_message || data.level != m_last_level) { m_repeat_count = 0; m_last_message = data.msg; m_last_level = data.level; m_last_time = now; return false; } //--- Increment counter once per check m_repeat_count++; //--- Check suppression modes if(((m_config.mode & LOG_SUPRESSION_MODE_BY_REPEAT_COUNT) == LOG_SUPRESSION_MODE_BY_REPEAT_COUNT) && m_repeat_count >= m_config.max_repeat_count) { return true; } if(((m_config.mode & LOG_SUPRESSION_MODE_THROTTLE_TIME) == LOG_SUPRESSION_MODE_THROTTLE_TIME) && (now - m_last_time) < m_config.throttle_seconds) { return true; } if((m_config.mode & LOG_SUPRESSION_MODE_CONSECUTIVE) == LOG_SUPRESSION_MODE_CONSECUTIVE) { return true; } m_last_time = now; return false; } //+------------------------------------------------------------------+