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