JeVois  1.0
JeVois Smart Embedded Machine Vision Toolkit
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 
32  /*! \defgroup debugging Debugging helper classes, functions and macros */
33 
34  //! Current log level
35  /*! The log level is set by changing the value of this global variable. The default value is LOG_INFO.
36 
37  The possible log values are defined in sys/syslog.h and here we only handle the following with different amounts
38  of messages: LOG_CRIT, LOG_ERR, LOG_INFO, LOG_DEBUG. \ingroup debugging */
39  extern int logLevel;
40 
41  //! Current trace level
42  /*! Higher levels yield more verbosity in tracing. Note that this has effect only if JEVOIS_TRACE_ENABLE is specified
43  as a compile option, and the trace messages are issued at the LDEBUG log level, so JEVOIS_LDEBUG_ENABLE must also
44  be specified at compile time. \ingroup debugging*/
45  extern int traceLevel;
46 
47  //! Logger class
48  /*! Users would typically not use this class directly but instead invoke one of the LDEBUG(msg), LINFO(msg), etc
49  macros. Note that by default logging is asynchronous, i.e., when issuing a log message it is assembled and then
50  pushed into a queue, and another thread then pops it back from the queue and displays it. Define
51  JEVOIS_USE_SYNC_LOG at compile time to have the mesage displayed immediately but beware that this can break USB
52  strict timing requirements. \ingroup debugging */
53  template <int Level>
54  class Log
55  {
56  public:
57  //! Construct a new Log, adding a prefix to the log stream
58  /*! If outstr is non-null, the log message will be copied into it upon destruction. */
59  Log(char const * fullFileName, char const * functionName, std::string * outstr = nullptr);
60 
61  //! Close the Log, outputting the aggregated message
62  ~Log();
63 
64  //! Overloaded stream input operator for any type that has operator<< defined for ostream.
65  template <class T> inline
66  Log<Level> & operator<<(T const & out_item) { itsLogStream << out_item; return *this; }
67 
68  //! Overload of operator<< for uint8 (displays it as an int rather than char)
69  Log<Level> & operator<<(uint8_t const & out_item);
70 
71  //! Overload of operator<< for int8 (displays it as an int rather than char)
72  Log<Level> & operator<<(int8_t const & out_item);
73 
74  private:
75  std::ostringstream itsLogStream;
76  std::string * itsOutStr;
77  };
78 
79  //! Convenience function to catch an exception, issue some LERROR (depending on type), and rethrow it
80  /*! User code that is not going to swallow exceptions can use this function as follows, to log some trace of the
81  exception that was thrown:
82 
83  \code
84  try { do_something_risky(); } catch (...) { jevois::warnAndRethrowException(); }
85  \endcode
86 
87  \note This function throws! So obviously it only makes sense to use it inside a catch block. \ingroup debugging */
88  void warnAndRethrowException[[noreturn]]();
89 
90  //! Convenience function to catch an exception, issue some LERROR (depending on type), and ignore it
91  /*! User code can use this function as follows, to log some trace of the exception that was thrown, and then swallow
92  (ignore) the exception. Use this sparingly, swallowing exceptions often defeats the whole logic of using
93  exceptions in the first place. Example use:
94 
95  \code
96  try { do_something_risky_and_we_dont_care_if_it_fails(); } catch (...) { jevois::warnAndIgnoreException(); }
97  \endcode
98 
99  Note that the message that is logged to console is also returned as a string (without the "Ignoring uncaught..."
100  part), in case one wants to report it in some other way (e.g., in a GUI). \ingroup debugging */
101  std::string warnAndIgnoreException();
102 
103  //! Set an Engine so that all log messages will be forwarded to its serial ports
104  /*! This function is not intended for general use, Engine uses it internally when users set one of its
105  parameters to enable forwarding of log messages to serial ports. \ingroup debugging*/
106  void logSetEngine(Engine * e);
107 
108 } // namespace jevois
109 
110 
111 #ifdef JEVOIS_LDEBUG_ENABLE
112 //! Convenience macro for users to print out console or syslog messages, DEBUG level
113 /*! \def LDEBUG(msg)
114  \hideinitializer
115 
116  This macro is intended to be used with a stream-oriented syntax for everything that is passed as argument to the
117  macro. The syntax is a bit strange at first but you will rapidly get used to it. This allows any datatype that has
118  an operator<< defined to be printed out in a log (contrary to printf-style syntax). For example:
119 
120  @code
121  int x = 3; std::string str = "hello"; jevois::StepRange<int> rng(0, 5, 100);
122  LDEBUG("x=" << x << " and str=" << str << " and rng=" << rng);
123  @endcode
124 
125  \note This is the preferred way to issue messages. Do you use printf, do not use cout<<"blah", etc.
126 
127  By design, your log message will not be evaluated if the current log level is below (stronger than) the debug
128  level. This means that you should not be afraid of wasting CPU computing messages that will not be output; for
129  example:
130 
131  @code
132  LDEBUG("CPU-intensive function says: " << cpu_intensive_function());
133  @endcode
134 
135  will not run the cpu-intensive function if the current log level is LOG_ERR (it will still run one "if" statement to
136  check the current log level). This also means that you should never assume that your log message will be
137  evaluated. For example:
138 
139  @code
140  int x = 42;
141  LDEBUG("x = " << (x++) ); // x may now be 43 or 42 depending on current log level...
142  @endcode
143 
144  \note Because LDEBUG() may be used for debugging of many fast loops, including through the use of
145  JEVOIS_TRACE(level), it will be compiled in only if JEVOIS_LDEBUG_ENABLE is defined during build (typicaly, this is
146  done as an option passed to cmake), otherwise it will simply be commented out so that no CPU is wasted.
147  \ingroup debugging */
148 #define LDEBUG(msg) do { if (jevois::logLevel >= LOG_DEBUG) \
149  jevois::Log<LOG_DEBUG>(__FILE__, __FUNCTION__) << msg; } while (false)
150 
151 //! Like LDEBUG but appends errno and strerror(errno), to be used when some system call fails
152 /*! \def PLDEBUG(msg)
153  \hideinitializer
154 
155  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
156 #define PLDEBUG(msg) do { if (jevois::logLevel >= LOG_DEBUG) \
157  jevois::Log<LOG_DEBUG>(__FILE__, __FUNCTION__) << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
158  while (false)
159 #else
160 #define LDEBUG(msg) do { } while (false)
161 #define PLDEBUG(msg) do { } while (false)
162 #endif
163 
164 //! Convenience macro for users to print out console or syslog messages, INFO level
165 /*! \def LINFO(msg)
166  \hideinitializer
167 
168  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
169 #define LINFO(msg) do { if (jevois::logLevel >= LOG_INFO) jevois::Log<LOG_INFO>(__FILE__, __FUNCTION__) << msg; } \
170  while (false)
171 
172 //! Like LINFO but appends errno and strerror(errno), to be used when some system call fails
173 /*! \def PLINFO(msg)
174  \hideinitializer
175 
176  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
177 #define PLINFO(msg) do { if (jevois::logLevel >= LOG_INFO) \
178  jevois::Log<LOG_INFO>(__FILE__, __FUNCTION__) << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
179  while (false)
180 
181 //! Convenience macro for users to print out console or syslog messages, ERROR level
182 /*! \def LERROR(msg)
183  \hideinitializer
184 
185  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
186 #define LERROR(msg) do { if (jevois::logLevel >= LOG_ERR) jevois::Log<LOG_ERR>(__FILE__, __FUNCTION__) << msg; } \
187  while (false)
188 
189 //! Like LERROR but appends errno and strerror(errno), to be used when some system call fails
190 /*! \def PLERROR(msg)
191  \hideinitializer
192 
193  Usage syntax is the same as for LDEBUG(msg) \ingroup debugging */
194 #define PLERROR(msg) do { if (jevois::logLevel >= LOG_ERR) \
195  jevois::Log<LOG_ERR>(__FILE__, __FUNCTION__) << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
196  while (false)
197 
198 
199 //! Convenience macro for users to print out console or syslog messages, FATAL level
200 /*! \def LFATAL(msg)
201  \hideinitializer
202 
203  Usage syntax is the same as for LDEBUG(msg)
204  \note After printing the message, this also throws std::runtime_error \ingroup debugging */
205 #define LFATAL(msg) do { std::string str; { jevois::Log<LOG_CRIT>(__FILE__, __FUNCTION__, &str) << msg; } \
206  throw std::runtime_error(str); } while (false)
207 
208 //! Like LDEBUG but appends errno and strerror(errno), to be used when some system call fails
209 /*! \def PLFATAL(msg)
210  \hideinitializer
211 
212  Usage syntax is the same as for LDEBUG(msg)
213  \note After printing the message, this also throws std::runtime_error \ingroup debugging */
214 #define PLFATAL(msg) do { std::string str; { jevois::Log<LOG_CRIT>(__FILE__, __FUNCTION__, &str) \
215  << msg << " [" << errno << "](" << strerror(errno) << ')'; } \
216  throw std::runtime_error(str); } while (false)
217 
218 //! Test whether something is true and issue an LFATAL if not
219 /*! \def JEVOIS_ASSERT(cond)
220  \hideinitializer \ingroup debugging */
221 #define JEVOIS_ASSERT(cond) do { if (cond) { } else \
222  { std::string str; { jevois::Log<LOG_CRIT>(__FILE__, __FUNCTION__, &str) << "Assertion failed: " #cond; } \
223  throw std::runtime_error(str); } } while (false)
224 
225 // ##############################################################################################################
226 #ifdef JEVOIS_TRACE_ENABLE
227 namespace jevois
228 {
229  namespace trace
230  {
231  //! Helper class for tracing, issues one message on construction, and another on destruction
232  /*! Users would typically use the JEVOIS_TRACE(level) macro rather than this class directly. \ingroup debugging */
234  {
235  public:
236  //! Constructor, logs "file:function Enter"
237  inline TraceObject(int level, char const * file, char const * func) :
238  itsLevel(level), itsFile(file), itsFunc(func)
239  { if (jevois::traceLevel >= itsLevel) jevois::Log<LOG_DEBUG>(file, func) << "Enter"; }
240 
241  //! Destructor, logs "file:function Exit"
242  inline ~TraceObject()
243  { if (jevois::traceLevel >= itsLevel) jevois::Log<LOG_DEBUG>(itsFile, itsFunc) << "Exit"; }
244 
245  private:
246  int const itsLevel;
247  char const * const itsFile;
248  char const * const itsFunc;
249  };
250  }
251 }
252 
253 //! Trace object
254 /*! \def JEVOIS_TRACE(level)
255  \hideinitializer
256 
257  Use this as you do with, e.g., std::lock_guard. Issues one LDEBUG() message on construction, and one on
258  destruction. Typically, you would hence invoke JEVOIS_TRACE as the first command in each of the functions you want
259  to trace. \ingroup debugging */
260 #define JEVOIS_TRACE(level) jevois::trace::TraceObject __jevois_trace_reserved(level, __FILE__, __FUNCTION__)
261 #else
262 #define JEVOIS_TRACE(level) do { } while (0)
263 #endif
264 
265 // ##############################################################################################################
266 namespace jevois
267 {
268  //! Acquire a lock object on a std::timed_mutex, or LFATAL after 1 second of waiting
269  /*! Use this as you would use lock_guard (but make sure your mutex is std::timed_mutex). It will throw in case of
270  deadlock, useful for debugging. Users would typically use the JEVOIS_TIMED_LOCK(mtx) macro rather than this class
271  directly. \ingroup debugging */
273  {
274  public:
275  //! Constructor, locks the mutex or throw if it cannot be locked before timeout
276  explicit timed_lock_guard(std::timed_mutex & mtx, char const * file, char const * func);
277 
278  //! Destructor, unlocks the mutex
280 
281  private:
282  std::timed_mutex & itsMutex;
283  };
284 }
285 
286 //! Helper macro to create a timed_lock_guard object
287 /*! \def JEVOIS_TIMED_LOCK(mtx)
288  \hideinitializer
289 
290  Creates a timed_lock_guard over std::timed_mutex mtx, which will throw if mtx cannot be locked before timeout. The
291  guard will unlock the mutex upon destruction. \ingroup debugging */
292 #define JEVOIS_TIMED_LOCK(mtx) jevois::timed_lock_guard __jevois_timed_lock_guard_reserved(mtx, __FILE__, __FUNCTION__)
293 
std::string warnAndIgnoreException()
Convenience function to catch an exception, issue some LERROR (depending on type), and ignore it.
Definition: Log.C:200
Log< Level > & operator<<(T const &out_item)
Overloaded stream input operator for any type that has operator<< defined for ostream.
Definition: Log.H:66
~TraceObject()
Destructor, logs "file:function Exit".
Definition: Log.H:242
TraceObject(int level, char const *file, char const *func)
Constructor, logs "file:function Enter".
Definition: Log.H:237
Helper class for tracing, issues one message on construction, and another on destruction.
Definition: Log.H:233
void warnAndRethrowException()
Convenience function to catch an exception, issue some LERROR (depending on type), and rethrow it.
Definition: Log.C:181
void logSetEngine(Engine *e)
Set an Engine so that all log messages will be forwarded to its serial ports.
Definition: Log.C:112
int logLevel
Current log level.
Definition: Log.C:26
Logger class.
Definition: Log.H:54
~timed_lock_guard()
Destructor, unlocks the mutex.
Definition: Log.C:237
Acquire a lock object on a std::timed_mutex, or LFATAL after 1 second of waiting. ...
Definition: Log.H:272
int traceLevel
Current trace level.
Definition: Log.C:27
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:226
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:118
~Log()
Close the Log, outputting the aggregated message.
Definition: Log.C:146