ErrorReporting.cpp
Go to the documentation of this file.
1 #include "ErrorReporting.h"
2 
3 #include <iomanip>
4 
5 #include <boost/lockfree/policies.hpp>
6 #include <boost/lockfree/queue.hpp>
7 
10 
12 {
13  std::ostream&
14  operator<<(std::ostream& stream, const Type& rhs)
15  {
16  switch (rhs)
17  {
18  case Type::Bus:
19  stream << "Bus";
20  break;
21  case Type::Slave:
22  stream << "Slave";
23  break;
24  case Type::Device:
25  stream << "Device";
26  break;
27  case Type::General:
28  stream << "General";
29  break;
30  }
31  return stream;
32  }
33 
34  std::ostream&
35  operator<<(std::ostream& stream, const Severity& rhs)
36  {
37  switch (rhs)
38  {
39  case Severity::Info:
40  stream << "INFO";
41  break;
42  case Severity::Debug:
43  stream << "DEBUG";
44  break;
45  case Severity::Warning:
46  stream << "WARNING";
47  break;
48  case Severity::Error:
49  stream << "ERROR";
50  break;
51  case Severity::Fatal:
52  stream << "FATAL";
53  break;
54  }
55  return stream;
56  }
57 
59  {
60  static constexpr size_t queueSize = 100;
61 
62  boost::lockfree::queue<Entry, boost::lockfree::capacity<queueSize>> queue;
63  };
64 
65  Reporting::Reporting(std::size_t maxErrorHistorySize) :
66  pimpl(std::make_unique<QueueImpl>()), m_maxErrorHistorySize(maxErrorHistorySize)
67  {
68  setTag("ethercat::Reporting");
69 
70  m_timestampReportingStart = armarx::rtNow();
71  m_errorProcessingThread = std::thread(&Reporting::errorProcessingLoop, this);
72  }
73 
74  Reporting::~Reporting()
75  {
76  m_errorProcessingRunning.store(false);
77  m_errorProcessingCondition.notify_all();
78  m_errorProcessingThread.join();
79  }
80 
81  Reporting&
83  {
84  static Reporting _instance;
85  return _instance;
86  }
87 
88  Reporter
90  {
91  return Reporter(this);
92  }
93 
94  Reporter&
96  {
97  static Reporter _instance = Reporter(&getErrorReporting());
98  return _instance;
99  }
100 
101  void
103  {
104  error.m_timestamp = armarx::rtNow();
105  bool result = pimpl->queue.push(error);
106  if (not result)
107  {
108  ARMARX_RT_LOGF_WARNING("Logging queue is full!").deactivateSpam(0.1);
109  }
110  m_errorProcessingCondition.notify_all();
111  }
112 
113  void
114  Reporting::setMaxErrorHistorySize(std::size_t maxErrorHistorySize)
115  {
116  m_maxErrorHistorySize = maxErrorHistorySize;
117  }
118 
119  std::optional<std::filesystem::path>
120  Reporting::dumpToFile(std::filesystem::path file, std::uint64_t lastNSeconds)
121  {
122  const auto endTime = armarx::rtNow();
123  IceUtil::Time startTime =
124  endTime - IceUtil::Time::seconds(static_cast<std::int64_t>(lastNSeconds));
125  if (startTime < m_timestampReportingStart)
126  {
127  startTime = m_timestampReportingStart;
128  }
129  const std::string yearString = startTime.toString("%y-%m-%d");
130  const std::string startTimeString = startTime.toString("%H-%M-%S");
131  const std::string endTimeString = endTime.toString("%H-%M-%S");
132 
133  file.replace_filename(std::string(file.stem().c_str()) + "_" + yearString + "__" +
134  startTimeString + "_" + endTimeString);
135  file.replace_extension(".errors");
136 
137  if (std::filesystem::exists(file))
138  {
139  file.replace_filename(file.filename().string() + ".new");
140  }
141 
142  auto history = m_errorHistory;
143  auto it = history.begin();
144  for (const auto& [key, value] : history)
145  {
146  if (key.timestamp < startTime.toMicroSeconds())
147  {
148  it++;
149  }
150  }
151  history.erase(history.begin(), it);
152 
153  if (history.size() > 0)
154  {
155  ARMARX_INFO << history.size() << " errors from " << startTimeString << " to "
156  << endTimeString << " will be saved to file " << file.string();
157  }
158  else
159  {
160  ARMARX_VERBOSE << "No errors happend from " << startTimeString << " to "
161  << endTimeString;
162  return std::nullopt;
163  }
164 
165 
166  if (!std::filesystem::exists(file.parent_path()) &&
167  !std::filesystem::create_directories(file.parent_path()))
168  {
169  throw std::runtime_error{"Could not create directories for '" + file.string() + "'"};
170  }
171 
172  std::ofstream outFile{file.string(), std::ofstream::out};
173  if (!outFile)
174  {
175  throw std::runtime_error{"dumpToFile could not open filestream for '" + file.string() +
176  "'"};
177  }
178 
179  for (const auto& [key, value] : history)
180  {
181 
182  std::string timeStr = value.m_timestamp.toDateTime();
183  timeStr = timeStr.substr(timeStr.find(' ') + 1);
184 
185  outFile << "[" << timeStr << "]";
186  outFile << "[" << value.m_type << "]";
187  outFile << "[" << value.m_severity << "] ";
188  outFile << std::string(value.m_message, value.m_message_size) << "\n";
189  if (value.m_sid.slaveIndex != -1)
190  {
191  outFile << value.m_sid.toString(" ") << "\n";
192  }
193  }
194 
195  return std::move(file);
196  }
197 
198  void
199  Reporting::errorProcessingLoop()
200  {
201  Entry e;
202  auto handleEntries = [&, this]()
203  {
204  while (pimpl->queue.pop(e))
205  {
206  const std::int64_t timestamp = e.m_timestamp.toMicroSeconds();
207  std::uint16_t i = 0;
208 
209  // Check for max size of history
210  while (m_errorHistory.size() >= m_maxErrorHistorySize)
211  {
212  m_errorHistory.erase(m_errorHistory.begin());
213  }
214 
215  // Check if there is already an entry with the same timestamp and find correct subindex
216  if (m_errorHistory.begin()->first.timestamp == timestamp)
217  {
218  i = m_errorHistory.begin()->first.subid + 1;
219  }
220  while (m_errorHistory.count({timestamp, i}))
221  {
222  i++;
223  }
224 
225  m_errorHistory.insert({{timestamp, i}, e});
226 
227  auto logpointer =
228  (loghelper(e.m_metaInfo.file, e.m_metaInfo.line, e.m_metaInfo.function));
229  switch (e.m_severity)
230  {
232  *logpointer->setBacktrace(false)
233  << ::armarx::MessageTypeT::INFO << deactivateSpam(1.f, e.toString())
234  << e.toString();
235  return;
237  *logpointer->setBacktrace(false)
238  << ::armarx::MessageTypeT::DEBUG
239  << deactivateSpam(e.m_deactivate_spam_seconds, e.toString())
240  << e.toString();
241  return;
243  *logpointer->setBacktrace(false)
244  << ::armarx::MessageTypeT::WARN
245  << deactivateSpam(e.m_deactivate_spam_seconds, e.toString())
246  << e.toString();
247  return;
249  *logpointer->setBacktrace(false)
250  << ::armarx::MessageTypeT::ERROR
251  << deactivateSpam(e.m_deactivate_spam_seconds, e.toString())
252  << e.toString();
253  return;
255  *logpointer << ::armarx::MessageTypeT::FATAL
256  << deactivateSpam(e.m_deactivate_spam_seconds, e.toString())
257  << e.toString();
258  return;
259  }
260  }
261  };
262 
263  while (m_errorProcessingRunning.load() == true)
264  {
265  std::unique_lock<std::mutex> lock{m_errorProcessingConditionMutex};
266  m_errorProcessingCondition.wait(
267  lock, [&]() { return !pimpl->queue.empty() || !m_errorProcessingRunning.load(); });
268  handleEntries();
269  }
270 
271  // Also handle leftover entries
272  handleEntries();
273  }
274 
275  Reporter::ReportWrapper
277  {
278  info.m_severity = Severity::Debug;
279  return Reporter::ReportWrapper{info, this};
280  }
281 
282  Reporter::ReportWrapper
284  {
285  info.m_severity = Severity::Info;
286  return Reporter::ReportWrapper{info, this};
287  }
288 
289  Reporter::ReportWrapper
291  {
292  error.m_severity = Severity::Warning;
293  m_errorCount++;
294  return Reporter::ReportWrapper{error, this};
295  }
296 
297  Reporter::ReportWrapper
299  {
300  error.m_severity = Severity::Error;
301  m_errorCount++;
302  return Reporter::ReportWrapper{error, this};
303  }
304 
305  void
307  {
308  error.m_severity = Severity::Fatal;
309  m_errorCount++;
310  m_errorReporting->report(std::move(error));
311  throw std::runtime_error(error.toString());
312  }
313 
314  bool
316  {
317  return m_errorCount > 0;
318  }
319 
320  Reporter::Reporter(Reporting* errorReporting) : m_errorReporting(errorReporting)
321  {
322  }
323 
324  Entry&
326  {
327  m_sid = sid;
328  return *this;
329  }
330 
331  Entry&
333  {
334  m_type = type;
335  return *this;
336  }
337 
338  Entry&
339  Entry::metaInfo(const char* file, int line, const char* function)
340  {
341  snprintf(m_metaInfo.file, sizeof(m_metaInfo.file), "%s", file);
342  m_metaInfo.line = line;
343  snprintf(m_metaInfo.function, sizeof(m_metaInfo.function), "%s", function);
344  return *this;
345  }
346 
347  Entry&
348  Entry::deviceName(const char* deviceName)
349  {
350  snprintf(m_device_name, sizeof(m_device_name), "%s", deviceName);
351  return *this;
352  }
353 
354  std::string
356  {
357  std::stringstream ss;
358  ss << "[" << m_type << "] ";
359  ss << std::string(m_message, m_message_size) << "\n";
360  if (m_sid.slaveIndex != -1)
361  {
362  ss << m_sid.toMinimalString(" ");
363  }
364  if (m_device_name[0] != '\0')
365  {
366  ss << " " << std::left << std::setw(16) << "Name: " << std::string(m_device_name);
367  }
368  if (m_bus_iteration_number != 0)
369  {
370  ss << " " << std::left << std::setw(16)
371  << "Bus Iteration: " << std::to_string(m_bus_iteration_number);
372  }
373  ss << "\n";
374  return ss.str();
375  }
376 
377  Entry&
378  Entry::busIterationNumber(std::uint64_t iteration)
379  {
380  m_bus_iteration_number = iteration;
381  return *this;
382  }
383 
384  Reporter::ReportWrapper::ReportWrapper(Entry& error, Reporter* reporter) :
385  m_error(error), m_reporter(reporter)
386  {
387  }
388 
389  Reporter::ReportWrapper::~ReportWrapper()
390  {
391  m_reporter->m_errorReporting->report(std::move(m_error));
392  }
393 
394  Reporter::ReportWrapper&
396  {
397  m_error.m_deactivate_spam_seconds = std::fmax(0.f, seconds);
398  return *this;
399  }
400 
401 } // namespace armarx::control::ethercat::reporting
armarx::control::ethercat::reporting::Reporting::getGlobalErrorReporter
static Reporter & getGlobalErrorReporter()
Definition: ErrorReporting.cpp:95
RtLogging.h
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:180
armarx::control::ethercat::reporting::Severity::Debug
@ Debug
armarx::control::ethercat::reporting::Reporting::report
void report(Entry &&error)
Definition: ErrorReporting.cpp:102
armarx::control::ethercat::reporting
Definition: ErrorReporting.cpp:11
ARMARX_RT_LOGF_WARNING
#define ARMARX_RT_LOGF_WARNING(...)
Definition: ControlThreadOutputBuffer.h:343
armarx::control::ethercat::reporting::Entry::busIterationNumber
Entry & busIterationNumber(std::uint64_t iteration)
Definition: ErrorReporting.cpp:378
RtTiming.h
armarx::Logging::loghelper
LogSenderPtr loghelper(const char *file, int line, const char *function) const
Definition: Logging.cpp:102
armarx::control::ethercat::reporting::Type::Bus
@ Bus
Bussdfnödf.
armarx::control::ethercat::reporting::Reporter::hasErrors
bool hasErrors() const
Definition: ErrorReporting.cpp:315
armarx::control::ethercat::reporting::Reporting::QueueImpl::queueSize
static constexpr size_t queueSize
Definition: ErrorReporting.cpp:60
armarx::control::ethercat::reporting::Type::General
@ General
armarx::control::ethercat::SlaveIdentifier
The SlaveIdentifier class is a POD-type representing a unique set of values identifying an EtherCAT s...
Definition: SlaveIdentifier.h:53
armarx::control::ethercat::reporting::Reporting::dumpToFile
std::optional< std::filesystem::path > dumpToFile(std::filesystem::path file, std::uint64_t lastNSeconds=1000000)
Definition: ErrorReporting.cpp:120
armarx::control::ethercat::reporting::Entry::toString
std::string toString() const
Definition: ErrorReporting.cpp:355
armarx::control::ethercat::reporting::operator<<
std::ostream & operator<<(std::ostream &stream, const Type &rhs)
Definition: ErrorReporting.cpp:14
deactivateSpam
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition: Logging.cpp:72
armarx::control::ethercat::reporting::Severity::Info
@ Info
cxxopts::value
std::shared_ptr< Value > value()
Definition: cxxopts.hpp:926
armarx::control::ethercat::reporting::Type::Slave
@ Slave
sdfluieb,dj
armarx::control::ethercat::reporting::Severity
Severity
The Severity enum.
Definition: ErrorReporting.h:47
armarx::control::ethercat::reporting::Type::Device
@ Device
armarx::control::ethercat::reporting::Reporting::getErrorReporting
static Reporting & getErrorReporting()
Definition: ErrorReporting.cpp:82
armarx::control::ethercat::reporting::Reporter::reportDebug
ReportWrapper reportDebug(Entry &error)
Definition: ErrorReporting.cpp:276
armarx::control::ethercat::reporting::Reporting::getErrorReporter
Reporter getErrorReporter()
Definition: ErrorReporting.cpp:89
armarx::control::ethercat::reporting::Reporting::setMaxErrorHistorySize
void setMaxErrorHistorySize(std::size_t maxErrorHistorySize)
Definition: ErrorReporting.cpp:114
armarx::armem::Time
armarx::core::time::DateTime Time
Definition: forward_declarations.h:13
armarx::to_string
const std::string & to_string(const std::string &s)
Definition: StringHelpers.h:40
ErrorReporting.h
armarx::control::ethercat::reporting::Reporter::reportInfo
ReportWrapper reportInfo(Entry &error)
Definition: ErrorReporting.cpp:283
armarx::control::ethercat::reporting::Reporter
Brief description of class Reporter.
Definition: ErrorReporting.h:142
armarx::control::ethercat::reporting::Severity::Warning
@ Warning
armarx::control::ethercat::reporting::Entry::errorType
Entry & errorType(Type type)
Definition: ErrorReporting.cpp:332
armarx::control::ethercat::reporting::Entry::deviceName
Entry & deviceName(const char *deviceName)
Definition: ErrorReporting.cpp:348
armarx::control::ethercat::reporting::Severity::Fatal
@ Fatal
armarx::control::ethercat::SlaveIdentifier::toMinimalString
std::string toMinimalString(const std::string &linePrefix="") const
Returns a minimal string representation of a SlaveIdentifier.
Definition: SlaveIdentifier.cpp:100
armarx::control::ethercat::SlaveIdentifier::slaveIndex
std::int16_t slaveIndex
The index of the slave on the bus.
Definition: SlaveIdentifier.h:71
armarx::control::ethercat::reporting::Reporter::reportWarning
ReportWrapper reportWarning(Entry &error)
Definition: ErrorReporting.cpp:290
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
std
Definition: Application.h:66
armarx::control::ethercat::reporting::Type
Type
Definition: ErrorReporting.h:31
armarx::control::ethercat::reporting::Reporter::reportError
ReportWrapper reportError(Entry &error)
Definition: ErrorReporting.cpp:298
armarx::control::ethercat::reporting::Reporting
Brief description of class Reporting.
Definition: ErrorReporting.h:183
armarx::Logging::deactivateSpam
SpamFilterDataPtr deactivateSpam(float deactivationDurationSec=10.0f, const std::string &identifier="", bool deactivate=true) const
disables the logging for the current line for the given amount of seconds.
Definition: Logging.cpp:92
armarx::control::ethercat::reporting::Severity::Error
@ Error
armarx::Logging::setTag
void setTag(const LogTag &tag)
Definition: Logging.cpp:55
armarx::control::ethercat::reporting::Entry::slaveIdentifier
Entry & slaveIdentifier(SlaveIdentifier sid)
Definition: ErrorReporting.cpp:325
armarx::control::ethercat::reporting::Entry::metaInfo
Entry & metaInfo(const char *file, int line, const char *function)
Definition: ErrorReporting.cpp:339
armarx::control::ethercat::reporting::Reporting::QueueImpl::queue
boost::lockfree::queue< Entry, boost::lockfree::capacity< queueSize > > queue
Definition: ErrorReporting.cpp:62
armarx::control::ethercat::reporting::Reporter::reportErrorAndThrow
void reportErrorAndThrow(Entry &error)
Definition: ErrorReporting.cpp:306
armarx::control::ethercat::reporting::Reporting::QueueImpl
Definition: ErrorReporting.cpp:58
armarx::control::ethercat::reporting::Entry
Brief description of class Entry.
Definition: ErrorReporting.h:68
armarx::rtNow
IceUtil::Time rtNow()
Definition: RtTiming.h:40