JeVois  1.16
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  //! Display an error message into a RawImage
105  /*! The error message should consist of a string where multiple lines may be separated by \\n characters, such as the
106  string returned by warnAndIgnoreException(). The message will be written in the image, which should be
107  valid(). This is useful to display module exceptions in the video stream that is sent over USB.*/
108  void drawErrorImage(std::string const & errmsg, RawImage & videoerrimg);
109 
110  //! Set an Engine so that all log messages will be forwarded to its serial ports
111  /*! This function is not intended for general use, Engine uses it internally when users set one of its
112  parameters to enable forwarding of log messages to serial ports. \ingroup debugging*/
113  void logSetEngine(Engine * e);
114 
115  //! Terminate log service
116  /*! You must call this once you a ready to end a program, to stop the logger thread. Otherwise the ThreadPool will be
117  stuck with one running thread and will never exit. */
118  void logEnd();
119 
120 } // namespace jevois
121 
122 
123 #ifdef JEVOIS_LDEBUG_ENABLE
124 //! Convenience macro for users to print out console or syslog messages, DEBUG level
125 /*! \def LDEBUG(msg)
126  \hideinitializer
127 
128  This macro is intended to be used with a stream-oriented syntax for everything that is passed as argument to the
129  macro. The syntax is a bit strange at first but you will rapidly get used to it. This allows any datatype that has
130  an operator<< defined to be printed out in a log (contrary to printf-style syntax). For example:
131 
132  @code
133  int x = 3; std::string str = "hello"; jevois::StepRange<int> rng(0, 5, 100);
134  LDEBUG("x=" << x << " and str=" << str << " and rng=" << rng);
135  @endcode
136 
137  \note This is the preferred way to issue messages. Do you use printf, do not use cout<<"blah", etc.
138 
139  By design, your log message will not be evaluated if the current log level is below (stronger than) the debug
140  level. This means that you should not be afraid of wasting CPU computing messages that will not be output; for
141  example:
142 
143  @code
144  LDEBUG("CPU-intensive function says: " << cpu_intensive_function());
145  @endcode
146 
147  will not run the cpu-intensive function if the current log level is LOG_ERR (it will still run one "if" statement to
148  check the current log level). This also means that you should never assume that your log message will be
149  evaluated. For example:
150 
151  @code
152  int x = 42;
153  LDEBUG("x = " << (x++) ); // x may now be 43 or 42 depending on current log level...
154  @endcode
155 
156  \note Because LDEBUG() may be used for debugging of many fast loops, including through the use of
157  JEVOIS_TRACE(level), it will be compiled in only if JEVOIS_LDEBUG_ENABLE is defined during build (typicaly, this is
158  done as an option passed to cmake), otherwise it will simply be commented out so that no CPU is wasted.
159  \ingroup debugging */
160 #define LDEBUG(msg) do { if (jevois::logLevel >= LOG_DEBUG) \
161  jevois::Log<LOG_DEBUG>(__FILE__, __FUNCTION__) << msg; } while (false)
162 
163 //! Like LDEBUG but appends errno and strerror(errno), to be used when some system call fails
164 /*! \def PLDEBUG(msg)
165  \hideinitializer
166 
167  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
168 #define PLDEBUG(msg) do { if (jevois::logLevel >= LOG_DEBUG) \
169  jevois::Log<LOG_DEBUG>(__FILE__, __FUNCTION__) << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
170  while (false)
171 #else
172 #define LDEBUG(msg) do { } while (false)
173 #define PLDEBUG(msg) do { } while (false)
174 #endif
175 
176 //! Convenience macro for users to print out console or syslog messages, INFO level
177 /*! \def LINFO(msg)
178  \hideinitializer
179 
180  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
181 #define LINFO(msg) do { if (jevois::logLevel >= LOG_INFO) jevois::Log<LOG_INFO>(__FILE__, __FUNCTION__) << msg; } \
182  while (false)
183 
184 //! Like LINFO but appends errno and strerror(errno), to be used when some system call fails
185 /*! \def PLINFO(msg)
186  \hideinitializer
187 
188  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
189 #define PLINFO(msg) do { if (jevois::logLevel >= LOG_INFO) \
190  jevois::Log<LOG_INFO>(__FILE__, __FUNCTION__) << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
191  while (false)
192 
193 //! Convenience macro for users to print out console or syslog messages, ERROR level
194 /*! \def LERROR(msg)
195  \hideinitializer
196 
197  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
198 #define LERROR(msg) do { if (jevois::logLevel >= LOG_ERR) jevois::Log<LOG_ERR>(__FILE__, __FUNCTION__) << msg; } \
199  while (false)
200 
201 //! Like LERROR but appends errno and strerror(errno), to be used when some system call fails
202 /*! \def PLERROR(msg)
203  \hideinitializer
204 
205  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
206 #define PLERROR(msg) do { if (jevois::logLevel >= LOG_ERR) \
207  jevois::Log<LOG_ERR>(__FILE__, __FUNCTION__) << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
208  while (false)
209 
210 
211 //! Convenience macro for users to print out console or syslog messages, FATAL level
212 /*! \def LFATAL(msg)
213  \hideinitializer
214 
215  Usage syntax is the same as for LDEBUG(msg)
216  \note After printing the message, this also throws std::runtime_error \ingroup debugging */
217 #define LFATAL(msg) do { std::string str; { jevois::Log<LOG_CRIT>(__FILE__, __FUNCTION__, &str) << msg; } \
218  throw std::runtime_error(str); } while (false)
219 
220 //! Like LDEBUG but appends errno and strerror(errno), to be used when some system call fails
221 /*! \def PLFATAL(msg)
222  \hideinitializer
223 
224  Usage syntax is the same as for LDEBUG(msg)
225  \note After printing the message, this also throws std::runtime_error \ingroup debugging */
226 #define PLFATAL(msg) do { std::string str; { jevois::Log<LOG_CRIT>(__FILE__, __FUNCTION__, &str) \
227  << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
228  throw std::runtime_error(str); } while (false)
229 
230 //! Test whether something is true and issue an LFATAL if not
231 /*! \def JEVOIS_ASSERT(cond)
232  \hideinitializer \ingroup debugging */
233 #define JEVOIS_ASSERT(cond) do { if (cond) { } else \
234  { std::string str; { jevois::Log<LOG_CRIT>(__FILE__, __FUNCTION__, &str) << "Assertion failed: " #cond; } \
235  throw std::runtime_error(str); } } while (false)
236 
237 // ##############################################################################################################
238 #ifdef JEVOIS_TRACE_ENABLE
239 namespace jevois
240 {
241  namespace trace
242  {
243  //! Helper class for tracing, issues one message on construction, and another on destruction
244  /*! Users would typically use the JEVOIS_TRACE(level) macro rather than this class directly. \ingroup debugging */
246  {
247  public:
248  //! Constructor, logs "file:function Enter"
249  inline TraceObject(int level, char const * file, char const * func) :
250  itsLevel(level), itsFile(file), itsFunc(func)
251  { if (jevois::traceLevel >= itsLevel) jevois::Log<LOG_DEBUG>(file, func) << ">>> TRACE: Enter >>>"; }
252 
253  //! Destructor, logs "file:function Exit"
254  inline ~TraceObject()
255  { if (jevois::traceLevel >= itsLevel) jevois::Log<LOG_DEBUG>(itsFile, itsFunc) << "<<< TRACE: Exit <<<"; }
256 
257  private:
258  int const itsLevel;
259  char const * const itsFile;
260  char const * const itsFunc;
261  };
262  }
263 }
264 
265 //! Trace object
266 /*! \def JEVOIS_TRACE(level)
267  \hideinitializer
268 
269  Use this as you do with, e.g., std::lock_guard. Issues one LDEBUG() message on construction, and one on
270  destruction. Typically, you would hence invoke JEVOIS_TRACE as the first command in each of the functions you want
271  to trace. \ingroup debugging */
272 #define JEVOIS_TRACE(level) jevois::trace::TraceObject __jevois_trace_reserved(level, __FILE__, __FUNCTION__)
273 #else
274 #define JEVOIS_TRACE(level) do { } while (0)
275 #endif
276 
277 // ##############################################################################################################
278 namespace jevois
279 {
280  //! Acquire a lock object on a std::timed_mutex, or LFATAL after 1 second of waiting
281  /*! Use this as you would use lock_guard (but make sure your mutex is std::timed_mutex). It will throw in case of
282  deadlock, useful for debugging. Users would typically use the JEVOIS_TIMED_LOCK(mtx) macro rather than this class
283  directly. \ingroup debugging */
285  {
286  public:
287  //! Constructor, locks the mutex or throw if it cannot be locked before timeout
288  explicit timed_lock_guard(std::timed_mutex & mtx, char const * file, char const * func);
289 
290  //! Destructor, unlocks the mutex
292 
293  private:
294  std::timed_mutex & itsMutex;
295  };
296 }
297 
298 //! Helper macro to create a timed_lock_guard object
299 /*! \def JEVOIS_TIMED_LOCK(mtx)
300  \hideinitializer
301 
302  Creates a timed_lock_guard over std::timed_mutex mtx, which will throw if mtx cannot be locked before timeout. The
303  guard will unlock the mutex upon destruction. \ingroup debugging */
304 #define JEVOIS_TIMED_LOCK(mtx) jevois::timed_lock_guard __jevois_timed_lock_guard_reserved(mtx, __FILE__, __FUNCTION__)
305 
306 // ##############################################################################################################
307 //! Wait for a future to become ready, throws after 5 seconds
308 #define JEVOIS_WAIT_FOR_FUTURE(f) do { if (f.valid() && f.wait_for(std::chrono::seconds(2)) == \
309 std::future_status::timeout) LFATAL("Timeout waiting for future " #f); } while(false)
310 
311 //! Wait for a future to become ready for 5 seconds, get(), warn and ignore exception, report on timeout
312 #define JEVOIS_WAIT_GET_FUTURE(f) do { if (f.valid()) { \
313  if (f.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) LERROR("Timeout waiting for future " #f); \
314  try { f.get(); } catch (...) { jevois::warnAndIgnoreException(); } } } while(false)
315 
jevois::warnAndRethrowException
void warnAndRethrowException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:193
jevois::timed_lock_guard::~timed_lock_guard
~timed_lock_guard()
Destructor, unlocks the mutex.
Definition: Log.C:316
jevois::drawErrorImage
void drawErrorImage(std::string const &errmsg, RawImage &videoerrimg)
Display an error message into a RawImage.
Definition: Log.C:260
jevois::trace::TraceObject
Helper class for tracing, issues one message on construction, and another on destruction.
Definition: Log.H:245
jevois::Log::~Log
~Log()
Close the Log, outputting the aggregated message.
Definition: Log.C:158
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:305
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::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:224
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:254
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:284
jevois::trace::TraceObject::TraceObject
TraceObject(int level, char const *file, char const *func)
Constructor, logs "file:function Enter".
Definition: Log.H:249