LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
ELdestination.cc
Go to the documentation of this file.
2 // vim: set sw=2 expandtab :
3 
4 #include "cetlib/bold_fontify.h"
6 #include "fhiclcpp/types/Atom.h"
9 #include "fhiclcpp/types/Table.h"
14 
15 #include <bitset>
16 #include <cstddef>
17 #include <cstdio>
18 #include <functional>
19 #include <iomanip>
20 #include <limits>
21 #include <map>
22 #include <ostream>
23 #include <sstream>
24 #include <string>
25 
26 #include <sys/time.h>
27 #include <time.h>
28 
29 using namespace std;
30 using namespace std::string_literals;
31 
33  "ELdestination"s;
34 
35 namespace mf::service {
36 
37  ELdestination::Category::Config::~Config() = default;
38 
39  ELdestination::Category::Config::Config()
40  : limit{fhicl::Name{"limit"}, -1}
41  , reportEvery{fhicl::Name{"reportEvery"}, -1}
42  , timespan{fhicl::Name{"timespan"}, -1}
43  {}
44 
46  : limit{fhicl::Name{"limit"}, pset.get<int>("limit", -1)}
47  , reportEvery{fhicl::Name{"reportEvery"}, pset.get<int>("reportEvery", -1)}
48  , timespan{fhicl::Name{"timespan"}, pset.get<int>("timespan", -1)}
49  {}
50 
51  std::string
53  {
54  return R"(The 'limit' parameter is an integer after which the logger will start
55 to ignore messages of this category. Beyond the specified limit, an
56 occasional further message will appear, based on an exponential
57 fall-off. For example, if the limit is set to 5, and 100 messages of
58 this category are issued, then the destination containing this
59 configuration will log messages numbered 1, 2, 3, 4, 5, 10, 15, 25,
60 45, and 85. A limit of zero disables reporting any messages.)";
61  }
62 
63  std::string
65  {
66  return R"(The 'reportEvery' parameter is an integer n which logs only every nth
67 message. If the value is zero or less, then the report-every feature
68 is disabled.)";
69  }
70 
71  std::string
73  {
74  return R"(The 'timespan' parameter is an integer representing seconds. When a
75 limit is set, one can also specify that if no occurrences for that
76 particular category of messages have been seen in a period of time
77 (the timespan), then the count toward that limit is to be reset. For
78 example, if one wish to suppress most of the thousands of warnings of
79 some category expected at startup, but would like to know if another
80 one happens after a gap of ten minutes, one can set the timespan value
81 to 600. A value of zero or less disables the timespan functionality.)";
82  }
83 
86 
89  : limit_{}, reportEvery_{}, timespan_{}
90  {}
91 
94  : previousTimestamp_{}
95  , msgCount_{}
97  , limit_{}
98  , reportEvery_{}
99  , timespan_{}
100  {}
101 
104  : timestamp{fhicl::Name("timestamp"),
106  "The 'timestamp' parameter represents a format that can be "
107  "interpreted\n"
108  "by strftime. Allowed values include:\n\n"
109  " - \"none\" (suppress timestamp printing)\n"
110  " - \"default\" (format string shown below)\n"
111  " - \"default_ms\" (use millisecond precision)\n"
112  " - any user-specified format interpretable by strftime"),
113  "%d-%b-%Y %H:%M:%S %Z"}
114  , noLineBreaks{fhicl::Name("noLineBreaks"), false}
115  , lineLength{fhicl::Name{"lineLength"},
116  fhicl::Comment{"The following parameter is allowed only if "
117  "'noLineBreaks' has been set to 'false'."},
118  [this] { return !noLineBreaks(); },
119  80ull}
120  , wantModule{fhicl::Name("wantModule"), true}
121  , wantSubroutine{fhicl::Name("wantSubroutine"), true}
122  , wantText{fhicl::Name("wantText"), true}
123  , wantSomeContext{fhicl::Name("wantSomeContext"), true}
124  , wantSerial{fhicl::Name("wantSerial"), false}
125  , wantFullContext{fhicl::Name("wantFullContext"), false}
126  , wantTimeSeparate{fhicl::Name("wantTimeSeparate"), false}
127  , wantEpilogueSeparate{fhicl::Name("wantEpilogueSeparate"), false}
128  {}
129 
131 
133  : timeMethod_{0}, timeFmt_{}
134  {
135  auto const& value = config.timestamp();
136  bool const use_timestamp = (value != "none"s);
137  flags.set(TIMESTAMP, use_timestamp);
138  if (!use_timestamp) {
139  timeMethod_ = 0;
140  } else if (value == "default"s) {
141  timeMethod_ = 1;
142  timeFmt_ = "%d-%b-%Y %H:%M:%S %Z"s;
143  } else if (value == "default_ms"s) {
144  timeMethod_ = 2;
145  timeFmt_ = "%d-%b-%Y %H:%M:%S.%%03u %Z"s;
146  } else {
147  timeMethod_ = 1;
148  timeFmt_ = value;
149  }
150  lineLength = config.noLineBreaks() ? 32000ull : config.lineLength();
151  flags.set(NO_LINE_BREAKS, config.noLineBreaks());
152  flags.set(MODULE, config.wantModule());
153  flags.set(SUBROUTINE, config.wantSubroutine());
154  flags.set(TEXT, config.wantText());
155  flags.set(SOME_CONTEXT, config.wantSomeContext());
156  flags.set(SERIAL, config.wantSerial());
157  flags.set(FULL_CONTEXT, config.wantFullContext());
158  flags.set(TIME_SEPARATE, config.wantTimeSeparate());
159  flags.set(EPILOGUE_SEPARATE, config.wantEpilogueSeparate());
160  }
161 
162  bool
164  {
165  return flags.test(FLAG);
166  }
167 
168  string
170  {
171  size_t constexpr SIZE{144};
172  string ret;
173  if (timeMethod_ == 0) {
174  return ret;
175  }
176  struct tm timebuf;
177  char ts[SIZE];
178  if (timeMethod_ == 1) {
179  strftime(
180  ts, sizeof(ts), timeFmt_.data(), localtime_r(&t.tv_sec, &timebuf));
181  ret = ts;
182  return ret;
183  }
184  char tmpts[SIZE];
185  strftime(
186  tmpts, sizeof(tmpts), timeFmt_.data(), localtime_r(&t.tv_sec, &timebuf));
187  snprintf(ts, sizeof(ts), tmpts, static_cast<unsigned>(t.tv_usec / 1000));
188  ret = ts;
189  return ret;
190  }
191 
193 
195  : n_{}
196  , aggregateN_{}
197  , ignoredFlag_{false}
198  , context1_{}
199  , context2_{}
200  , contextLast_{}
201  {}
202 
203  void
204  ELdestination::StatsCount::add(string const& context, bool const reactedTo)
205  {
206  if (reactedTo) {
207  ++n_;
208  }
209  ++aggregateN_;
210  ((n_ == 1) ? context1_ :
211  (n_ == 2) ? context2_ :
212  contextLast_) = string(context, 0, 16);
213  if (!reactedTo) {
214  ignoredFlag_ = true;
215  }
216  }
217 
219 
221  : reset{fhicl::Name("reset"),
222  fhicl::Comment("Used for statistics destinations"),
223  false}
224  , resetStatistics{fhicl::Name("resetStatistics"), false}
225  {}
226 
227  ELdestination::Config::~Config() = default;
228 
230  : dest_type{fhicl::Name{"type"}}
231  , threshold{fhicl::Name{"threshold"},
233  "The 'threshold' parameter specifies the lowest severity "
234  "level of\n"
235  "messages that will be logged to the destination"},
236  "INFO"}
237  , format{fhicl::Name{"format"}}
238  , categories{fhicl::Name{"categories"},
240  R"(The 'categories' parameter (if provided) is a FHiCL table that
241 configures the behavior of logging to this destination for the specified
242 category. For example, if the following appears in C++ source code:
243 
244  mf::LogInfo{"Tracking"} << my_track.diagnostics();
245 
246 the category is 'Tracking', and its behavior can be specified via:
247 
248  categories: {
249  Tracking: {
250  limit: -1 # default
251  reportEvery: -1 # default
252  timespan: -1 # default
253  }
254  }
255 
256 Within the 'categories' table, it is permitted to specify a 'default'
257 category, which becomes the configuration for all message categories
258 that are not explicitly listed.
259 
260 Note the categories listed only customize the behavior of messages
261 that are logged specifically to this destination. Messages that are
262 routed to other destinations are not be affected.
263 
264 Category parameters
265 ===================
266 
268  "\n\n" + Category::Config::reportEvery_comment() + "\n\n" +
270  , outputStatistics{fhicl::Name{"outputStatistics"}, false}
271  , msgStatistics{}
272  {}
273 
275  : threshold_{pset.threshold()}
276  , format_{pset.format()}
277  , defaultLimit_{numeric_limits<decltype(defaultLimit_)>::max()}
278  , defaultReportEvery_{-1}
279  , defaultTimespan_{numeric_limits<decltype(defaultTimespan_)>::max()}
280  , categoryParams_{}
281  , xidLimiters_{}
282  , statsMap_{}
283  , outputStatistics_{pset.outputStatistics()}
284  , updatedStats_{false}
285  , reset_{pset.msgStatistics().reset() ||
286  pset.msgStatistics().resetStatistics()}
287  , charsOnLine_{}
288  {
289  vector<string> configuration_errors;
290  // Grab this destination's category configurations.
291  fhicl::ParameterSet cats_pset{};
292  pset.categories.get_if_present<fhicl::ParameterSet>(cats_pset);
293  // Grab the list of categories, removing the default category
294  // since it is handled specially.
295  auto const default_category_name = "default"s;
296  auto categories = cats_pset.get_pset_names();
297  auto erase_from =
298  remove_if(begin(categories), end(categories), [](auto const& category) {
299  return category == "default"s;
300  });
301  categories.erase(erase_from, categories.cend());
302  // Setup the default configuration for categories--this involves
303  // resetting the limits table according to the user-specified
304  // default configuration.
305  auto const& default_pset =
306  cats_pset.get<fhicl::ParameterSet>("default"s, {});
307  try {
308  fhicl::WrappedTable<Category::Config> default_params{default_pset};
309  defaultLimit_ = (default_params().limit() < 0) ?
310  numeric_limits<decltype(defaultLimit_)>::max() :
311  default_params().limit();
312  defaultReportEvery_ = default_params().reportEvery();
313  defaultTimespan_ = (default_params().timespan() < 0) ?
314  numeric_limits<decltype(defaultTimespan_)>::max() :
315  default_params().timespan();
316  }
317  catch (fhicl::detail::validationException const& e) {
318  string msg{"Category: "s + cet::bold_fontify("default"s) + "\n\n"s};
319  msg += e.what();
320  configuration_errors.push_back(std::move(msg));
321  }
322  // Now establish this destination's limit/reportEvery/timespan for
323  // each category, using the values of the possibly-specified
324  // default configuration when a given category is missing the
325  // fields.
326  for (auto const& category : categories) {
327  fhicl::Table<Category::Config> category_params{fhicl::Name{category},
328  default_pset};
329  try {
330  category_params.validate(cats_pset.get<fhicl::ParameterSet>(category));
331  }
332  catch (fhicl::detail::validationException const& e) {
333  string msg{"Category: " + cet::bold_fontify(category) + "\n\n"};
334  msg += e.what();
335  configuration_errors.push_back(std::move(msg));
336  }
337 
338  if (category_params().limit() < 0) {
339  categoryParams_[category].limit_ = numeric_limits<int>::max();
340  } else {
341  categoryParams_[category].limit_ = category_params().limit();
342  }
343  categoryParams_[category].reportEvery_ = category_params().reportEvery();
344  if (category_params().timespan() < 0) {
345  categoryParams_[category].timespan_ = numeric_limits<int>::max();
346  } else {
347  categoryParams_[category].timespan_ = category_params().timespan();
348  }
349  }
350  if (!configuration_errors.empty()) {
351  string msg{"The following categories were misconfigured:\n\n"};
352  for (auto const& error : configuration_errors) {
353  msg += error;
354  }
356  }
357  }
358 
359  string
361  {
362  if (c.substr(0, 4) != "Run:") {
363  return c;
364  }
365  istringstream is(c);
366  string runWord;
367  int run;
368  is >> runWord >> run;
369  if (!is) {
370  return c;
371  }
372  if (runWord != "Run:") {
373  return c;
374  }
375  string eventWord;
376  int event;
377  is >> eventWord >> event;
378  if (!is) {
379  return c;
380  }
381  if (eventWord != "Event:") {
382  return c;
383  }
384  ostringstream os;
385  os << run << "/" << event;
386  return os.str();
387  }
388 
389  void
391  string const& s,
392  bool const nl,
393  bool const preambleMode)
394  {
395  string const indent(6, ' ');
396  if (s.empty()) {
397  if (nl) {
398  os << '\n';
399  charsOnLine_ = 0UL;
400  }
401  return;
402  }
403  if (!preambleMode) {
404  os << s;
405  return;
406  }
407  char const first = s[0];
408  char const second = (s.length() < 2) ? '\0' : s[1];
409  char const last = (s.length() < 2) ? '\0' : s[s.length() - 1];
410  char const last2 = (s.length() < 3) ? '\0' : s[s.length() - 2];
411  // checking -2 because the very last char is sometimes a ' '
412  // inserted by ErrorLog::operator<<
413  // Accounts for newline @ the beginning of the string
414  if (first == '\n' || (charsOnLine_ + s.length()) > format_.lineLength) {
415  charsOnLine_ = 0UL;
416  if (second != ' ') {
417  os << ' ';
418  ++charsOnLine_;
419  }
420  if (first == '\n') {
421  os << s.substr(1);
422  } else {
423  os << s;
424  }
425  } else {
426  os << s;
427  }
428  if ((last == '\n') || (last2 == '\n')) {
429  os << indent;
430  if (last != ' ') {
431  os << ' ';
432  }
433  charsOnLine_ = indent.length() + 1UL;
434  }
435  if (nl) {
436  os << '\n';
437  charsOnLine_ = 0UL;
438  } else {
439  charsOnLine_ += s.length();
440  }
441  }
442 
443  void
444  ELdestination::fillPrefix(ostringstream& oss, ErrorObj const& msg)
445  {
446  if (msg.is_verbatim()) {
447  return;
448  }
449  auto const& xid = msg.xid();
450  charsOnLine_ = 0UL;
451  emitToken(oss, "%MSG"s, false, true);
452  emitToken(oss, xid.severity().getSymbol(), false, true);
453  emitToken(oss, " ", false, true);
454  emitToken(oss, xid.id(), false, true);
455  emitToken(oss, msg.idOverflow(), false, true);
456  emitToken(oss, ": ", false, true);
457  if (format_.want(SERIAL)) {
458  ostringstream s;
459  s << msg.serial();
460  emitToken(oss, "[serial #" + s.str() + "] ", false, true);
461  }
462  bool needAspace = true;
464  if (xid.module().length() + xid.subroutine().length() > 0) {
465  emitToken(oss, "\n", false, true);
466  needAspace = false;
467  } else if (format_.want(TIMESTAMP) && !format_.want(TIME_SEPARATE)) {
468  emitToken(oss, "\n", false, true);
469  needAspace = false;
470  }
471  }
472  if (format_.want(MODULE) && (xid.module().length() > 0)) {
473  if (needAspace) {
474  emitToken(oss, " ", false, true);
475  needAspace = false;
476  }
477  emitToken(oss, xid.module() + " ", false, true);
478  }
479  if (format_.want(SUBROUTINE) && (xid.subroutine().length() > 0)) {
480  if (needAspace) {
481  emitToken(oss, " ", false, true);
482  needAspace = false;
483  }
484  emitToken(oss, xid.subroutine() + "() ", false, true);
485  }
486  if (format_.want(TIMESTAMP)) {
487  if (format_.want(TIME_SEPARATE)) {
488  emitToken(oss, "\n", false, true);
489  needAspace = false;
490  }
491  if (needAspace) {
492  emitToken(oss, " ", false, true);
493  needAspace = false;
494  }
495  emitToken(oss, format_.timestamp(msg.timestamp()) + " ", false, true);
496  }
497  if (format_.want(SOME_CONTEXT)) {
498  if (needAspace) {
499  emitToken(oss, " ", false, true);
500  needAspace = false;
501  }
502  emitToken(oss, msg.context(), false, true);
503  }
504  }
505 
506  void
507  ELdestination::fillUsrMsg(ostringstream& oss, ErrorObj const& msg)
508  {
509  if (!format_.want(TEXT)) {
510  return;
511  }
512  // Determine if file and line should be included
513  if (!msg.is_verbatim()) {
514  if (!msg.filename().empty()) {
515  emitToken(oss, " "s);
516  emitToken(oss, msg.filename());
517  emitToken(oss, ":"s);
518  emitToken(oss, to_string(msg.lineNumber()));
519  }
520  // Check for user-requested line breaks
521  if (format_.want(NO_LINE_BREAKS)) {
522  emitToken(oss, " ==> ");
523  } else {
524  emitToken(oss, "", true);
525  }
526  }
527  // Print the contents.
528  for (auto const& val : msg.items()) {
529  emitToken(oss, val);
530  }
531  }
532 
533  void
534  ELdestination::fillSuffix(ostringstream& oss, ErrorObj const& msg)
535  {
536  if (!msg.is_verbatim() && !format_.want(NO_LINE_BREAKS)) {
537  emitToken(oss, "\n%MSG");
538  }
539  oss << '\n';
540  }
541 
542  void
543  ELdestination::routePayload(ostringstream const&, ErrorObj const&)
544  {}
545 
546  void
548  {
549  if (msg.xid().severity() < threshold_) {
550  if (outputStatistics_) {
551  statsMap_[msg.xid()].add(summarizeContext(msg.context()), false);
552  updatedStats_ = true;
553  }
554  return;
555  }
556  if (skipMsg(msg.xid())) {
557  if (outputStatistics_) {
558  statsMap_[msg.xid()].add(summarizeContext(msg.context()), false);
559  updatedStats_ = true;
560  }
561  return;
562  }
563  msg.setReactedTo(true);
564  ostringstream payload;
565  fillPrefix(payload, msg);
566  fillUsrMsg(payload, msg);
567  fillSuffix(payload, msg);
568  routePayload(payload, msg);
569  if (outputStatistics_) {
570  statsMap_[msg.xid()].add(summarizeContext(msg.context()), true);
571  updatedStats_ = true;
572  }
573  }
574 
575  string
577  {
578  ostringstream s;
579  int n{};
580  // ----- Summary part I:
581  //
582  bool ftnote{false};
583  struct part3 {
584  long n{}, t{};
586  for (auto const& [xid, count] : statsMap_) {
587  auto const& cat = xid.id();
588  // ----- Emit new part I header, if needed:
589  //
590  if (n == 0) {
591  s << "\n";
592  s << " type category sev module subroutine "
593  " count total\n"
594  << " ---- -------------------- -- ---------------- "
595  "---------------- ----- -----\n";
596  }
597  // ----- Emit detailed message information:
598  //
599  s
600  // Type
601  << right << setw(5) << ++n
602  << ' '
603  // Category
604  << left << setw(20) << cat.substr(0, 20)
605  << ' '
606  // Severity
607  << left << setw(2) << xid.severity().getSymbol()
608  << ' '
609  // Module
610  << left << setw(16) << xid.module().substr(0, 16)
611  << ' '
612  // Subroutine
613  << left << setw(16)
614  << xid.subroutine().substr(0, 16)
615  // Count
616  << right << setw(7) << count.n_ << left << setw(1)
617  << (count.ignoredFlag_ ? '*' : ' ')
618  // Total Count
619  << right << setw(8) << count.aggregateN_ << '\n';
620  ftnote = ftnote || count.ignoredFlag_;
621  // ----- Obtain information for Part III, below:
622  //
623  p3[xid.severity().getLevel()].n += count.n_;
624  p3[xid.severity().getLevel()].t += count.aggregateN_;
625  }
626  // ----- Provide footnote to part I, if needed:
627  //
628  if (ftnote) {
629  s << "\n* Some occurrences of this message were suppressed in all "
630  "logs, due to limits.\n";
631  }
632  // ----- Summary part II:
633  //
634  n = 0;
635  for (auto const& pr : statsMap_) {
636  auto const& xid = pr.first;
637  auto const& count = pr.second;
638  string const& cat = xid.id();
639  if (n == 0) {
640  s << '\n'
641  << " type category Examples: run/evt run/evt "
642  "run/evt\n"
643  << " ---- -------------------- ---------------- ---------------- "
644  "----------------\n";
645  }
646  s
647  // Type
648  << right << setw(5) << ++n
649  << ' '
650  // Category
651  << left << setw(20) << cat
652  << ' '
653  // Context1
654  << left << setw(16) << count.context1_.c_str()
655  << ' '
656  // Context2
657  << left << setw(16) << count.context2_.c_str()
658  << ' '
659  // ContextLast
660  << count.contextLast_ << '\n';
661  }
662  // ----- Summary part III:
663  //
664  s << '\n'
665  << "Severity # Occurrences Total Occurrences\n"
666  << "-------- ------------- -----------------\n";
667  for (int k = 0; k < ELseverityLevel::nLevels; ++k) {
668  if (p3[k].n != 0 || p3[k].t != 0) {
669  s
670  // Severity
671  << left << setw(8)
672  << ELseverityLevel{ELseverityLevel::ELsev_(k)}.getName().c_str()
673  // Occurrences
674  << right << setw(17)
675  << p3[k].n
676  // Total Occurrences
677  << right << setw(20) << p3[k].t << '\n';
678  }
679  }
680  return s.str();
681  }
682 
683  // Called only by MessageLoggerScribe::summarize()
684  // Called only by MessageLogger::LogStatistics()
685  void
687  {
689  ostringstream payload;
690  payload << "\n=============================================\n\n"
691  << "MessageLogger Summary\n"
692  << formSummary();
693  routePayload(payload, ErrorObj{ELzeroSeverity, "No ostream"s});
694  }
695  updatedStats_ = false;
696  if (reset_) {
698  resetLimiters();
699  }
700  }
701 
702  void
704  {}
705 
706  // Used only by MessageLoggerScribe to disable
707  // cerr_early once startup is complete.
708  void
710  {
711  threshold_ = sv;
712  }
713 
714  void
716  {
717  ErrorObj msg{ELwarning, "ELdestination"s};
718  msg << "Call to unimplemented flush()!";
719  log(msg);
720  }
721 
722  // A return value of true tells the destination
723  // to not log this message.
724  // Called by ELdestination log().
725  bool
727  {
728  auto xl_iter = xidLimiters_.find(xid);
729  if (xl_iter == xidLimiters_.end()) {
730  // Need to create and initialize a limiter for this xid.
731  int limit{defaultLimit_};
732  int reportEvery{defaultReportEvery_};
733  int timespan{defaultTimespan_};
734  // Does the category of this xid have explicit limits?
735  auto cp_iter = categoryParams_.find(xid.id());
736  if (cp_iter != categoryParams_.end()) {
737  // Category limits found, use them.
738  auto const& cp = cp_iter->second;
739  limit = (cp.limit_ < 0) ? defaultLimit_ : cp.limit_;
740  reportEvery =
741  (cp.reportEvery_ < 0) ? defaultReportEvery_ : cp.reportEvery_;
742  timespan = (cp.timespan_ < 0) ? defaultTimespan_ : cp.timespan_;
743  }
744  XidLimiter& limiter = xidLimiters_[xid];
745  limiter.previousTimestamp_ = time(0);
746  limiter.msgCount_ = 0;
747  limiter.skippedMsgCount_ =
748  reportEvery - 1; // So that the FIRST of the prescaled messages emerges
749  limiter.limit_ = limit;
750  limiter.reportEvery_ = reportEvery;
751  limiter.timespan_ = timespan;
752  xl_iter = xidLimiters_.find(xid);
753  }
754  XidLimiter& limiter = xl_iter->second;
755  time_t now = time(0);
756  // Has it been so long that we should restart counting toward the limit?
757  if ((limiter.timespan_ >= 0) &&
758  (difftime(now, limiter.previousTimestamp_) >= limiter.timespan_)) {
759  limiter.msgCount_ = 0;
760  if (limiter.reportEvery_ > 0) {
761  // So this message will be reacted to
762  limiter.skippedMsgCount_ = limiter.reportEvery_ - 1;
763  } else {
764  limiter.skippedMsgCount_ = 0;
765  }
766  }
767  limiter.previousTimestamp_ = now;
768  ++limiter.msgCount_;
769  ++limiter.skippedMsgCount_;
770  if (limiter.skippedMsgCount_ < limiter.reportEvery_) {
771  // Skip message.
772  return true;
773  }
774  if (limiter.limit_ == 0) {
775  // Zero limit - never react to this
776  // Skip message.
777  return true;
778  }
779  if ((limiter.limit_ < 0) || (limiter.msgCount_ <= limiter.limit_)) {
780  limiter.skippedMsgCount_ = 0;
781  // Accept message.
782  return false;
783  }
784  // Now we are over the limit - have we exceeded limit by 2^N * limit?
785  long diff = limiter.msgCount_ - limiter.limit_;
786  long r = diff / limiter.limit_;
787  if (r * limiter.limit_ != diff) {
788  // Not a multiple of limit - don't react
789  // Skip message.
790  return true;
791  }
792  if (r == 1) {
793  // Exactly twice limit - react
794  limiter.skippedMsgCount_ = 0;
795  // Accept message.
796  return false;
797  }
798  while (r > 1) {
799  if ((r & 1) != 0) {
800  // Not 2**msgCount_ times limit - don't react
801  // Skip message.
802  return true;
803  }
804  r >>= 1;
805  }
806  // If you never get an odd number till one, r is 2**msgCount_ so react
807  limiter.skippedMsgCount_ = 0;
808  // Accept message.
809  return false;
810  }
811 
812  void
814  {
815  statsMap_.clear();
816  }
817 
818  void
820  {
821  xidLimiters_.clear();
822  }
823 
824 } // namespace mf::service
TRandom r
Definition: spectrum.C:23
int lineNumber() const
Definition: ErrorObj.cc:126
fhicl::Table< MsgFormatSettings::Config > format
ELseverityLevel severity() const
Definition: ELextendedID.cc:33
virtual void fillUsrMsg(std::ostringstream &, mf::ErrorObj const &msg)
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:102
bool skipMsg(ELextendedID const &)
timeval timestamp() const
Definition: ErrorObj.cc:73
fhicl::TableFragment< MsgStatistics::Config > msgStatistics
void add(std::string const &context, bool reactedTo)
void emitToken(std::ostream &os, std::string const &s, bool nl=false, bool preambleMode=false)
STL namespace.
fhicl::OptionalDelegatedParameter categories
virtual void fillSuffix(std::ostringstream &, mf::ErrorObj const &msg)
std::map< std::string const, CategoryParams > categoryParams_
virtual void log(mf::ErrorObj &)
std::list< std::string > const & items() const
Definition: ErrorObj.cc:79
std::string const & context() const
Definition: ErrorObj.cc:98
bool is_verbatim() const
Definition: ErrorObj.cc:91
fhicl::Atom< std::string > threshold
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
fhicl::Atom< unsigned long long > lineLength
std::string const & idOverflow() const
Definition: ErrorObj.cc:67
std::vector< std::string > get_pset_names() const
std::map< ELextendedID const, StatsCount > statsMap_
decltype(auto) constexpr to_string(T &&obj)
ADL-aware version of std::to_string.
std::string summarizeContext(std::string const &)
ELslProxy< ELzeroSeverityGen > constexpr ELzeroSeverity
ELextendedID const & xid() const
Definition: ErrorObj.cc:61
ELslProxy< ELwarningGen > constexpr ELwarning
T get(std::string const &key) const
Definition: ParameterSet.h:314
std::string indent(std::size_t const i)
std::string const & filename() const
Definition: ErrorObj.cc:120
ELdestination(Config const &)
ELseverityLevel threshold_
std::string const & id() const
Definition: ELextendedID.cc:27
double value
Definition: spectrum.C:18
int serial() const
Definition: ErrorObj.cc:55
fhicl::Atom< bool > outputStatistics
constexpr auto const & left(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:94
char const * what() const noexcept override
std::optional< T > get_if_present(std::string const &key) const
Definition: ParameterSet.h:267
virtual void fillPrefix(std::ostringstream &, mf::ErrorObj const &msg)
virtual void setReactedTo(bool)
Definition: ErrorObj.cc:177
MsgFormatSettings format_
Char_t n[5]
void setThreshold(ELseverityLevel sv)
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:69
std::map< ELextendedID const, XidLimiter > xidLimiters_
Float_t e
Definition: plot.C:35
second_as<> second
Type of time stored in seconds, in double precision.
Definition: spacetime.h:82
std::string nl(std::size_t i=1)
virtual void routePayload(std::ostringstream const &, mf::ErrorObj const &msg)
Event finding and building.