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