Belle II Software  release-05-02-19
LogSystem.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2010-2018 Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Andreas Moll, Thomas Kuhr, Martin Ritter *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 #include <framework/logging/LogSystem.h>
12 #include <framework/logging/LogMessage.h>
13 #include <framework/logging/LogConnectionBase.h>
14 #include <framework/logging/LogConnectionFilter.h>
15 #include <framework/logging/LogConnectionConsole.h>
16 #include <framework/datastore/DataStore.h>
17 
18 #include <TROOT.h>
19 #include <csignal>
20 
21 #include <cstdio>
22 #include <iostream>
23 #include <unordered_map>
24 #include <functional>
25 
26 using namespace Belle2;
27 using namespace std;
28 
29 
30 bool LogSystem::s_debugEnabled = false;
31 
32 
34 {
35  static LogSystem instance;
36  return instance;
37 }
38 
39 
41 {
42  m_logConnections.push_back(logConnection);
43 }
44 
45 
47 {
48  for (auto connection : m_logConnections) {
49  delete connection;
50  }
51  m_logConnections.clear();
52 }
53 
54 
56 {
57  bool messageSent = false;
58  for (auto con : m_logConnections) {
59  messageSent |= con->sendMessage(message);
60  }
61  return messageSent;
62 }
63 
64 void LogSystem::showText(LogConfig::ELogLevel level, const std::string& txt, int info)
65 {
66  LogMessage customText(level, txt, "", "", "", 0);
67  customText.setLogInfo(info);
68  deliverMessageToConnections(customText);
69 }
70 
72 {
73  LogConfig::ELogLevel logLevel = message.getLogLevel();
74  auto packageLogConfig = m_packageLogConfigs.find(message.getPackage());
75  if ((packageLogConfig != m_packageLogConfigs.end()) && packageLogConfig->second.getLogInfo(logLevel)) {
76  message.setLogInfo(packageLogConfig->second.getLogInfo(logLevel));
77  } else if (m_moduleLogConfig && m_moduleLogConfig->getLogInfo(logLevel)) {
78  message.setLogInfo(m_moduleLogConfig->getLogInfo(logLevel));
79  } else {
80  message.setLogInfo(m_logConfig.getLogInfo(logLevel));
81  }
82 
83  message.setModule(m_moduleName);
84 
85  // We want to count it whether we've seen it or not
86  incMessageCounter(logLevel);
87 
88  // add message to list of message or increase repetition value
89  bool lastTime(false);
90  if (m_printErrorSummary || m_maxErrorRepetition > 0) {
91  unsigned int repetition{0};
92  if (m_messageLog.size() >= c_errorSummaryMaxLines) {
93  // we already have maximum size of the error log so don't add more messages.
94  // but we might want to increase the counter if it already exists
95  /* cppcheck-suppress stlIfFind */
96  if (auto it = m_messageLog.find(message); it != m_messageLog.end()) {
97  repetition = ++(it->second);
98  }
99  } else if (logLevel >= LogConfig::c_Warning or m_maxErrorRepetition > 0) {
100  // otherwise we only keep warnings or higher unless we suppress repetitions,
101  // then we keep everything
102  repetition = ++m_messageLog[message];
103  }
104  lastTime = m_maxErrorRepetition > 0 and repetition == m_maxErrorRepetition;
105  if (m_maxErrorRepetition > 0 and repetition > m_maxErrorRepetition) {
106  // We've seen this message more than once before so it cannot be an abort
107  // level message. So we can just not do anything here except counting ...
108  ++m_suppressedMessages;
109  // However we should warn from time to time that messages are being
110  // suppressed ... but not too often otherwise we don't gain anything so
111  // let's warn first each 100, then each 1000 suppressed messages.
112  if ((m_suppressedMessages < 1000 and m_suppressedMessages % 100 == 0) or
113  (m_suppressedMessages < 10000 and m_suppressedMessages % 1000 == 0) or
114  (m_suppressedMessages % 10000 == 0)) {
115  showText(LogConfig::c_Warning, "... " + std::to_string(m_suppressedMessages) + " log messages suppressed due to repetition ...");
116  }
117  return true;
118  }
119  }
120 
121  // Ok we want to see the message
122  bool messageSent = deliverMessageToConnections(message);
123 
124  if (logLevel >= m_logConfig.getAbortLevel()) {
125  printErrorSummary();
126  // make sure loc connections are finalized to not loose output
127  for (auto connection : m_logConnections) {
128  connection->finalizeOnAbort();
129  }
130  DataStore::Instance().reset(); // ensure we are executed before ROOT's exit handlers
131 
132  //in good tradition, ROOT signal handlers are unsafe.
133  //this avoids a problem seen in ROOT's atexit() handler, which might crash
134  //and then deadlock the process in the SIGSEGV handler...
135  signal(SIGSEGV, nullptr);
136 
137  //ROOT will also try to do this, but doesn't get the order right.
138  gROOT->CloseFiles();
139  exit(1);
140  }
141 
142  // And if it is the last time we show it let's add a trailer for good measure
143  if (messageSent and lastTime) {
144  showText(max(message.getLogLevel(), LogConfig::c_Warning),
145  "The previous message has occurred " + std::to_string(m_maxErrorRepetition) +
146  " times and will be suppressed in future",
148  }
149 
150  return messageSent;
151 }
152 
153 
155 {
156  for (int& i : m_messageCounter) {
157  i = 0;
158  }
159  m_suppressedMessages = 0;
160  m_messageLog.clear();
161  m_messageLog.reserve(100);
162 }
163 
164 
166 {
167  return m_messageCounter[logLevel];
168 }
169 
170 
171 //============================================================================
172 // Private methods
173 //============================================================================
174 
176  m_logConfig(LogConfig::c_Info),
177  m_moduleLogConfig(nullptr),
178  m_printErrorSummary(false),
179  m_messageCounter{0}
180 {
181  resetLogging();
182 }
183 
185 {
188  m_moduleLogConfig = nullptr;
189  m_packageLogConfigs.clear();
190  constexpr unsigned int logInfo = LogConfig::c_Level + LogConfig::c_Message;
191  constexpr unsigned int warnLogInfo = LogConfig::c_Level + LogConfig::c_Message + LogConfig::c_Module;
192  constexpr unsigned int debugLogInfo = LogConfig::c_Level + LogConfig::c_Message + LogConfig::c_Module + LogConfig::c_File +
194  constexpr unsigned int fatalLogInfo = LogConfig::c_Level + LogConfig::c_Message + LogConfig::c_Module + LogConfig::c_Function;
201 
205 
206  m_printErrorSummary = false;
207  s_debugEnabled = false;
208 }
209 
211 {
212  if (!m_printErrorSummary)
213  return; //disabled
214 
215  int numLogWarn = getMessageCounter(LogConfig::c_Warning);
216  int numLogError = getMessageCounter(LogConfig::c_Error);
217 
218  // ok, only errors and warnings in the error summary
219  std::vector<std::pair<LogMessage, unsigned int>> messages;
220  for (auto && value : m_messageLog) {
221  if (value.first.getLogLevel() < LogConfig::c_Warning) continue;
222  messages.emplace_back(std::move(value));
223  }
224  // but show them sorted by severity and occurrence
225  std::stable_sort(messages.begin(), messages.end(), [](const auto & a, const auto & b) {
226  return a.first.getLogLevel() > b.first.getLogLevel() or
227  (a.first.getLogLevel() == b.first.getLogLevel() and a.second > b.second);
228  });
229  if (messages.size() == 0) {
230  return; //nothing to do
231  }
232 
233  // save configuration
234  const LogConfig oldConfig = m_logConfig;
235  // and make sure module configuration is bypassed, otherwise changing the settings in m_logConfig would be ignored
236  const LogConfig* oldModuleConfig {nullptr};
237  std::swap(m_moduleLogConfig, oldModuleConfig);
238  // similar for package configuration
239  map<string, LogConfig> oldPackageConfig;
240  std::swap(m_packageLogConfigs, oldPackageConfig);
241  // prevent calling printErrorSummary() again when printing
243 
244  // only show level & message
247  logInfo |= LogConfig::c_Module;
252 
253  // and then show all the messages
254  showText(LogConfig::c_Info, "===Error Summary================================================================",
256  for (auto & [msg, count] : messages) {
257  const bool multiple = count > 1;
258  // don't show variables if the message appeared more than once, could have different values/variables
259  msg.setLogInfo(m_logConfig.getLogInfo(msg.getLogLevel()) | (multiple ? LogConfig::c_NoVariables : 0));
260  // and directly print the message as it was saved.
262  // and also tell us the count
263  if (multiple) {
264  showText(LogConfig::c_Info, " (last message occurred " + std::to_string(count) + " times)");
265  }
266  }
267  showText(LogConfig::c_Info, "================================================================================",
269  if (numLogError) {
270  showText(LogConfig::c_Error, "in total, " + std::to_string(numLogError) + " errors occurred during processing",
272  }
273  if (numLogWarn) {
274  showText(LogConfig::c_Warning, "in total, " + std::to_string(numLogWarn) + " warnings occurred during processing",
276  }
277  if (m_messageLog.size() == c_errorSummaryMaxLines) {
278  showText(LogConfig::c_Warning, "Note: The error summary was truncated to " +
279  std::to_string(c_errorSummaryMaxLines) + " (distinct) messages",
281  }
282  if (m_suppressedMessages) {
283  showText(LogConfig::c_Warning, std::to_string(m_suppressedMessages) + " log messages were suppressed",
285  }
286 
287  // and then clear the log
288  m_messageLog.clear();
289 
290  // restore old configuration
291  m_logConfig = oldConfig;
292  std::swap(m_moduleLogConfig, oldModuleConfig);
293  std::swap(m_packageLogConfigs, oldPackageConfig);
294 }
295 
297 {
299 }
300 
302 {
303  m_messageCounter[logLevel]++;
304 }
Belle2::LogConfig::getLogInfo
unsigned int getLogInfo(ELogLevel logLevel) const
Returns the configured log information for the given level.
Definition: LogConfig.h:130
Belle2::LogConfig::c_Result
@ c_Result
Result: for informational summary messages, e.g.
Definition: LogConfig.h:38
Belle2::LogSystem::m_suppressedMessages
unsigned int m_suppressedMessages
The amount of messages we have suppressed so far just to get an indication we print this from time to...
Definition: LogSystem.h:231
Belle2::DataStore::Instance
static DataStore & Instance()
Instance of singleton Store.
Definition: DataStore.cc:54
Belle2::LogConfig::c_Fatal
@ c_Fatal
Fatal: for situations were the program execution can not be continued.
Definition: LogConfig.h:41
Belle2::LogSystem::resetLogConnections
void resetLogConnections()
Removes all log connections.
Definition: LogSystem.cc:46
Belle2::LogSystem::resetLogging
void resetLogging()
Reset logging system to defaults: empty all log messages and reset connections to the default.
Definition: LogSystem.cc:184
Belle2::LogConfig::setLogLevel
void setLogLevel(ELogLevel logLevel)
Configure the log level.
Definition: LogConfig.cc:27
Belle2::LogConfig::c_NoVariables
@ c_NoVariables
If set don't output any variables that are part of the message.
Definition: LogConfig.h:54
Belle2::LogSystem::m_messageLog
std::unordered_map< LogMessage, int, LogMessage::TextHasher, LogMessage::TextHasher > m_messageLog
Count of previous log messages for the summary and to suppress repetitive messages.
Definition: LogSystem.h:227
Belle2::LogSystem::m_packageLogConfigs
std::map< std::string, LogConfig > m_packageLogConfigs
Stores the log configuration objects for packages.
Definition: LogSystem.h:223
Belle2::LogSystem::sendMessage
bool sendMessage(LogMessage &&message)
Sends a log message using the log connection object.
Definition: LogSystem.cc:71
Belle2::LogConfig::c_Info
@ c_Info
Info: for informational messages, e.g.
Definition: LogConfig.h:37
Belle2::LogConfig::c_File
@ c_File
Source file in which the message was emitted.
Definition: LogConfig.h:51
Belle2::LogConfig::setAbortLevel
void setAbortLevel(ELogLevel abortLevel)
Configure the abort level.
Definition: LogConfig.h:108
Belle2::LogConfig::ELogLevel
ELogLevel
Definition of the supported log levels.
Definition: LogConfig.h:36
Belle2::LogSystem::resetMessageCounter
void resetMessageCounter()
Resets the message counter and error log by setting all message counts to 0.
Definition: LogSystem.cc:154
Belle2::LogConnectionBase
Abstract base class for the different types of log connections.
Definition: LogConnectionBase.h:31
Belle2::DataStore::reset
void reset(EDurability durability)
Frees memory occupied by data store items and removes all objects from the map.
Definition: DataStore.cc:86
Belle2::LogSystem::m_messageCounter
int m_messageCounter[LogConfig::c_Default]
Counts the number of messages sent per message level.
Definition: LogSystem.h:233
Belle2::LogSystem::m_logConfig
LogConfig m_logConfig
The global log system configuration.
Definition: LogSystem.h:217
Belle2::LogConfig::c_Line
@ c_Line
Line in source file in which the message was emitted.
Definition: LogConfig.h:52
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::LogSystem
Class for logging debug, info and error messages.
Definition: LogSystem.h:56
Belle2::LogConfig::c_Level
@ c_Level
Log level of the message.
Definition: LogConfig.h:46
Belle2::LogConfig::c_Error
@ c_Error
Error: for things that went wrong and have to be fixed.
Definition: LogConfig.h:40
Belle2::LogMessage::setLogInfo
void setLogInfo(unsigned int logInfo)
Configure which information should be printed.
Definition: LogMessage.h:123
Belle2::LogSystem::s_debugEnabled
static bool s_debugEnabled
Global flag for fast checking if debug output is enabled.
Definition: LogSystem.h:235
Belle2::LogSystem::deliverMessageToConnections
bool deliverMessageToConnections(const LogMessage &msg)
Do nothing else than to send the message to all connected connections.
Definition: LogSystem.cc:55
Belle2::LogSystem::Instance
static LogSystem & Instance()
Static method to get a reference to the LogSystem instance.
Definition: LogSystem.cc:33
Belle2::LogConfig::c_Function
@ c_Function
Function in which the message was emitted.
Definition: LogConfig.h:50
Belle2::LogConfig::setLogInfo
void setLogInfo(ELogLevel logLevel, unsigned int logInfo)
Configure the printed log information for the given level.
Definition: LogConfig.h:123
Belle2::LogConfig::c_Message
@ c_Message
Log message text.
Definition: LogConfig.h:47
Belle2::LogSystem::LogSystem
LogSystem()
The constructor is hidden to avoid that someone creates an instance of this class.
Definition: LogSystem.cc:175
Belle2::LogSystem::c_errorSummaryMaxLines
static const unsigned int c_errorSummaryMaxLines
Error log will contain at most this many lines.
Definition: LogSystem.h:58
Belle2::LogSystem::m_printErrorSummary
bool m_printErrorSummary
Wether to re-print errors-warnings encountered during execution at the end.
Definition: LogSystem.h:225
Belle2::LogConnectionConsole
Implements a log connection to an IO Stream.
Definition: LogConnectionConsole.h:34
Belle2::LogConfig::c_Debug
@ c_Debug
Debug: for code development.
Definition: LogConfig.h:36
Belle2::LogConnectionFilter
Implements a log connection that filters repeated messages.
Definition: LogConnectionFilter.h:38
Belle2::LogSystem::showText
void showText(LogConfig::ELogLevel level, const std::string &txt, int info=LogConfig::c_Message)
Send a custom message which looks like a log message but should not be counted as such.
Definition: LogSystem.cc:64
Belle2::LogConfig::c_Warning
@ c_Warning
Warning: for potential problems that the user should pay attention to.
Definition: LogConfig.h:39
Belle2::LogSystem::addLogConnection
void addLogConnection(LogConnectionBase *logConnection)
Adds a log connection object which is used to the send the logging messages.
Definition: LogSystem.cc:40
Belle2::LogConfig::c_Default
@ c_Default
Default: use globally configured log level.
Definition: LogConfig.h:42
Belle2::LogSystem::~LogSystem
~LogSystem()
The LogSystem destructor.
Definition: LogSystem.cc:296
Belle2::LogConfig
The LogConfig class.
Definition: LogConfig.h:32
Belle2::LogSystem::printErrorSummary
void printErrorSummary()
Print error/warning summary at end of execution.
Definition: LogSystem.cc:210
Belle2::LogSystem::m_moduleLogConfig
const LogConfig * m_moduleLogConfig
log config of current module
Definition: LogSystem.h:219
Belle2::LogSystem::getMessageCounter
int getMessageCounter(LogConfig::ELogLevel logLevel) const
Returns the number of logging calls per log level.
Definition: LogSystem.cc:165
Belle2::LogConfig::c_DefaultDebugLevel
static const int c_DefaultDebugLevel
Default debug level.
Definition: LogConfig.h:57
Belle2::LogMessage
The LogMessage class.
Definition: LogMessage.h:39
Belle2::LogSystem::incMessageCounter
void incMessageCounter(LogConfig::ELogLevel logLevel)
Increases the counter for the called message level by one.
Definition: LogSystem.cc:301
Belle2::LogConfig::setDebugLevel
void setDebugLevel(int debugLevel)
Configure the debug messaging level.
Definition: LogConfig.h:94
Belle2::LogConfig::c_Module
@ c_Module
Module in which the message was emitted.
Definition: LogConfig.h:48