JeVois  1.20
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Log.H
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 /*! \file */
17 
18 #pragma once
19 
20 #include <sys/syslog.h> // for the syslog levels
21 #include <string.h> // for strerror
22 #include <string>
23 #include <sstream>
24 #include <cstdint>
25 #include <mutex>
26 
27 
28 namespace jevois
29 {
30  class Engine;
31  class RawImage;
32 
33  /*! \defgroup debugging Debugging helper classes, functions and macros */
34 
35  //! Current log level
36  /*! The log level is set by changing the value of this global variable. The default value is LOG_INFO.
37 
38  The possible log values are defined in sys/syslog.h and here we only handle the following with different amounts
39  of messages: LOG_CRIT, LOG_ERR, LOG_INFO, LOG_DEBUG. \ingroup debugging */
40  extern int logLevel;
41 
42  //! Current trace level
43  /*! Higher levels yield more verbosity in tracing. Note that this has effect only if JEVOIS_TRACE_ENABLE is specified
44  as a compile option, and the trace messages are issued at the LDEBUG log level, so JEVOIS_LDEBUG_ENABLE must also
45  be specified at compile time. \ingroup debugging*/
46  extern int traceLevel;
47 
48  //! Logger class
49  /*! Users would typically not use this class directly but instead invoke one of the LDEBUG(msg), LINFO(msg), etc
50  macros. Note that by default logging is asynchronous, i.e., when issuing a log message it is assembled and then
51  pushed into a queue, and another thread then pops it back from the queue and displays it. Define
52  JEVOIS_USE_SYNC_LOG at compile time to have the mesage displayed immediately but beware that this can break USB
53  strict timing requirements. \ingroup debugging */
54  template <int Level>
55  class Log
56  {
57  public:
58  //! Construct a new Log, adding a prefix to the log stream
59  /*! If outstr is non-null, the log message will be copied into it upon destruction. */
60  Log(char const * fullFileName, char const * functionName, std::string * outstr = nullptr);
61 
62  //! Close the Log, outputting the aggregated message
63  ~Log();
64 
65  //! Overloaded stream input operator for any type that has operator<< defined for ostream.
66  template <class T> inline
67  Log<Level> & operator<<(T const & out_item) { itsLogStream << out_item; return *this; }
68 
69  //! Overload of operator<< for uint8 (displays it as an int rather than char)
70  Log<Level> & operator<<(uint8_t const & out_item);
71 
72  //! Overload of operator<< for int8 (displays it as an int rather than char)
73  Log<Level> & operator<<(int8_t const & out_item);
74 
75  private:
76  std::ostringstream itsLogStream;
77  std::string * itsOutStr;
78  };
79 
80  //! Convenience function to catch an exception, issue some LERROR (depending on type), and rethrow it
81  /*! User code that is not going to swallow exceptions can use this function as follows, to log some trace of the
82  exception that was thrown:
83 
84  \code
85  try { do_something_risky(); } catch (...) { jevois::warnAndRethrowException(); }
86  \endcode
87 
88  \note This function throws! So obviously it only makes sense to use it inside a catch block. \ingroup debugging */
89  void warnAndRethrowException[[noreturn]](std::string const & prefix = "");
90 
91  //! Convenience function to catch an exception, issue some LERROR (depending on type), and ignore it
92  /*! User code can use this function as follows, to log some trace of the exception that was thrown, and then swallow
93  (ignore) the exception. Use this sparingly, swallowing exceptions often defeats the whole logic of using
94  exceptions in the first place. Example use:
95 
96  \code
97  try { do_something_risky_and_we_dont_care_if_it_fails(); } catch (...) { jevois::warnAndIgnoreException(); }
98  \endcode
99 
100  Note that the message that is logged to console is also returned as a string, in case one wants to report it in
101  some other way (e.g., in a GUI or in a video frame using drawErrorImage()). \ingroup debugging */
102  std::string warnAndIgnoreException(std::string const & prefix = "");
103 
104  //! Convenience function for parameter callback exceptions
105  /*! Used internally by Parameter, likely not so useful to others, included in the jevois namespace to avoid having to
106  pull boost::python into Parameter, which would pull it into pretty much everything and increase compile time a
107  lot.
108  \note This function throws! So obviously it only makes sense to use it inside a catch block. \ingroup debugging */
109  void warnAndRethrowParamCallbackException[[noreturn]](std::string const & descriptor, std::string const & strval);
110 
111  //! Display an error message into a RawImage
112  /*! The error message should consist of a string where multiple lines may be separated by \\n characters, such as the
113  string returned by warnAndIgnoreException(). The message will be written in the image, which should be
114  valid(). This is useful to display module exceptions in the video stream that is sent over USB.*/
115  void drawErrorImage(std::string const & errmsg, RawImage & videoerrimg);
116 
117  //! Set an Engine so that all log messages will be forwarded to its serial ports
118  /*! This function is not intended for general use, Engine uses it internally when users set one of its
119  parameters to enable forwarding of log messages to serial ports. \ingroup debugging*/
120  void logSetEngine(Engine * e);
121 
122  //! Terminate log service
123  /*! You must call this once you a ready to end a program, to stop the logger thread. Otherwise the ThreadPool will be
124  stuck with one running thread and will never exit. */
125  void logEnd();
126 
127 } // namespace jevois
128 
129 
130 #ifdef JEVOIS_LDEBUG_ENABLE
131 //! Convenience macro for users to print out console or syslog messages, DEBUG level
132 /*! \def LDEBUG(msg)
133  \hideinitializer
134 
135  This macro is intended to be used with a stream-oriented syntax for everything that is passed as argument to the
136  macro. The syntax is a bit strange at first but you will rapidly get used to it. This allows any datatype that has
137  an operator<< defined to be printed out in a log (contrary to printf-style syntax). For example:
138 
139  @code
140  int x = 3; std::string str = "hello"; jevois::StepRange<int> rng(0, 5, 100);
141  LDEBUG("x=" << x << " and str=" << str << " and rng=" << rng);
142  @endcode
143 
144  \note This is the preferred way to issue messages. Do not use printf, do not use cout<<"blah", etc.
145 
146  \warning No line breaks ('\n' and similar) are allowed in LDEBUG(), LINFO(), and LERROR(), as these may be sent out
147  over serial ports to simple processors like Arduino, with just one prefix ("DBG ", "INF ", "ERR ") followed by the
148  message, for easy parsing. JeVois-Inventor will likely not be able to function if you send multiline messages. Line
149  breaks are allowed in exception error messages and in LFATAL() and LTHROW(), and the multiple lines will be sent as
150  several consecutive messages.
151 
152  By design, your log message will not be evaluated if the current log level is below (stronger than) the debug
153  level. This means that you should not be afraid of wasting CPU computing messages that will not be output; for
154  example:
155 
156  @code
157  LINFO("CPU-intensive function says: " << cpu_intensive_function());
158  @endcode
159 
160  will not run the cpu-intensive function if the current log level is LOG_ERR (it will still run one "if" statement to
161  check the current log level). This also means that you should never assume that your log message will be
162  evaluated. For example:
163 
164  @code
165  int x = 42;
166  LDEBUG("x = " << (x++) ); // x may now be 43 or 42 depending on current log level...
167  @endcode
168 
169  \note Because LDEBUG() may be used for debugging of many fast loops, including through the use of
170  JEVOIS_TRACE(level), it will be compiled in only if JEVOIS_LDEBUG_ENABLE is defined during build (typicaly, this is
171  done as an option passed to cmake), otherwise it will simply be commented out so that no CPU is wasted.
172  \ingroup debugging */
173 #define LDEBUG(msg) do { if (jevois::logLevel >= LOG_DEBUG) \
174  jevois::Log<LOG_DEBUG>(__FILE__, __FUNCTION__) << msg; } while (false)
175 
176 //! Like LDEBUG but appends errno and strerror(errno), to be used when some system call fails
177 /*! \def PLDEBUG(msg)
178  \hideinitializer
179 
180  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
181 #define PLDEBUG(msg) do { if (jevois::logLevel >= LOG_DEBUG) \
182  jevois::Log<LOG_DEBUG>(__FILE__, __FUNCTION__) << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
183  while (false)
184 #else
185 #define LDEBUG(msg) do { } while (false)
186 #define PLDEBUG(msg) do { } while (false)
187 #endif
188 
189 //! Convenience macro for users to print out console or syslog messages, INFO level
190 /*! \def LINFO(msg)
191  \hideinitializer
192 
193  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
194 #define LINFO(msg) do { if (jevois::logLevel >= LOG_INFO) jevois::Log<LOG_INFO>(__FILE__, __FUNCTION__) << msg; } \
195  while (false)
196 
197 //! Like LINFO but appends errno and strerror(errno), to be used when some system call fails
198 /*! \def PLINFO(msg)
199  \hideinitializer
200 
201  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
202 #define PLINFO(msg) do { if (jevois::logLevel >= LOG_INFO) \
203  jevois::Log<LOG_INFO>(__FILE__, __FUNCTION__) << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
204  while (false)
205 
206 //! Convenience macro for users to print out console or syslog messages, ERROR level
207 /*! \def LERROR(msg)
208  \hideinitializer
209 
210  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
211 #define LERROR(msg) do { if (jevois::logLevel >= LOG_ERR) jevois::Log<LOG_ERR>(__FILE__, __FUNCTION__) << msg; } \
212  while (false)
213 
214 //! Like LERROR but appends errno and strerror(errno), to be used when some system call fails
215 /*! \def PLERROR(msg)
216  \hideinitializer
217 
218  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
219 #define PLERROR(msg) do { if (jevois::logLevel >= LOG_ERR) \
220  jevois::Log<LOG_ERR>(__FILE__, __FUNCTION__) << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
221  while (false)
222 
223 
224 //! Convenience macro for users to print out console or syslog messages, FATAL level
225 /*! \def LFATAL(msg)
226  \hideinitializer
227 
228  Usage syntax is the same as for LDEBUG(msg)
229  \note After printing the message, this also throws std::runtime_error \ingroup debugging */
230 #define LFATAL(msg) do { std::string str; { jevois::Log<LOG_CRIT>(__FILE__, __FUNCTION__, &str) << msg; } \
231  throw std::runtime_error(str); } while (false)
232 
233 //! Like LDEBUG but appends errno and strerror(errno), to be used when some system call fails
234 /*! \def PLFATAL(msg)
235  \hideinitializer
236 
237  Usage syntax is the same as for LDEBUG(msg)
238  \note After printing the message, this also throws std::runtime_error \ingroup debugging */
239 #define PLFATAL(msg) do { std::string str; { jevois::Log<LOG_CRIT>(__FILE__, __FUNCTION__, &str) \
240  << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
241  throw std::runtime_error(str); } while (false)
242 
243 //! Convenience macro for users to throw std::runtime_error with convenient message formatting
244 /*! \def LFATAL(msg)
245  \hideinitializer
246 
247  Usage syntax is the same as for LDEBUG(msg). Nothing is added by this function to the user-provided
248  error message. So this is mainly useful for situations where the exception will be caught and
249  a consolidated error message will then be issued via LFATAL() (maybe adding some more context details).
250  \note This throws std::runtime_error \ingroup debugging */
251 #define LTHROW(msg) do { std::string str; { jevois::Log<LOG_ALERT>(nullptr, nullptr, &str) << msg; } \
252  throw std::runtime_error(str); } while (false)
253 
254 //! Test whether something is true and issue an LFATAL if not
255 /*! \def JEVOIS_ASSERT(cond)
256  \hideinitializer \ingroup debugging */
257 #define JEVOIS_ASSERT(cond) do { if (cond) { } else \
258  { std::string str; { jevois::Log<LOG_CRIT>(__FILE__, __FUNCTION__, &str) << "Assertion failed: " #cond; } \
259  throw std::runtime_error(str); } } while (false)
260 
261 // ##############################################################################################################
262 #ifdef JEVOIS_TRACE_ENABLE
263 namespace jevois
264 {
265  namespace trace
266  {
267  //! Helper class for tracing, issues one message on construction, and another on destruction
268  /*! Users would typically use the JEVOIS_TRACE(level) macro rather than this class directly. \ingroup debugging */
270  {
271  public:
272  //! Constructor, logs "file:function Enter"
273  inline TraceObject(int level, char const * file, char const * func) :
274  itsLevel(level), itsFile(file), itsFunc(func)
275  { if (jevois::traceLevel >= itsLevel) jevois::Log<LOG_DEBUG>(file, func) << ">>> TRACE: Enter >>>"; }
276 
277  //! Destructor, logs "file:function Exit"
278  inline ~TraceObject()
279  { if (jevois::traceLevel >= itsLevel) jevois::Log<LOG_DEBUG>(itsFile, itsFunc) << "<<< TRACE: Exit <<<"; }
280 
281  private:
282  int const itsLevel;
283  char const * const itsFile;
284  char const * const itsFunc;
285  };
286  }
287 }
288 
289 //! Trace object
290 /*! \def JEVOIS_TRACE(level)
291  \hideinitializer
292 
293  Use this as you do with, e.g., std::lock_guard. Issues one LDEBUG() message on construction, and one on
294  destruction. Typically, you would hence invoke JEVOIS_TRACE as the first command in each of the functions you want
295  to trace. \ingroup debugging */
296 #define JEVOIS_TRACE(level) jevois::trace::TraceObject __jevois_trace_reserved(level, __FILE__, __FUNCTION__)
297 #else
298 #define JEVOIS_TRACE(level) do { } while (0)
299 #endif
300 
301 // ##############################################################################################################
302 namespace jevois
303 {
304  //! Acquire a lock object on a std::timed_mutex, or LFATAL after 1 second of waiting
305  /*! Use this as you would use lock_guard (but make sure your mutex is std::timed_mutex). It will throw in case of
306  deadlock, useful for debugging. Users would typically use the JEVOIS_TIMED_LOCK(mtx) macro rather than this class
307  directly. \ingroup debugging */
309  {
310  public:
311  //! Constructor, locks the mutex or throw if it cannot be locked before timeout
312  explicit timed_lock_guard(std::timed_mutex & mtx, char const * file, char const * func);
313 
314  //! Destructor, unlocks the mutex
316 
317  private:
318  std::timed_mutex & itsMutex;
319  };
320 }
321 
322 //! Helper macro to create a timed_lock_guard object
323 /*! \def JEVOIS_TIMED_LOCK(mtx)
324  \hideinitializer
325 
326  Creates a timed_lock_guard over std::timed_mutex mtx, which will throw if mtx cannot be locked before timeout. The
327  guard will unlock the mutex upon destruction. \ingroup debugging */
328 #define JEVOIS_TIMED_LOCK(mtx) jevois::timed_lock_guard __jevois_timed_lock_guard_reserved(mtx, __FILE__, __FUNCTION__)
329 
330 // ##############################################################################################################
331 //! Wait for a future to become ready, throws after 5 seconds
332 #define JEVOIS_WAIT_FOR_FUTURE(f) do { if (f.valid() && f.wait_for(std::chrono::seconds(2)) == \
333 std::future_status::timeout) LFATAL("Timeout waiting for future " #f); } while(false)
334 
335 //! Wait for a future to become ready for 5 seconds, get(), warn and ignore exception, report on timeout
336 #define JEVOIS_WAIT_GET_FUTURE(f) do { if (f.valid()) { \
337  if (f.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) LERROR("Timeout waiting for future " #f); \
338  try { f.get(); } catch (...) { jevois::warnAndIgnoreException(); } } } while(false)
339 
jevois::warnAndRethrowException
void warnAndRethrowException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:203
jevois::timed_lock_guard::~timed_lock_guard
~timed_lock_guard()
Destructor, unlocks the mutex.
Definition: Log.C:356
jevois::drawErrorImage
void drawErrorImage(std::string const &errmsg, RawImage &videoerrimg)
Display an error message into a RawImage.
Definition: Log.C:300
jevois::trace::TraceObject
Helper class for tracing, issues one message on construction, and another on destruction.
Definition: Log.H:269
jevois::Log::~Log
~Log()
Close the Log, outputting the aggregated message.
Definition: Log.C:167
jevois::timed_lock_guard::timed_lock_guard
timed_lock_guard(std::timed_mutex &mtx, char const *file, char const *func)
Constructor, locks the mutex or throw if it cannot be locked before timeout.
Definition: Log.C:345
jevois::traceLevel
int traceLevel
Current trace level.
Definition: Log.C:30
jevois::imu::file
Data collection mode RAW means that the latest available raw data is returned each time hence timing may not be very accurate depending on how regularly grate into a FIFO and and accumulates resulting output data into the IMU s internal FIFO buffer at a fixed rate This parameter can only be set in a module s params cfg file
Definition: ICM20948.H:67
jevois
Definition: Concepts.dox:1
jevois::logSetEngine
void logSetEngine(Engine *e)
Set an Engine so that all log messages will be forwarded to its serial ports.
Definition: Log.C:124
jevois::warnAndRethrowParamCallbackException
void warnAndRethrowParamCallbackException(std::string const &descriptor, std::string const &strval)
Convenience function for parameter callback exceptions.
Definition: Log.C:274
jevois::Log
Logger class.
Definition: Log.H:55
jevois::warnAndIgnoreException
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:236
jevois::Log::operator<<
Log< Level > & operator<<(T const &out_item)
Overloaded stream input operator for any type that has operator<< defined for ostream.
Definition: Log.H:67
jevois::logLevel
int logLevel
Current log level.
Definition: Log.C:29
jevois::trace::TraceObject::~TraceObject
~TraceObject()
Destructor, logs "file:function Exit".
Definition: Log.H:278
jevois::Log::Log
Log(char const *fullFileName, char const *functionName, std::string *outstr=nullptr)
Construct a new Log, adding a prefix to the log stream.
Definition: Log.C:130
jevois::logEnd
void logEnd()
Terminate log service.
Definition: Log.C:125
jevois::timed_lock_guard
Acquire a lock object on a std::timed_mutex, or LFATAL after 1 second of waiting.
Definition: Log.H:308
jevois::trace::TraceObject::TraceObject
TraceObject(int level, char const *file, char const *func)
Constructor, logs "file:function Enter".
Definition: Log.H:273