1515
1616#include " InfoLogger/InfoLogger.hxx"
1717#include " InfoLogger/InfoLogger.h"
18+ #include " InfoLogger/InfoLoggerMacros.hxx"
1819
1920#include " infoLoggerMessage.h"
2021#include " InfoLoggerClient.h"
@@ -184,6 +185,8 @@ class InfoLogger::Impl
184185 currentStreamMessage.clear ();
185186 currentStreamOptions = undefinedMessageOption;
186187
188+ floodReset ();
189+
187190 if (infoLog_proto_init ()) {
188191 throw __LINE__;
189192 }
@@ -233,11 +236,13 @@ class InfoLogger::Impl
233236 if (client != nullptr ) {
234237 delete client;
235238 }
239+ floodReset ();
236240 }
237241
238242 int pushMessage (InfoLogger::Severity severity, const char * msg); // todo: add extra "configurable" fields, e.g. line, etc
239243
240- int pushMessage (const InfoLoggerMessageOption& options, const InfoLoggerContext& context, const char * msg);
244+ // the noFlood parameter allows to send message even if in flood mode
245+ int pushMessage (const InfoLoggerMessageOption& options, const InfoLoggerContext& context, const char * msg, bool noFlood=0 );
241246
242247 friend class InfoLogger ; // < give access to this data from InfoLogger class
243248
@@ -293,6 +298,40 @@ class InfoLogger::Impl
293298
294299 bool filterDiscardDebug = false ; // when set, messages with debug severity are dropped
295300 int filterDiscardLevel = InfoLogger::undefinedMessageOption.level; // when set, messages with higher level (>=) are dropped
301+
302+ // message flood prevention
303+ // constants
304+ const unsigned int flood_maxmsg_sec=500 ; // maximum messages in one second to trigger flood
305+ const unsigned int flood_maxmsg_min=1000 ; // maximum messages in one minute to trigger flood
306+ const unsigned int flood_maxmsg_file=1000 ; // maximum number of messages in overflow log file
307+ const unsigned int flood_maxmsg_reset=10 ; // maximum number of messages in one minute to reset
308+ const std::string floodFile_dir=" /tmp" ; // path where to create overflow files
309+ // runtime vars
310+ unsigned int floodStat_msgs_sec; // number of messages in last second
311+ unsigned int floodStat_msgs_min; // number of messages since minute interval begin
312+ unsigned int floodStat_msgs_lastmin; // number of messages in last minute
313+ double floodStat_time_lastsec; // time counter for last second
314+ double floodStat_time_lastmin; // time counter for last minute
315+ int floodMode; // current flood status = 0: no flood, 1: messages redirected to local file, 2: messages dropped
316+ FILE *floodFile_fp=nullptr ; // handle to current flood file to store messages in excess
317+ unsigned int floodFile_msg; // number of messages recorded to flood file
318+ unsigned int floodFile_msg_drop; // number of messages dropped
319+
320+ void floodReset () {
321+ floodStat_msgs_sec=0 ;
322+ floodStat_msgs_min=0 ;
323+ floodStat_msgs_lastmin=flood_maxmsg_reset;
324+ floodStat_time_lastsec=time (NULL );
325+ floodStat_time_lastmin=time (NULL );
326+ floodMode=0 ;
327+ if (floodFile_fp!=nullptr ) {
328+ fclose (floodFile_fp);
329+ }
330+ floodFile_fp=nullptr ;
331+ floodFile_msg=0 ;
332+ floodFile_msg_drop=0 ;
333+ }
334+
296335};
297336
298337void InfoLogger::Impl::refreshDefaultMsg ()
@@ -353,7 +392,7 @@ int InfoLogger::Impl::pushMessage(InfoLogger::Severity severity, const char* mes
353392 return pushMessage (options, currentContext, messageBody);
354393}
355394
356- int InfoLogger::Impl::pushMessage (const InfoLoggerMessageOption& options, const InfoLoggerContext& context, const char * messageBody)
395+ int InfoLogger::Impl::pushMessage (const InfoLoggerMessageOption& options, const InfoLoggerContext& context, const char * messageBody, bool noFlood )
357396{
358397 // check if message passes local filter criteria, if any
359398 if (filterDiscardDebug && (options.severity == InfoLogger::Severity::Debug)) {
@@ -368,8 +407,9 @@ int InfoLogger::Impl::pushMessage(const InfoLoggerMessageOption& options, const
368407 infoLog_msg_t msg = defaultMsg;
369408
370409 struct timeval tv;
410+ double now = 0 ;
371411 if (gettimeofday (&tv, NULL ) == 0 ) {
372- double now = (double )tv.tv_sec + (double )tv.tv_usec / 1000000 ;
412+ now = (double )tv.tv_sec + (double )tv.tv_usec / 1000000 ;
373413 InfoLoggerMessageHelperSetValue (msg, msgHelper.ix_timestamp , Double, now);
374414 }
375415
@@ -435,7 +475,78 @@ int InfoLogger::Impl::pushMessage(const InfoLoggerMessageOption& options, const
435475 InfoLoggerMessageHelperSetValue (msg, msgHelper.ix_username , String, context.userName .c_str ());
436476 }
437477
438- // printf("%s\n",buffer);
478+ // update message statistics */
479+ if (now-floodStat_time_lastsec>1 ) {
480+ floodStat_time_lastsec=now;
481+ floodStat_msgs_sec=1 ;
482+ } else {
483+ floodStat_msgs_sec++;
484+ }
485+ if (now-floodStat_time_lastmin>60 ) {
486+ floodStat_time_lastmin=now;
487+ floodStat_msgs_lastmin=floodStat_msgs_min;
488+ floodStat_msgs_min=1 ;
489+ } else {
490+ floodStat_msgs_min++;
491+ }
492+
493+ // message flood prevention
494+ if (!noFlood) {
495+ switch (floodMode) {
496+ case 0 :
497+ if ((floodStat_msgs_sec>flood_maxmsg_sec)||(floodStat_msgs_min>flood_maxmsg_min)) {
498+ floodFile_msg=0 ;
499+ floodFile_msg_drop=0 ;
500+ std::string floodFile_path = floodFile_dir + " /infoLogger.flood-" + std::to_string (context.processId ) + " @" + context.hostName + " -" + std::to_string ((int )now);
501+ floodFile_fp=fopen (floodFile_path.c_str ()," w" );
502+ if (floodFile_fp==NULL ) {
503+ floodMode=2 ;
504+ std::string msg = " Message flood detected - further messages will be dropped (failed to create flood file " + floodFile_path + " )" ;
505+ pushMessage (LogWarningSupport_ (1101 ), currentContext, msg.c_str (),1 );
506+ goto case2;
507+ } else {
508+ floodMode=1 ;
509+ std::string msg = " Message flood detected - further messages will be stored locally in " + floodFile_path;
510+ pushMessage (LogWarningSupport_ (1101 ), currentContext, msg.c_str (),1 );
511+ }
512+ } else {
513+ break ;
514+ }
515+ case 1 :
516+ if (floodStat_msgs_lastmin<flood_maxmsg_reset) {
517+ // reset flood mode
518+ break ;
519+ }
520+ if (floodFile_msg>=flood_maxmsg_file) {
521+ if (floodFile_fp!=nullptr ) {
522+ fclose (floodFile_fp);
523+ floodFile_fp=nullptr ;
524+ }
525+ pushMessage (LogWarningSupport_ (1101 ), currentContext, " Message flood - maximum entries in local file exceeded, further messages will be dropped" ,1 );
526+ floodMode=2 ;
527+ } else {
528+ // log to flood file
529+ fprintf (floodFile_fp," %f\t %c\t %s\t %s\n " ,now,(char )options.severity ,context.facility .c_str (),messageBody);
530+ fflush (floodFile_fp);
531+ floodFile_msg++;
532+ return 0 ;
533+ }
534+ case 2 :
535+ case2:
536+ if (floodStat_msgs_lastmin<flood_maxmsg_reset) {
537+ // reset flood mode
538+ break ;
539+ }
540+ // drop message
541+ floodFile_msg_drop++;
542+ return 0 ;
543+ }
544+ if (floodMode) {
545+ std::string msg = " Message flood - resuming normal operation: " + std::to_string (floodFile_msg) + " messages stored locally, " + std::to_string (floodFile_msg_drop) + " messages dropped" ;
546+ pushMessage (LogWarningSupport_ (1102 ), currentContext, msg.c_str (),1 );
547+ floodReset ();
548+ }
549+ }
439550
440551 if (client != nullptr ) {
441552 char buffer[LOG_MAX_SIZE];
0 commit comments