Belle II Software development
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
24using namespace Belle2;
25using namespace std;
26
27
28bool 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
62void LogSystem::showText(LogConfig::ELogLevel level, const std::string& txt, int info)
63{
64 LogMessage customText(level, txt, "", "", "", 0);
65 customText.setLogInfo(info);
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};
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 ...
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()) {
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 }
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{
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 }
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
ELogLevel getAbortLevel() const
Returns the configured abort level.
Definition: LogConfig.h:119
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
unsigned int m_maxErrorRepetition
Maximum number to show the same message.
Definition: LogSystem.h:219
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
std::vector< LogConnectionBase * > m_logConnections
Stores the pointers to the log connection objects.
Definition: LogSystem.h:205
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
std::string m_moduleName
The current module name.
Definition: LogSystem.h:211
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.
STL namespace.