JeVois  1.21
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
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
28namespace 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
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
263namespace 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 */
269 class TraceObject
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// ##############################################################################################################
302namespace 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 */
308 class timed_lock_guard
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
315 ~timed_lock_guard();
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)) == \
333std::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
340
Logger class.
Definition Log.H:56
Log< Level > & operator<<(T const &out_item)
Overloaded stream input operator for any type that has operator<< defined for ostream.
Definition Log.H:67
~Log()
Close the Log, outputting the aggregated message.
Definition Log.C:178
int logLevel
Current log level.
Definition Log.C:29
int traceLevel
Current trace level.
Definition Log.C:30
void logSetEngine(Engine *e)
Set an Engine so that all log messages will be forwarded to its serial ports.
Definition Log.C:144
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition Log.C:236
void warnAndRethrowParamCallbackException(std::string const &descriptor, std::string const &strval)
Convenience function for parameter callback exceptions.
Definition Log.C:274
void warnAndRethrowException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition Log.C:203
Main namespace for all JeVois classes and functions.
Definition Concepts.dox:2
void drawErrorImage(std::string const &errmsg, RawImage &videoerrimg)
Display an error message into a RawImage.
Definition Log.C:300
void logEnd()
Terminate log service.
Definition Log.C:145