iMSTK
Interactive Medical Simulation Toolkit
imstkLoggerSynchronous.h
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #pragma once
8 
9 #include <stdexcept>
10 #include <string>
11 #include <memory>
12 #include <iostream>
13 #include <sstream>
14 #include <fstream>
15 
16 #include "imstkSpinLock.h"
17 
18 // Using g3log loglevels for compatibility
19 #include "g3log/loglevels.hpp"
20 #include <deque>
21 /*
22 
23 Still some weirdness, as we are still building G3 Log, the defines are still active
24 
25 #define DEBUG 0
26 #define INFO 1
27 #define WARNING 2
28 #define FATAL 3
29 */
30 namespace imstk
31 {
32 #if defined(__func__)
33 #define IMSTK_CURRENT_FUNCTION __func__
34 #elif defined(__FUNCSIG__)
35 #define IMSTK_CURRENT_FUNCTION __FUNCSIG__
36 #elif defined(__PRETTY_FUNCTION__)
37 #define IMSTK_CURRENT_FUNCTION __PRETTY_FUNCTION__
38 #elif defined(__FUNCTION__)
39 #define IMSTK_CURRENT_FUNCTION __FUNCTION__
40 #else
41 #define IMSTK_CURRENT_FUNCTION "???"
42 #endif
43 
44 #define IMSTK_MAKE_STRING(x) #x
45 
46 class AssertionFailure : public std::runtime_error
47 {
48 public:
51  explicit AssertionFailure(const std::string& message) : std::runtime_error(message)
52  {
53  }
54 };
55 
56 class LogOutput
57 {
58 public:
59  LogOutput() = default;
60  virtual ~LogOutput() = default;
61 
64  virtual bool writeMessage(const std::string& message) = 0;
65 };
66 
67 class NullOutput : public LogOutput
68 {
69 public:
70  virtual bool writeMessage(const std::string&) { return true; }
71 };
72 
73 /*
75 class FileOutput : public LogOutput
76 {
77 public:
78 
81  explicit FileOutput(const std::string& filename);
82 
85  bool writeMessage(const std::string& message) override;
86 
87 private:
88  std::string m_filename;
89  std::ofstream m_stream;
90  boost::mutex m_mutex;
91 };
92 */
93 
96 class StreamOutput : public LogOutput
97 {
98 public:
99 
103  explicit StreamOutput(std::ostream& ostream); //NOLINT
104 
108  bool writeMessage(const std::string& message) override;
109 
110 private:
111  std::ostream& m_stream;
112  ParallelUtils::SpinLock m_mutex;
113 };
114 
117 class CacheOutput : public LogOutput
118 {
119 public:
120  CacheOutput();
124  bool writeMessage(const std::string& message) override;
125 
126  bool hasMessages() const;
127 
128  std::string popLastMessage();
129 
130 private:
131  std::ofstream m_outFile;
132  std::shared_ptr<StreamOutput> m_fileOutput;
133  std::deque<std::string> m_messages;
134  ParallelUtils::SpinLock mutable m_mutex;
135 };
136 
138 {
139 public:
142  LoggerSynchronous(std::shared_ptr<LogOutput> output) : m_threshold(DEBUG.value), m_name("imstk"), m_output(output) { }
143 
144  static std::shared_ptr<LoggerSynchronous> instance()
145  {
146  static std::shared_ptr<LoggerSynchronous> defaultLogger = std::make_shared<LoggerSynchronous>(
147  std::make_shared<CacheOutput>());
148  return defaultLogger;
149  }
150 
152  ~LoggerSynchronous() = default;
153 
155  static void startLogger() {}
156 
160  bool writeMessage(const std::string& message)
161  {
162  return m_output->writeMessage(message);
163  }
164 
168  int getThreshold() const
169  {
170  return m_threshold;
171  }
172 
176  void setThreshold(int val)
177  {
178  m_threshold = val;
179  }
180 
183  std::shared_ptr<LogOutput> getOutput() const
184  {
185  return m_output;
186  }
187 
190  std::shared_ptr<CacheOutput> getCacheOutput() const
191  {
192  return std::dynamic_pointer_cast<CacheOutput>(m_output);
193  }
194 
197  void setOutput(std::shared_ptr<LogOutput> val)
198  {
199  m_output = val;
200  }
201 
202  std::string getName()
203  {
204  return m_name;
205  }
206 
207 private:
208  int m_threshold;
209  std::string m_name;
210  std::shared_ptr<LogOutput> m_output;
211 };
212 
214 {
215 public:
216 
220  LogMessageBase(LoggerSynchronous* logger, int level);
221 
222  ~LogMessageBase() noexcept(false) {
223  flush();
224  };
225 
228  template<typename T>
229  LogMessageBase& operator <<(T&& input)
230  {
231  m_stream << input;
232  return *this;
233  }
234 
235  // A specialization for output manipulators (functions that apply to the stream).
236  // Otherwise overloaded manipulators like std::endl and std::endl don't work, since the compiler can't know
237  // what overloaded variant to apply.
238  LogMessageBase& operator <<(std::ios_base& (*manipulator)(std::ios_base&))
239  {
240  m_stream << *manipulator;
241  return *this;
242  }
243 
244  // A specialization for output manipulators (functions that apply to the stream).
245  // Otherwise overloaded manipulators like std::hex and std::endl don't work, since the compiler can't know
246  // what overloaded variant to apply.
247  LogMessageBase& operator <<(std::ostream& (*manipulator)(std::ostream&))
248  {
249  m_stream << *manipulator;
250  return *this;
251  }
252 
253 protected:
255  std::string getMessage()
256  {
257  return m_stream.str();
258  }
259 
261  void flush()
262  {
263  m_logger->writeMessage(m_stream.str());
264  }
265 
266 private:
267  std::ostringstream m_stream;
268  LoggerSynchronous* m_logger;
269 };
270 
272 {
273 public:
275  typedef void (* DeathCallback)(const std::string& message);
276 
279  explicit AssertMessage(LoggerSynchronous* logger) : LogMessageBase(logger, FATAL.value)
280  {
281  }
282 
285  explicit AssertMessage(const std::unique_ptr<LoggerSynchronous>& logger) : LogMessageBase(logger.get(), FATAL.value)
286  {
287  }
288 
291  explicit AssertMessage(const std::shared_ptr<LoggerSynchronous>& logger) : LogMessageBase(logger.get(), FATAL.value)
292  {
293  }
294 
295  ~AssertMessage() noexcept(false)
296  {
297  flush();
298  m_killMeNow(getMessage());
299  }
300 
305  static void setFailureCallback(DeathCallback callback);
306 
310  static DeathCallback getFailureCallback();
311 
315  {
316  setFailureCallback(throwException);
317  }
318 
322  {
323  setFailureCallback(killApplication);
324  }
325 
326 private:
329  static void throwException(const std::string& errorMessage);
330 
333  static void killApplication(const std::string& errorMessage);
334 
337  static DeathCallback m_killMeNow;
338 };
339 } // namespace imstk
340 
341 #define LOG(level) \
342  if (level.value < (::imstk::LoggerSynchronous::instance())->getThreshold()) \
343  { \
344  } \
345  else \
346  /* important: no curly braces around this! */ \
347  ::imstk::LogMessageBase(::imstk::LoggerSynchronous::instance().get(), level.value)
348 
349 #define LOG_IF(level, condition) \
350  if (!(condition)) \
351  { \
352  } \
353  else \
354  /* important: no curly braces around this! */ \
355  LOG(level)
356 
357 #define CHECK(condition) \
358  if ((condition)) \
359  { \
360  } \
361  else \
362  /* important: no curly braces around this! */ \
363  ::imstk::AssertMessage(::imstk::LoggerSynchronous::instance().get()) << "*** Assertion failed: " << \
364  IMSTK_MAKE_STRING(condition) << " ***" << std::endl << \
365  " in " << IMSTK_CURRENT_FUNCTION << std::endl << \
366  " at " << __FILE__ << ":" << __LINE__ << std::endl
The SpinLock class.
Definition: imstkSpinLock.h:20
static void setFailureBehaviorToThrow()
Compound Geometry.
bool writeMessage(const std::string &message)
AssertMessage(const std::shared_ptr< LoggerSynchronous > &logger)
virtual bool writeMessage(const std::string &)
void setOutput(std::shared_ptr< LogOutput > val)
static void startLogger()
For compatibility.
static void setFailureBehaviorToDeath()
AssertMessage(const std::unique_ptr< LoggerSynchronous > &logger)
std::shared_ptr< LogOutput > getOutput() const
void flush()
write the current message to the logger
std::shared_ptr< CacheOutput > getCacheOutput() const
AssertMessage(LoggerSynchronous *logger)
AssertionFailure(const std::string &message)
LoggerSynchronous(std::shared_ptr< LogOutput > output)