Turi Create  4.0
logger.hpp
Go to the documentation of this file.
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 /**
7  * \defgroup turilogger Logging Utilities
8  */
9 
10 /**
11  * @file logger.hpp
12  * Usage:
13  * First include logger.hpp. To logger, use the logger() function
14  * There are 2 output levels. A "soft" output level which is
15  * set by calling global_logger.set_log_level(), as well as a "hard" output
16  * level OUTPUTLEVEL which is set in the source code (logger.h).
17  *
18  * when you call "logger()" with a loglevel and if the loglevel is greater than
19  * both of the output levels, the string will be written.
20  * written to a logger file. Otherwise, logger() has no effect.
21  *
22  * The difference between the hard level and the soft level is that the
23  * soft level can be changed at runtime, while the hard level optimizes away
24  * logging calls at compile time.
25  */
26 
27 #ifndef TURI_LOG_LOG_HPP
28 #define TURI_LOG_LOG_HPP
29 
30 #ifndef TURI_LOGGER_THROW_ON_FAILURE
31 #define TURI_LOGGER_THROW_ON_FAILURE
32 #endif
33 
34 #if defined(COMPILER_HAS_IOS_BASE_FAILURE_WITH_ERROR_CODE) && (_MSC_VER < 1600)
35 #undef COMPILER_HAS_IOS_BASE_FAILURE_WITH_ERROR_CODE
36 #endif
37 
38 #include <fstream>
39 #include <sstream>
40 #include <cstdlib>
41 #include <cassert>
42 #include <cstring>
43 #include <cstdarg>
44 #ifdef COMPILER_HAS_IOS_BASE_FAILURE_WITH_ERROR_CODE
45 #include <system_error>
46 #endif
47 #include <functional>
48 #include <core/parallel/pthread_h.h>
49 #include <timer/timer.hpp>
50 #include <core/logging/fail_method.hpp>
51 #include <core/logging/backtrace.hpp>
52 #include <core/logging/error.hpp>
53 #include <core/system/cppipc/server/cancel_ops.hpp>
54 #include <core/util/code_optimization.hpp>
55 #include <process/process_util.hpp>
56 
57 /**
58  * \ingroup turilogger
59  *
60  * \addtogroup levels Log Levels
61  * \brief Log Levels
62  * \{
63  * \def LOG_FATAL
64  * Used for fatal and probably irrecoverable conditions.
65  */
66 /**
67  * \def LOG_ERROR
68  * Used for errors which are recoverable within the scope of the function
69  */
70 /**
71  * \def LOG_WARNING
72  * Logs interesting conditions which are probably not fatal
73  */
74 /**
75  * \def LOG_EMPH
76  * Outputs as LOG_INFO, but in LOG_WARNING colors. Useful for
77  * outputting information you want to emphasize.
78  */
79 /**
80  * \def LOG_INFO
81  * Used for providing general useful information
82  */
83 /**
84  * \def LOG_DEBUG
85  * Debugging purposes only
86  */
87 /**
88  * \def LOG_EVERYTHING
89  * Log everything
90  * \}
91  */
92 // sgr - needed additional debug levels. I can undo this change if
93 // necessary. although it seems to me that log levels should count
94 // up and saturate so the messages label array can always be used.
95 #define LOG_NONE 8
96 #define LOG_FATAL 7
97 #define LOG_ERROR 6
98 #define LOG_WARNING 5
99 #define LOG_PROGRESS 4
100 #define LOG_EMPH 3
101 #define LOG_INFO 2
102 #define LOG_DEBUG 1
103 #define LOG_EVERYTHING 0
104 
105 /**
106  * \ingroup turilogger
107  * \def OUTPUTLEVEL
108  * The minimum level to logger at. Set to LOG_NONE
109  * OUTPUTLEVEL to LOG_NONE to disable logging
110  */
111 
112 #ifndef OUTPUTLEVEL
113 #define OUTPUTLEVEL LOG_DEBUG
114 #endif
115 /// If set, logs to screen will be printed in color
116 #define COLOROUTPUT
117 
118 
119 /**
120  * \ingroup turilogger
121  * \addtogroup Global Logging Statements
122  * \brief Global Logging Statements
123  * \{
124  * \def logger(lvl, fmt,...)
125  * Emits a log line output in printf format at a particular log level. lvl
126  * must be one of the log levels from LOG_DEBUG to LOG_FATAL. Emitting a
127  * LOG_FATAL will kill the process.
128  *
129  * Example:
130  * \code
131  * logger(LOG_INFO, "hello world: %d", 10);
132  * \endcode
133  */
134 
135 /**
136  * \def logstream(lvl)
137  * An output stream for specified log level. lvl must be one of the
138  * log levels from LOG_DEBUG to LOG_FATAL. Emitting a LOG_FATAL will
139  * kill the process. The stream must terminate with a "\n" or std::endl
140  * at the end of the message.
141  *
142  * Example:
143  * \code
144  * logstream(LOG_INFO << "hello world: " << 10 << std::endl;
145  * \endcode
146  */
147 
148 /**
149  * \def logger_once(lvl, fmt,...)
150  * Emits a log line output in printf format at a particular log level, only
151  * the first time the line is encountered. lvl must be one of the log levels
152  * from LOG_DEBUG to LOG_FATAL. Emitting a LOG_FATAL will kill the process.
153  *
154  * Example:
155  * \code
156  * logger_once(LOG_INFO, "Class %s constructed", str);
157  * \endcode
158  */
159 
160 /**
161  * \def logstream_once(lvl)
162  * An output stream for specified log level. This will only output the first
163  * time the line is encountered. lvl must be one of the log levels from
164  * LOG_DEBUG to LOG_FATAL. Emitting a LOG_FATAL will kill the process. The
165  * stream must terminate with a "\n" or std::endl at the end of the message.
166  *
167  * Example:
168  * \code
169  * logstream_once(LOG_INFO) << "Class " << str << " constructed" << std::endl;
170  * \endcode
171  */
172 
173 /**
174  * \def logger_ontick(sec, lvl, fmt,...)
175  * Emits a log line output in printf format at a particular log level, but
176  * will only print approximately once every "sec" seconds. lvl must be one
177  * of the log levels from LOG_DEBUG to LOG_FATAL. Emitting a LOG_FATAL will
178  * kill the process.
179  *
180  * Example:
181  * \code
182  * // only print once every 5 seconds
183  * logger_ontick(5, LOG_INFO, "Class %s constructed", str);
184  * \endcode
185  */
186 
187 /**
188  * \def logstream_ontick(sec, lvl)
189  * An output stream for specified log level. This will only print
190  * approximately once every "sec" seconds. lvl must be one of the log levels
191  * from LOG_DEBUG to LOG_FATAL. Emitting a LOG_FATAL will kill the process.
192  * The stream must terminate with a "\n" or std::endl at the end of the
193  * message.
194  *
195  * Example:
196  * \code
197  * logstream_ontick(5, LOG_INFO) << "Class " << str << " constructed"
198  * << std::endl;
199  * \endcode
200  */
201 
202 /**
203  * \def logprogress(fmt,...)
204  * Emits a progress message using printf format.
205  *
206  * Example:
207  * \code
208  * logprogress("hello world: %d", 10);
209  * \endcode
210  */
211 
212 /**
213  * \def logprogress_stream
214  * Emits a progress message using a stream. The stream must terminate with a
215  * "\n" or std::endl at the end of the message.
216  *
217  * Example:
218  * \code
219  * logprogress_stream << "hello world " << 10 << std::endl;
220  * \endcode
221  */
222 
223 /**
224  * \def logprogress_ontick(sec, fmt,...)
225  * Emits a progress message using printf format, but will only print
226  * approximately once every "sec" seconds.
227  * Example:
228  * \code
229  * // only print once every 5 seconds
230  * logprogress_ontick(5, "Class %s constructed", str);
231  * \endcode
232  */
233 
234 /**
235  * \def logprogress_stream_ontick(sec)
236  * Emits a progress message using a stream. The stream must terminate with a
237  * "\n" or std::endl at the end of the message. This will only print
238  * approximately once every "sec" seconds. The stream must terminate with
239  * a "\n" or std::endl at the end of the message.
240  *
241  * Example:
242  * \code
243  * logprogress_stream_ontick(5) << "Class " << str << " constructed"
244  * << std::endl;
245  * \endcode
246  * \}
247  */
248 #if OUTPUTLEVEL == LOG_NONE
249 // totally disable logging
250 #define logger(lvl,fmt,...)
251 #define logbuf(lvl,buflen,
252 #define logstream(lvl) if(0) null_stream()
253 
254 #define logger_once(lvl,fmt,...)
255 #define logstream_once(lvl) if(0) null_stream()
256 
257 #define logger_ontick(sec,lvl,fmt,...)
258 #define logstream_ontick(sec, lvl) if(0) null_stream()
259 
260 #define logprogress(fmt,...)
261 #define logprogress_stream if(0) null_stream()
262 
263 #define logprogress_ontick(sec,fmt,...)
264 #define logprogress_stream_ontick(sec) if(0) null_stream()
265 
266 #else
267 
268 #define logger(lvl, fmt, ...) \
269  (log_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl, __FILE__, __func__, __LINE__, \
270  fmt, ##__VA_ARGS__))
271 
272 #define logbuf(lvl, buf, len) \
273  (log_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl, __FILE__, __func__, __LINE__, \
274  buf, len))
275 
276 #define logstream(lvl) \
277  if (lvl >= global_logger().get_log_level()) \
278  (log_stream_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl, __FILE__, __func__, \
279  __LINE__))
280 
281 #define logger_once(lvl, fmt, ...) \
282  { \
283  static bool __printed__ = false; \
284  if (!__printed__) { \
285  __printed__ = true; \
286  (log_dispatch<(lvl >= OUTPUTLEVEL)>::exec( \
287  lvl, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)); \
288  } \
289  }
290 
291 #define logstream_once(lvl) \
292  (*({ \
293  static bool __printed__ = false; \
294  bool __prev_printed__ = __printed__; \
295  if (!__printed__) __printed__ = true; \
296  &(log_stream_dispatch<(lvl >= OUTPUTLEVEL)>::exec( \
297  lvl, __FILE__, __func__, __LINE__, !__prev_printed__)); \
298  }))
299 
300 #define logger_ontick(sec, lvl, fmt, ...) \
301  { \
302  static float last_print = -sec - 1; \
303  float curtime = turi::timer::approx_time_seconds(); \
304  if (last_print + sec <= curtime) { \
305  last_print = curtime; \
306  (log_dispatch<(lvl >= OUTPUTLEVEL)>::exec( \
307  lvl, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)); \
308  } \
309  }
310 
311 #define logstream_ontick(sec, lvl) \
312  (*({ \
313  static float last_print = -sec - 1; \
314  float curtime = turi::timer::approx_time_seconds(); \
315  bool print_now = false; \
316  if (last_print + sec <= curtime) { \
317  last_print = curtime; \
318  print_now = true; \
319  } \
320  &(log_stream_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl, __FILE__, __func__, \
321  __LINE__, print_now)); \
322  }))
323 
324 #define logprogress(fmt,...) logger(LOG_PROGRESS, fmt, ##__VA_ARGS__)
325 #define logprogress_stream logstream(LOG_PROGRESS)
326 
327 #define logprogress_ontick(sec,fmt,...) logger_ontick(sec, LOG_PROGRESS, fmt, ##__VA_ARGS__)
328 #define logprogress_stream_ontick(sec) logstream_ontick(sec, LOG_PROGRESS)
329 
330 #endif
331 
332 #ifdef NDEBUG
333 
334 #define log_and_throw(message) \
335  do { \
336  auto throw_error = [&]() GL_COLD_NOINLINE_ERROR { \
337  logstream(LOG_ERROR) << (message) << std::endl; \
338  throw(std::string(message)); \
339  }; \
340  throw_error(); \
341  } while (0)
342 
343 #define std_log_and_throw(key_type, message) \
344  do { \
345  auto throw_error = [&]() GL_COLD_NOINLINE_ERROR { \
346  logstream(LOG_ERROR) << (message) << std::endl; \
347  throw(key_type(message)); \
348  }; \
349  throw_error(); \
350  } while (0)
351 
352 #ifdef COMPILER_HAS_IOS_BASE_FAILURE_WITH_ERROR_CODE
353 #define log_and_throw_io_failure(message) \
354  do { \
355  auto throw_error = [&]() GL_COLD_NOINLINE_ERROR { \
356  logstream(LOG_ERROR) << (message) << std::endl; \
357  throw(turi::error::io_error(message, std::error_code())); \
358  }; \
359  throw_error(); \
360  } while (0)
361 #else
362 #define log_and_throw_io_failure(message) \
363  do { \
364  auto throw_error = [&]() GL_COLD_NOINLINE_ERROR { \
365  logstream(LOG_ERROR) << (message) << std::endl; \
366  throw(turi::error::io_error(message)); \
367  }; \
368  throw_error(); \
369  } while (0)
370 #endif
371 
372 #else // debug mode
373 
374 #define log_and_throw(message) \
375  do { \
376  auto throw_error = [&]() GL_COLD_NOINLINE_ERROR { \
377  std::stringstream _turi_ss; \
378  _turi_ss << (message) << ". " << __func__ << " from " << __FILE__ \
379  << " at " << __LINE__ << std::endl; \
380  logstream(LOG_ERROR) << message << std::endl; \
381  throw(_turi_ss.str()); \
382  }; \
383  throw_error(); \
384  } while (0)
385 
386 #define std_log_and_throw(key_type, message) \
387  do { \
388  auto throw_error = [&]() GL_COLD_NOINLINE_ERROR { \
389  std::stringstream _turi_ss; \
390  _turi_ss << (message) << ". " << __func__ << " from " << __FILE__ \
391  << " at " << __LINE__ << std::endl; \
392  logstream(LOG_ERROR) << message << std::endl; \
393  throw(key_type(_turi_ss.str())); \
394  }; \
395  throw_error(); \
396  } while (0)
397 
398 #ifdef COMPILER_HAS_IOS_BASE_FAILURE_WITH_ERROR_CODE
399 #define log_and_throw_io_failure(message) \
400  do { \
401  auto throw_error = [&]() GL_COLD_NOINLINE_ERROR { \
402  std::stringstream _turi_ss; \
403  _turi_ss << (message) << ". " << __func__ << " from " << __FILE__ \
404  << " at " << __LINE__ << std::endl; \
405  logstream(LOG_ERROR) << message << std::endl; \
406  throw(turi::error::io_error(__turi_ss.str(), std::error_code())); \
407  }; \
408  throw_error(); \
409  } while (0)
410 #else
411 #define log_and_throw_io_failure(message) \
412  do { \
413  auto throw_error = [&]() GL_COLD_NOINLINE_ERROR { \
414  std::stringstream _turi_ss; \
415  _turi_ss << (message) << ". " << __func__ << " from " << __FILE__ \
416  << " at " << __LINE__ << std::endl; \
417  logstream(LOG_ERROR) << message << std::endl; \
418  throw(turi::error::io_error(_turi_ss.str())); \
419  }; \
420  throw_error(); \
421  } while (0)
422 #endif
423 
424 #endif // end of NDEBUG
425 
426 #define log_and_throw_current_io_failure() \
427  do { \
428  auto error_code = errno; \
429  std::string error_message = std::strerror(error_code); \
430  errno = 0; /* clear errno */ \
431  log_and_throw_io_failure(error_message); \
432  } while (0)
433 
434 #define log_func_entry() \
435  do { \
436  logstream(LOG_INFO) << "Function entry" << std::endl; \
437  } while (0)
438 
439 #define Dlog_func_entry() \
440  do { \
441  logstream(LOG_DEBUG) << "Function entry" << std::endl; \
442  } while (0)
443 
444 namespace logger_impl {
445 struct streambuff_tls_entry {
446  std::stringstream streambuffer;
447  bool streamactive;
448  size_t header_len;
449  int loglevel;
450 };
451 }
452 
453 #define LOG_DEBUG_WITH_PID(...) \
454  do { \
455  auto __log_funct = [&]() { \
456  std::ostringstream ss; \
457  ss << "PID-" << global_logger().get_pid() << ": "; \
458  ss << __VA_ARGS__; \
459  logstream(LOG_DEBUG) << ss.str() << std::endl; \
460  }; \
461  if(LOG_DEBUG >= global_logger().get_log_level()) { __log_funct(); } \
462  } while(0)
463 
464 
465 extern void __print_back_trace();
466 
467 /**
468  * \ingroup turilogger
469  * The main logging class.
470  *
471  * This writes to a file, and/or the system console.
472  * The main logger \ref global_logger is a global instance of this class.
473  */
475  public:
476  /** Default constructor. By default, log_to_console is on,
477  there is no logger file, and logger level is set to LOG_EMPH
478  */
479  file_logger();
480 
481  ~file_logger(); /// destructor. flushes and closes the current logger file
482 
483  /** Closes the current logger file if one exists.
484  if 'file' is not an empty string, it will be opened and
485  all subsequent logger output will be written into 'file'.
486  Any existing content of 'file' will be cleared.
487  Return true on success and false on failure.
488  */
489  bool set_log_file(std::string file);
490 
491  /// If consolelog is true, subsequent logger output will be written
492  /// to stdout / stderr. If log_to_stderr is true, all output is
493  /// logged to stderr.
494  void set_log_to_console(bool consolelog, bool _log_to_stderr = false) {
495  log_to_console = consolelog;
496  log_to_stderr = _log_to_stderr;
497  }
498 
499  /// If consolelog is true, subsequent logger output will be written
500  /// to stdout / stderr. If log_to_stderr is true, all output is
501  /// logged to stderr.
502  void set_pid(size_t pid) {
503  reference_pid = pid;
504  }
505 
506  /// If consolelog is true, subsequent logger output will be written
507  /// to stdout / stderr. If log_to_stderr is true, all output is
508  /// logged to stderr.
509  size_t get_pid() const {
510  return reference_pid;
511  }
512 
513  /// Returns the current logger file.
514  std::string get_log_file(void) {
515  return log_file;
516  }
517 
518  /// Returns true if output is being written to stderr
520  return log_to_console;
521  }
522 
523  /// Returns the current logger level
525  return log_level;
526  }
527 
528  /**
529  * Set a callback to be called whenever a log message at a particular
530  * log level is issued. Only one observer can be set per log level.
531  */
532  void add_observer(int loglevel,
533  std::function<void(int lineloglevel, const char* buf, size_t len)> callback_fn) {
534  pthread_mutex_lock(&mut);
535  callback[loglevel] = callback_fn;
536  // has callback if callback_fn is not empty
537  has_callback[loglevel] = !!callback_fn;
538  pthread_mutex_unlock(&mut);
539  }
540 
541  /**
542  * Gets the callback called when a log message at a particular
543  * log level is issued. Note that only one observer can be set per log level.
544  */
545  inline std::function<void(int lineloglevel, const char* buf, size_t len)> get_observer(int loglevel) {
546  return callback[loglevel];
547  }
548 
549  file_logger& start_stream(int lineloglevel,const char* file,const char* function, int line, bool do_start = true);
550 
551  /**
552  * Streams a value into the logger.
553  */
554  template <typename T>
556  // get the stream buffer
557  logger_impl::streambuff_tls_entry* streambufentry = reinterpret_cast<logger_impl::streambuff_tls_entry*>(
558  pthread_getspecific(streambuffkey));
559  if (streambufentry != NULL) {
560  std::stringstream& streambuffer = streambufentry->streambuffer;
561  bool& streamactive = streambufentry->streamactive;
562 
563  if (streamactive) {
564  streambuffer << a;
565  }
566  }
567  return *this;
568  }
569 
570  /**
571  * Streams a value into the logger.
572  */
573  inline file_logger& operator<<(const char* a) {
574  // get the stream buffer
575  logger_impl::streambuff_tls_entry* streambufentry = reinterpret_cast<logger_impl::streambuff_tls_entry*>(
576  pthread_getspecific(streambuffkey));
577  if (streambufentry != NULL) {
578  std::stringstream& streambuffer = streambufentry->streambuffer;
579  bool& streamactive = streambufentry->streamactive;
580 
581  if (streamactive) {
582  streambuffer << a;
583  size_t s = strlen(a);
584  if (s > 0 && a[s-1] == '\n') {
585  stream_flush();
586  }
587  }
588  }
589  return *this;
590  }
591 
592  /**
593  * Streams an std::endl into the logger.
594  */
595  inline file_logger& operator<<(std::ostream& (*f)(std::ostream&)){
596  // get the stream buffer
597  logger_impl::streambuff_tls_entry* streambufentry = reinterpret_cast<logger_impl::streambuff_tls_entry*>(
598  pthread_getspecific(streambuffkey));
599  if (streambufentry != NULL) {
600  std::stringstream& streambuffer = streambufentry->streambuffer;
601  bool& streamactive = streambufentry->streamactive;
602 
603  if (streamactive) {
604  // TODO: previously, we had a check for if (endltype(f) == endltype(std::endl))
605  // and only flushed the stream on endl (ignoring all other stream modifiers).
606  // On recent clang compilers, this check seems to always return false in debug.
607  // (tested with Apple LLVM version 10.0.0 (clang-1000.11.45.5))
608  // As a workaround, let's just flush the stream on all modifiers.
609  // In practice they're usually endl anyway, so the perf hit should not be too bad.
610  streambuffer << f;
611  stream_flush();
612  if(streamloglevel == LOG_FATAL) {
613  __print_back_trace();
614  TURI_LOGGER_FAIL_METHOD("LOG_FATAL encountered");
615  }
616  }
617  }
618  return *this;
619  }
620 
621 
622 
623  /** Sets the current logger level. All logging commands below the current
624  logger level will not be written. */
625  void set_log_level(int new_log_level) {
626  log_level = new_log_level;
627  }
628 
629  /**
630  * logs the message if loglevel>=OUTPUTLEVEL
631  * This function should not be used directly. Use logger()
632  *
633  * @param loglevel Type of message \see LOG_DEBUG LOG_INFO LOG_WARNING LOG_ERROR LOG_FATAL
634  * @param file File where the logger call originated
635  * @param function Function where the logger call originated
636  * @param line Line number where the logger call originated
637  * @param fmt printf format string
638  * @param arg var args. The parameters that match the format string
639  */
640  void _log(int loglevel,const char* file,const char* function,
641  size_t line,const char* fmt, va_list arg );
642 
643  void _logbuf(int loglevel,const char* file,const char* function,
644  size_t line, const char* buf, size_t len);
645 
646  void _lograw(int loglevel, const char* buf, size_t len);
647 
648  inline void stream_flush() {
649  // get the stream buffer
650  logger_impl::streambuff_tls_entry* streambufentry =
651  reinterpret_cast<logger_impl::streambuff_tls_entry*>(
652  pthread_getspecific(streambuffkey));
653  if (streambufentry != NULL) {
654  std::stringstream& streambuffer = streambufentry->streambuffer;
655  int lineloglevel = streambufentry->loglevel;
656 
657  streambuffer.flush();
658  std::string msg = streambuffer.str();
659  _lograw(streamloglevel, msg.c_str(), msg.length());
660 
661  // call the callback on the message if any
662  if (has_callback[lineloglevel]) {
663  pthread_mutex_lock(&mut);
664  if (callback[lineloglevel]) {
665  callback[lineloglevel](lineloglevel,
666  msg.c_str() + streambufentry->header_len,
667  msg.length() - streambufentry->header_len);
668  }
669  streambufentry->header_len = 0;
670  pthread_mutex_unlock(&mut);
671  }
672  streambuffer.str("");
673  }
674  }
675  private:
676  std::ofstream fout;
677  std::string log_file;
678 
679  pthread_key_t streambuffkey;
680 
681  int streamloglevel;
682  pthread_mutex_t mut;
683 
684  bool log_to_console;
685  bool log_to_stderr;
686  int log_level;
687 
688  size_t reference_pid = size_t(-1);
689 
690  // LOG_NONE is the "highest" log level
691  std::function<void(int lineloglevel, const char* buf, size_t len)> callback[LOG_NONE];
692  int has_callback[LOG_NONE];
693 };
694 
695 
696 /**
697  * Gets a reference to the global logger which all the logging macros use.
698  */
700 
701 /**
702 Wrapper to generate 0 code if the output level is lower than the log level
703 */
704 template <bool dostuff>
705 struct log_dispatch {};
706 
707 template <>
708 struct log_dispatch<true> {
709  inline static void exec(int loglevel,const char* file,const char* function,
710  int line,const char* fmt, ... ) {
711  va_list argp;
712  va_start(argp, fmt);
713  global_logger()._log(loglevel, file, function, line, fmt, argp);
714  va_end(argp);
715  if(loglevel == LOG_FATAL) {
716  __print_back_trace();
717  TURI_LOGGER_FAIL_METHOD("LOG_FATAL encountered");
718  }
719  }
720 };
721 
722 template <>
723 struct log_dispatch<false> {
724  inline static void exec(int loglevel,const char* file,const char* function,
725  int line,const char* fmt, ... ) {}
726 };
727 
728 
729 struct null_stream {
730  template<typename T>
731  inline null_stream operator<<(T t) { return null_stream(); }
732  inline null_stream operator<<(const char* a) { return null_stream(); }
733  inline null_stream operator<<(std::ostream& (*f)(std::ostream&)) { return null_stream(); }
734 };
735 
736 
737 template <bool dostuff>
738 struct log_stream_dispatch {};
739 
740 template <>
741 struct log_stream_dispatch<true> {
742  inline static file_logger& exec(int lineloglevel,const char* file,const char* function, int line, bool do_start = true) {
743  // First see if there is an interupt waiting. This is a convenient place that a lot of people call.
744  if(cppipc::must_cancel()) {
745  log_and_throw("Canceled by user.");
746  }
747 
748 
749  return global_logger().start_stream(lineloglevel, file, function, line, do_start);
750  }
751 };
752 
753 template <>
754 struct log_stream_dispatch<false> {
755  inline static null_stream exec(int lineloglevel,const char* file,const char* function, int line, bool do_start = true) {
756 
757  // First see if there is an interupt waiting. This is a convenient place that a lot of people call.
758  if(cppipc::must_cancel()) {
759  log_and_throw("Canceled by user.");
760  }
761 
762  return null_stream();
763  }
764 };
765 
766 
767 #define TEXTCOLOR_RESET 0
768 #define TEXTCOLOR_BRIGHT 1
769 #define TEXTCOLOR_DIM 2
770 #define TEXTCOLOR_UNDERLINE 3
771 #define TEXTCOLOR_BLINK 4
772 #define TEXTCOLOR_REVERSE 7
773 #define TEXTCOLOR_HIDDEN 8
774 
775 #define TEXTCOLOR_BLACK 0
776 #define TEXTCOLOR_RED 1
777 #define TEXTCOLOR_GREEN 2
778 #define TEXTCOLOR_YELLOW 3
779 #define TEXTCOLOR_BLUE 4
780 #define TEXTCOLOR_MAGENTA 5
781 #define TEXTCOLOR_CYAN 6
782 #define TEXTCOLOR_WHITE 7
783 
784 void textcolor(FILE* handle, int attr, int fg);
785 std::string textcolor(int attr, int fg);
786 void reset_color(FILE* handle);
787 std::string reset_color();
788 
789 #endif
int get_log_level()
Returns the current logger level.
Definition: logger.hpp:524
void add_observer(int loglevel, std::function< void(int lineloglevel, const char *buf, size_t len)> callback_fn)
Definition: logger.hpp:532
std::string get_log_file(void)
Returns the current logger file.
Definition: logger.hpp:514
file_logger & operator<<(std::ostream &(*f)(std::ostream &))
Definition: logger.hpp:595
static std::ostream & operator<<(std::ostream &out, const uint128_t &x)
Enables printing of uint128_t values.
std::function< void(int lineloglevel, const char *buf, size_t len)> get_observer(int loglevel)
Definition: logger.hpp:545
void set_log_level(int new_log_level)
Definition: logger.hpp:625
file_logger & operator<<(T a)
Definition: logger.hpp:555
void set_log_to_console(bool consolelog, bool _log_to_stderr=false)
Definition: logger.hpp:494
file_logger & operator<<(const char *a)
Definition: logger.hpp:573
bool get_log_to_console()
Returns true if output is being written to stderr.
Definition: logger.hpp:519
void set_pid(size_t pid)
Definition: logger.hpp:502
size_t get_pid() const
Definition: logger.hpp:509
file_logger & global_logger()
#define LOG_FATAL
Definition: logger.hpp:96
void _log(int loglevel, const char *file, const char *function, size_t line, const char *fmt, va_list arg)