MessageLoggerScribe.cc
Go to the documentation of this file.
1 // ----------------------------------------------------------------------
2 // MessageLoggerScribe.cc
3 // ----------------------------------------------------------------------
4 
8 #include "cetlib/trim.h"
21 
22 #include <algorithm>
23 #include <cassert>
24 #include <iostream>
25 
26 using namespace std::string_literals;
27 using std::make_unique;
28 using std::string;
29 using vstring = std::vector<std::string>;
30 
31 namespace {
32 
33  bool constexpr throw_on_clean_slate [[gnu::unused]] {true};
34  bool constexpr no_throw_on_clean_slate [[gnu::unused]] {false};
35 
36  auto
37  default_statistics_config(fhicl::ParameterSet const& ordinaryDests)
38  {
39  // Provide default statistics destinations but only if there is
40  // also no list of ordinary destinations. (If a configuration
41  // specifies destinations, and no statistics, assume that is
42  // what the user wants.)
43 
44  fhicl::ParameterSet result;
45  if (ordinaryDests.is_empty()) {
46  std::string const config{"file_stats: {"
47  " type: file"
48  " filename: \"err.log\""
49  " threshold: WARNING"
50  "}"};
52  }
53  return result;
54  }
55 }
56 
57 namespace mf {
58  namespace service {
59 
60  MessageLoggerScribe::MessageLoggerScribe(
61  std::string const& applicationName) try : admin_ {
62  applicationName
63  }
64  , earlyDest_{admin_.attach("cerr_early",
66  "cerr",
67  "cerr_early",
69  {}
71  {
72  std::string msg{"\nConfiguration error for destination: " +
73  detail::bold_fontify("cerr_early") + "\n\n"};
74  msg += e.what();
75  throw Exception(errors::Configuration) << msg;
76  }
77 
78  //=============================================================================
80  {
81  // If there are any waiting message, finish them off
82  ErrorObj* errorobj_p = nullptr;
83  while (waitingMessages_.try_pop(errorobj_p)) {
84  if (!purgeMode_) {
85  for (auto const& cat : parseCategories(errorobj_p->xid().id())) {
86  errorobj_p->setID(cat);
87  admin_.log(*errorobj_p); // route the message text
88  }
89  }
90  delete errorobj_p;
91  }
92  admin_.finish();
93  }
94 
95  //=============================================================================
96  void
97  MessageLoggerScribe::runCommand(OpCode const opcode, void* operand)
98  {
99  switch (opcode) {
100  default: {
101  assert(false); // can't happen (we certainly hope!)
102  break;
103  }
104  case END_THREAD: {
105  break;
106  }
107  case LOG_A_MESSAGE: {
108  auto errorobj_p = static_cast<ErrorObj*>(operand);
109  try {
110  if (active_ && !purgeMode_) {
111  log(errorobj_p);
112  }
113  }
114  catch (cet::exception const& e) {
115  ++count_;
116  std::cerr << "MessageLoggerScribe caught " << count_
117  << " cet::exceptions, text = \n"
118  << e.what() << "\n";
119 
120  if (count_ > 25) {
121  std::cerr << "MessageLogger will no longer be processing "
122  << "messages due to errors (entering purge mode).\n";
123  purgeMode_ = true;
124  }
125  }
126  catch (...) {
127  std::cerr << "MessageLoggerScribe caught an unknown exception and "
128  << "will no longer be processing "
129  << "messages. (entering purge mode)\n";
130  purgeMode_ = true;
131  }
132  break;
133  }
134  case CONFIGURE: {
135  auto config = std::unique_ptr<MessageLoggerQ::Config>(
136  static_cast<MessageLoggerQ::Config*>(operand));
137  configure_errorlog(std::move(config));
138  break;
139  }
140  case SUMMARIZE: {
141  assert(operand == nullptr);
142  try {
144  }
145  catch (cet::exception const& e) {
146  std::cerr << "MessageLoggerScribe caught exception "
147  << "during summarize:\n"
148  << e.what() << "\n";
149  }
150  catch (...) {
151  std::cerr << "MessageLoggerScribe caught unkonwn exception type "
152  << "during summarize. (Ignored)\n";
153  }
154  break;
155  }
156  case SHUT_UP: {
157  assert(operand == nullptr);
158  active_ = false;
159  break;
160  }
161  case FLUSH_LOG_Q: {
162  break;
163  }
164  } // switch
165 
166  } // MessageLoggerScribe::runCommand(opcode, operand)
167 
168  void
170  {
171  admin_.setApplication(application);
172  }
173 
174  void
176  {
177  admin_.hostname_ = hostName;
178  }
179 
180  void
182  {
183  admin_.hostaddr_ = hostAddr;
184  }
185 
186  void
188  {
189  admin_.pid_ = pid;
190  }
191 
192  //=============================================================================
193  void
195  {
196  bool expected = false;
197  std::unique_ptr<ErrorObj> obj(errorobj_p);
198  if (messageBeingSent_.compare_exchange_strong(expected, true)) {
199  // Process the current message.
200  for (auto const& cat : parseCategories(errorobj_p->xid().id())) {
201  errorobj_p->setID(cat);
202  admin_.log(*errorobj_p); // route the message text
203  }
204  // process any waiting messages
205  errorobj_p = nullptr;
206  while (!purgeMode_ && waitingMessages_.try_pop(errorobj_p)) {
207  obj.reset(errorobj_p);
208  for (auto const& cat : parseCategories(errorobj_p->xid().id())) {
209  errorobj_p->setID(cat);
210  admin_.log(*errorobj_p); // route the message text
211  }
212  }
213  messageBeingSent_.store(false);
214  } else {
215  obj.release();
216  waitingMessages_.push(errorobj_p);
217  }
218  }
219 
220  //=============================================================================
221  void
223  std::unique_ptr<MessageLoggerQ::Config>&& config)
224  {
225  if (admin_.destinations().size() > 1) {
226  LogWarning("multiLogConfig")
227  << "The message logger has been configured multiple times";
228  cleanSlateConfiguration_ = false;
229  }
230 
231  fetchDestinations(std::move(config));
232  } // MessageLoggerScribe::configure_errorlog()
233 
234  //=============================================================================
235  void
237  std::unique_ptr<MessageLoggerQ::Config> config)
238  {
239  fhicl::ParameterSet dest_psets;
240  fhicl::ParameterSet ordinaryDests;
241  if (!config->destinations.get_if_present(dest_psets)) {
242  dest_psets = default_destinations_config();
243  }
244  ordinaryDests = dest_psets;
245  ordinaryDests.erase("statistics");
246 
247  // Dial down the early destination once the ordinary
248  // destinations are filled.
250 
251  auto statDests = dest_psets.get<fhicl::ParameterSet>(
252  "statistics", default_statistics_config(ordinaryDests));
253 
254  // Initialize universal suppression variables
258 
261  }
262 
263  //=============================================================================
264  void
266  fhicl::ParameterSet const& dests,
268  {
269  std::set<std::string> ids;
270 
271  std::vector<std::string> config_errors;
272  for (auto const& psetname : dests.get_pset_names()) {
273 
274  // Retrieve the destination pset object.
275  auto dest_pset = dests.get<fhicl::ParameterSet>(psetname);
276 
277  // If the provided parameter set is empty, replace its
278  // configuration with the default one.
279  if (dest_pset.is_empty()) {
280  dest_pset = default_destination_config();
281  }
282 
283  // Grab the destination type and filename.
284  // FIXME: This should only be part of the configuration
285  // validation!
286  std::string dest_type{};
287  if (!dest_pset.get_if_present("type", dest_type)) {
289  << "No 'type' specified for destination '" << psetname << "'.\n";
290  }
291  ELdestConfig::checkType(dest_type, configuration);
292 
293  bool const throw_on_duplicate_id =
294  (configuration == ELdestConfig::STATISTICS);
295  std::string const& outputId =
296  createId(ids, dest_type, psetname, dest_pset, throw_on_duplicate_id);
297 
298  // Use previously defined configuration of duplicated destination
300  outputId, configuration, no_throw_on_clean_slate))
301  continue;
302 
303  std::string const& libspec = dest_type;
304  auto& plugin_factory = configuration == ELdestConfig::STATISTICS ?
307 
308  // attach the current destination, keeping a control handle to it:
309  try {
311  outputId,
312  makePlugin_(plugin_factory, libspec, psetname, dest_pset));
313 
314  // Suppress the desire to do an extra termination summary just because
315  // of end-of-job info message for statistics jobs
316  if (configuration == ELdestConfig::STATISTICS)
317  dest.noTerminationSummary();
318  }
319  catch (fhicl::detail::validationException const& e) {
320  std::string msg{"Configuration error for destination: " +
321  detail::bold_fontify(psetname) + "\n\n"};
322  msg += e.what();
323  config_errors.push_back(std::move(msg));
324  }
325  }
326 
327  if (!config_errors.empty()) {
328  std::string msg{"\nThe following messagefacility destinations have "
329  "configuration errors:\n\n"};
330  constexpr cet::HorizontalRule rule{60};
331  msg += rule('=');
332  msg += "\n\n";
333  auto start = cbegin(config_errors);
334  msg += *start;
335  ++start;
336  for (auto it = start, e = cend(config_errors); it != e; ++it) {
337  msg += rule('-');
338  msg += "\n\n";
339  msg += *it;
340  }
341  msg += rule('=');
342  msg += "\n\n";
343  throw Exception(errors::Configuration) << msg;
344  }
345  } // make_destinations()
346 
347  //=============================================================================
348  vstring
350  {
351  vstring cats;
352  using namespace std::string_literals;
353  // Note: This algorithm assigns, as desired, one null category if it
354  // encounters an empty categories string
355 
356  auto const npos = s.length();
357  decltype(s.length()) i{};
358 
359  while (i <= npos) {
360 
361  if (i == npos) {
362  cats.push_back(std::string());
363  return cats;
364  }
365 
366  auto const j = s.find('|', i);
367  std::string cat = cet::trim_copy(s.substr(i, j - i), " \t\n"s);
368  cats.push_back(cat);
369  i = j;
370  while ((i < npos) && (s[i] == '|'))
371  ++i;
372  // the above handles cases of || and also | at end of string
373  }
374  return cats;
375  }
376 
377  //=============================================================================
378  void
380  {
381  for (auto& idDestPair : admin_.destinations()) {
382  auto& dest = *idDestPair.second;
383  dest.summary();
384  if (dest.resetStats())
385  dest.wipe();
386  }
387  }
388 
389  //=============================================================================
391  MessageLoggerScribe::createId(std::set<std::string>& existing_ids,
392  std::string const& type,
393  std::string const& file_name,
394  fhicl::ParameterSet const& pset,
395  bool const should_throw)
396  {
397  std::string output_id{type};
398  if (!cet::search_all(std::vector<std::string>{"cout", "cerr", "syslog"},
399  type)) {
400  output_id += ":" + pset.get<std::string>("filename", file_name);
401  }
402 
403  // Emplace and check that output_id doesn't already exist
404  if (!existing_ids.emplace(output_id).second) {
405  // Current usage case is to NOT throw for ordinary
406  // destinations, but to throw for statistics destinations.
407  if (should_throw) {
409  << "\n"
410  << " Output identifier: \"" << output_id << "\""
411  << " already specified within ordinary/statistics block in FHiCL "
412  "file"
413  << "\n";
414  }
415  }
416 
417  return output_id;
418  }
419 
420  //=============================================================================
421  bool
423  std::string const& output_id,
425  bool const should_throw)
426  {
427  std::string config_str;
428  switch (configuration) {
430  config_str = "MessageLogger";
431  break;
433  config_str = "MessageLogger Statistics";
434  break;
435  }
436 
437  auto dest_pr = admin_.destinations().find(output_id);
438  if (dest_pr == admin_.destinations().end())
439  return false;
440 
441  // For duplicate destinations
442  std::string const hrule{"\n=============================================="
443  "============================== \n"};
444  std::ostringstream except_msg, orig_config_msg;
445  except_msg << hrule << "\n Duplicate name for a " << config_str
446  << " destination: \"" << output_id << "\"";
447  orig_config_msg
448  << "\n Only original configuration instructions are used. \n"
449  << hrule;
450 
451  //------------------------------------------------------------------
452  // Handle statistics case where duplicate destinations are okay
453  //------------------------------------------------------------------
454 
455  // If user specifies the destination twice and the configuration
456  // is STATISTICS, then the ELdestination.userWantsStats_ flag
457  // needs to be set to 'true' so that statistics are output.
458 
459  if (configuration == ELdestConfig::STATISTICS) {
460  dest_pr->second->userWantsStats();
461  return true; // don't emit warning for statistics
462  }
463 
464  // Emit error message for everything else
466 
467  if (should_throw) {
469  << except_msg.str();
470  } else
471  LogError("duplicateDestination")
472  << except_msg.str() << orig_config_msg.str();
473 
474  } else { // !cleanSlateConfiguration_
475  LogWarning("duplicateDestination")
476  << except_msg.str() << orig_config_msg.str();
477  }
478 
479  return true;
480  }
481 
482  //=============================================================================
483  std::unique_ptr<ELdestination>
485  std::string const& libspec,
486  std::string const& psetname,
487  fhicl::ParameterSet const& pset)
488  {
489  std::unique_ptr<ELdestination> result;
490  try {
491  auto const pluginType = plugin_factory.pluginType(libspec);
493  result = plugin_factory.makePlugin<std::unique_ptr<ELdestination>>(
494  libspec, psetname, pset);
495  } else {
496  throw Exception(errors::Configuration, "MessageLoggerScribe: ")
497  << "unrecognized plugin type " << pluginType << "for plugin "
498  << libspec << ".\n";
499  }
500  }
501  catch (cet::exception const& e) {
502  throw Exception(errors::Configuration, "MessageLoggerScribe: ", e)
503  << "Exception caught while processing plugin spec.\n";
504  }
505  return result;
506  } //
507 
508  } // end of namespace service
509 } // end of namespace mf
MaybeLogger_< ELseverityLevel::ELsev_warning, false, true, detail::ConditionalLogger > LogWarning
Definition: MessageLogger.h:58
ELextendedID const & xid() const
Definition: ErrorObj.cc:44
const char expected[]
Definition: Exception_t.cc:22
std::string string
Definition: nybbler.cc:12
void configure_errorlog(std::unique_ptr< MessageLoggerQ::Config > &&dests_config)
void runCommand(OpCode opcode, void *operand) override
std::string pluginType(std::string const &libspec)
void checkType(std::string const &type, dest_config const configuration)
cet::BasicPluginFactory pluginFactory_
void make_ParameterSet(intermediate_table const &tbl, ParameterSet &ps)
std::enable_if_t< std::is_base_of< ELdestination, DEST >::value, ELdestination & > attach(std::string const &outputId, std::unique_ptr< DEST > &&dest)
std::string trim_copy(std::string source, std::string const &t=" ")
Definition: trim.h:66
virtual void setID(const std::string &ID)
Definition: ErrorObj.cc:103
std::vector< std::string > vstring
ELslProxy< ELhighestSeverityGen > constexpr ELhighestSeverity
list dests
Define destination.
std::string bold_fontify(std::string const &s)
Definition: bold_fontify.h:9
static bool warningAlwaysSuppressed
Definition: MessageDrop.h:77
std::string createId(std::set< std::string > &existing_ids, std::string const &type, std::string const &filename, fhicl::ParameterSet const &pset={}, bool const should_throw=true)
void setApplication(std::string const &application)
cet::coded_exception< errors::error, detail::translate > Exception
Definition: exception.h:16
std::vector< std::string > get_pset_names() const
bool search_all(FwdCont const &, Datum const &)
const double e
destination_collection_t const & destinations() const
T get(std::string const &key) const
Definition: ParameterSet.h:231
std::vector< std::string > parseCategories(std::string const &s)
static bool debugAlwaysSuppressed
Definition: MessageDrop.h:73
MaybeLogger_< ELseverityLevel::ELsev_error, false, true, detail::AlwaysLogger > LogError
Definition: MessageLogger.h:66
static bool infoAlwaysSuppressed
Definition: MessageDrop.h:75
cet::BasicPluginFactory pluginStatsFactory_
std::string const & id() const
Definition: ELextendedID.h:70
void setHostName(std::string const &hostName) override
virtual void noTerminationSummary()
std::unique_ptr< ELdestination > makePlugin_(cet::BasicPluginFactory &pluginFactory, std::string const &libspec, std::string const &psetname, fhicl::ParameterSet const &pset)
bool is_empty() const
Definition: ParameterSet.cc:97
char const * what() const noexcept override
void log(ErrorObj *errorobj_p)
std::enable_if_t<!std::is_function< RESULT_TYPE >::value, RESULT_TYPE > makePlugin(std::string const &libspec, ARGS &&...args)
start
Definition: test.py:4
bool erase(std::string const &key)
void setThreshold(ELseverityLevel sv)
tbb::concurrent_queue< ErrorObj * > waitingMessages_
void setApplication(std::string const &application) override
bool duplicateDestination(std::string const &output_id, ELdestConfig::dest_config const config, bool const should_throw)
static const double s
Definition: Units.h:99
void setHostAddr(std::string const &hostAddr) override
void fetchDestinations(std::unique_ptr< MessageLoggerQ::Config > dests_config)
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
void makeDestinations(fhicl::ParameterSet const &dests, ELdestConfig::dest_config const config)