Logger.h
Go to the documentation of this file.
1 // This file is part of the MercuryDPM project (https://www.mercurydpm.org).
2 // Copyright (c), The MercuryDPM Developers Team. All rights reserved.
3 // License: BSD 3-Clause License; see the LICENSE file in the root directory.
4 
5 #ifndef LOGGER_H
6 #define LOGGER_H
7 
8 #include <string>
9 #include <sstream>
10 #include <functional>
11 #include <type_traits>
12 #include <iomanip>
13 #include "GeneralDefine.h"
14 #include <iostream>
15 
16 #ifndef MERCURYDPM_LOGLEVEL
17 #define MERCURYDPM_LOGLEVEL Log::DEFAULT
18 #endif
19 
20 #ifndef CG_LOGLEVEL
21 #define CG_LOGLEVEL Log::DEFAULT
22 #endif
23 
24 #ifdef assert
25 #undef assert
26 //#error You included assert before the logger. Please use logger.assert_debug() instead.
27 #endif
28 
29 #ifdef MERCURYDPM_FORCE_ASSERTS
30 #define MERCURYDPM_ASSERTS true
31 #else
32 #ifdef MERCURYDPM_NO_ASSERTS
33 #define MERCURYDPM_ASSERTS false
34 #else
35 #ifdef NDEBUG
36 #define MERCURYDPM_ASSERTS false
37 #else
38 #define MERCURYDPM_ASSERTS true
39 #endif
40 #endif
41 #endif
42 
93 enum class Flusher
94 {
95  FLUSH,
96  NO_FLUSH
97 };
98 
107 enum class Log
108  : signed char
109 {
110  FATAL = -20, ERROR = -15, WARN = -10, INFO = -5, DEFAULT = 0, VERBOSE = 5, DEBUG = 10
111 };
112 
119 constexpr bool operator<=(const Log rhs, const Log lhs)
120 {
121  return ((static_cast<signed char>(rhs)) <= (static_cast<signed char>(lhs)));
122 }
123 
136 {
137 public:
138  std::function<void(std::string, std::string, Flusher)> onFatal;
139  std::function<void(std::string, std::string, Flusher)> onError;
140  std::function<void(std::string, std::string, Flusher)> onWarn;
141  std::function<void(std::string, std::string, Flusher)> onInfo;
142  std::function<void(std::string, std::string, Flusher)> onVerbose;
143  std::function<void(std::string, std::string, Flusher)> onDebug;
144 };
145 
154 extern LoggerOutput* loggerOutput;
155 
156 // Forward declaration..
157 template<Log L = Log::DEFAULT, bool ASSERTS = MERCURYDPM_ASSERTS>
158 class Logger;
159 
172 template<Log Level>
173 class LL
174 {
175 public:
176 };
177 
194 extern LL<Log::FATAL> FATAL;
195 
208 extern LL<Log::ERROR> ERROR;
209 
221 extern LL<Log::WARN> WARN;
222 
233 extern LL<Log::INFO> INFO;
234 
241 
253 
264 extern LL<Log::DEBUG> DEBUG;
265 
282 template<Log L, bool ASSERTS>
283 class Logger
284 {
285 
286 private:
296 
297 public:
298 
304  explicit Logger(const std::string name)
305  : module(name)
306  {
307  }
308 
313  = default;
314 
328  template<Log LOGLEVEL, typename ... Args>
329  typename std::enable_if<!((L < LOGLEVEL) && (MERCURYDPM_LOGLEVEL < LOGLEVEL)), void>::type
330  operator()(const LL<LOGLEVEL> log, const char* format UNUSED, Args&& ... arg UNUSED)
331  {
332  std::stringstream msgstream;
333  createMessage(msgstream, format, arg...);
334  if (LOGLEVEL <= Log::FATAL)
335  {
336  loggerOutput->onFatal(module, msgstream.str(), doFlush_);
337  }
338  else if (LOGLEVEL <= Log::ERROR)
339  {
340  loggerOutput->onError(module, msgstream.str(), doFlush_);
341  }
342  else if (LOGLEVEL <= Log::WARN)
343  {
344  loggerOutput->onWarn(module, msgstream.str(), doFlush_);
346  }
347  else if (LOGLEVEL <= Log::INFO)
348  {
349  loggerOutput->onInfo(module, msgstream.str(), doFlush_);
351  }
352  else if (LOGLEVEL <= Log::VERBOSE)
353  {
354  loggerOutput->onVerbose(module, msgstream.str(), doFlush_);
356  }
357  else
358  {
359  loggerOutput->onDebug(module, msgstream.str(), doFlush_ = Flusher::FLUSH);
361  }
362  }
363 
367  template<Log LOGLEVEL, typename... Args>
369  operator()(const LL<LOGLEVEL> log, const char* format UNUSED, Args&& ... arg UNUSED)
370  {
371  }
372 
376  //std::string is sometimes convenient, but always slow, so where possible, don't convert the const char* to a string
377  //before converting it back
378  template<Log LOGLEVEL, typename... Args>
379  void operator()(const LL<LOGLEVEL> log, const std::string& format UNUSED, Args&& ... arg
380  UNUSED)
381  {
382  (*this)(log, format.c_str(), arg...);
383  }
384 
399  template<typename... Args>
400  typename std::enable_if<(ASSERTS) && (sizeof...(Args) >= 0), void>::type
401  assert_debug(bool assertion, const char* format, Args&& ... arg)
402  {
403  assert_always(assertion, format, arg...);
404  }
405 
406  template<typename... Args>
407  typename std::enable_if<!((ASSERTS) && sizeof...(Args) >= 0), void>::type
408  assert_debug(bool assertion, const char* format, Args&& ... arg)
409  {
410  }
411 
415  //the conversion from "" to a std::string is so slow, it takes 50% of the total run time for a release build...
416  template<typename... Args>
417  void assert_debug(bool assertion, const std::string format, Args&& ... arg)
418  {
419  assert_debug(assertion, format.c_str(), arg...);
420  }
421 
422  template<typename... Args>
423  void assert_always(bool assertion, const char* format, Args&& ... arg)
424  {
425  if (!assertion)
426  {
427  std::stringstream msgstream;
428  createMessage(msgstream, format, arg...);
429  loggerOutput->onFatal(module, msgstream.str(), doFlush_);
430  }
431 
432  }
433 
437  template<typename... Args>
438  void assert_always(bool assertion, const std::string format, Args&& ... arg)
439  {
440  assert_always(assertion, format.c_str(), arg...);
441  }
442 
447  template<typename... Args>
449  void log(const Log loglevel, const std::string& format, Args&& ... arg)
450  {
451  if (loglevel <= L || loglevel <= MERCURYDPM_LOGLEVEL)
452  {
453  std::stringstream msgstream;
454  createMessage(msgstream, format.c_str(), arg...);
455  if (loglevel <= Log::FATAL)
456  {
457  loggerOutput->onFatal(module, msgstream.str(), doFlush_);
458  }
459  else if (loglevel <= Log::ERROR)
460  {
461  loggerOutput->onError(module, msgstream.str(), doFlush_);
462  }
463  else if (loglevel <= Log::WARN)
464  {
465  loggerOutput->onWarn(module, msgstream.str(), doFlush_);
467  }
468  else if (loglevel <= Log::INFO)
469  {
470  loggerOutput->onInfo(module, msgstream.str(), doFlush_);
472  }
473  else if (loglevel <= Log::VERBOSE)
474  {
475  loggerOutput->onVerbose(module, msgstream.str(), doFlush_);
477  }
478  else
479  {
480  loggerOutput->onDebug(module, msgstream.str(), doFlush_ = Flusher::FLUSH);
482  }
483  }
484  }
485 
486 private:
487 
502  template<typename Arg1, typename... Args>
503  void createMessage(std::stringstream& msg, const char* fmt,
504  Arg1&& arg, Args&& ... args)
505  {
506  bool doSkipNext = false;
507  while (*fmt != '%' || doSkipNext)
508  {
509  //Make sure we're not running past the end of our formatting string.
510  if (*fmt == '\0')
511  return;
512 
513  if (*fmt == '\\' && !doSkipNext)
514  { //Escape for the % character
515  doSkipNext = true;
516  fmt++;
517  }
518  else
519  {
520  msg << *fmt;
521  fmt++;
522  doSkipNext = false;
523  }
524  }
525  fmt++; //Consume the % character
526  int precision = 0;
527  int width = 0;
528  // if precision and width or only precision is defined
529  if (isdigit(*fmt))
530  {
531  precision = std::atoi(fmt);
532  while (isdigit(*fmt))
533  {
534  fmt++;
535  }
536  if (std::ispunct(*fmt))
537  {
538  fmt++;
539  if (std::isdigit(*fmt))
540  {
541  width = std::atoi(fmt);
542  while (isdigit(*fmt))
543  {
544  fmt++;
545  }
546  }
547  // else the char is a real full stop so set the pointer back to full stop.
548  else
549  {
550  fmt--;
551  }
552  }
553  }
554  // if only a width and no precision defined
555  else if (std::ispunct(*fmt))
556  {
557  fmt++;
558  if (std::isdigit(*fmt))
559  {
560  width = std::atoi(fmt);
561  while (isdigit(*fmt))
562  {
563  fmt++;
564  }
565  }
566  // else the char is a real full stop so set the pointer back to full stop.
567  else
568  {
569  fmt--;
570  }
571  }
572  if (width != 0 && precision != 0)
573  {
574  msg << std::setprecision(precision) << std::left << std::setw(width) << arg;
575  }
576  else if (precision != 0)
577  {
578  msg << std::setprecision(precision) << arg;
579  }
580  else if (width != 0)
581  {
582  msg << std::left << std::setw(width) << arg;
583  }
584  else
585  {
586  msg << arg;
587  } //include args somehow..
588  createMessage(msg, fmt, args...);//and recursively call ourselve / the method below.
589  }
590 
591 
602  //terminating case for Flusher not needed. This function is also called when the parameter pack Args&& args is
603  // empty
604  template<typename... Args>
605  void createMessage(std::stringstream& msg, const char* fmt,
606  Flusher arg, Args&& ... args)
607  {
608  // only suppress flushing if Mercury is not in CMAKE_BUILD_TYPE "Debug" and if the user defined loglevel from
609  // cMake is below VERBOSE/DEBUG (<=5)
610 #ifndef MERCURYDPM_DEBUG
612  {
614  }
615 #endif
616  // skip this argument by recursively calling this function again
617  createMessage(msg, fmt, args...);
618  }
619 
620 
629  // faster than above function and non recursive that is why it is good to have it
630  template<typename Arg1>
631  void createMessage(std::stringstream& msg, const char* fmt, Arg1&& arg)
632  {
633  bool doSkipNext = false;
634  while (*fmt != '%' || doSkipNext)
635  {
636  if (*fmt == '\0') // End of string
637  return;
638 
639  if (*fmt == '\\' && !doSkipNext)
640  { //Escape for the % character and the \ character
641  doSkipNext = true;
642  fmt++;
643  }
644  else
645  { //invoke the replacement
646  msg << *fmt;
647  fmt++;
648  doSkipNext = false;
649  }
650  }
651  fmt++; //Consume the % character
652  int precision = 0;
653  int width = 0;
654  // if precision and width or only precision is defined
655  if (isdigit(*fmt))
656  {
657  precision = std::atoi(fmt);
658  while (isdigit(*fmt))
659  {
660  fmt++;
661  }
662  if (std::ispunct(*fmt))
663  {
664  fmt++;
665  if (std::isdigit(*fmt))
666  {
667  width = std::atoi(fmt);
668  while (isdigit(*fmt))
669  {
670  fmt++;
671  }
672  }
673  // else the char is a real full stop so set the pointer back to full stop.
674  else
675  {
676  fmt--;
677  }
678  }
679  }
680  // if only a width and no precision defined
681  else if (std::ispunct(*fmt))
682  {
683  fmt++;
684  if (std::isdigit(*fmt))
685  {
686  width = std::atoi(fmt);
687  while (isdigit(*fmt))
688  {
689  fmt++;
690  }
691  }
692  // else the char is a real full stop so set the pointer back to full stop.
693  else
694  {
695  fmt--;
696  }
697  }
698  if (width != 0 && precision != 0)
699  {
700  msg << std::setprecision(precision) << std::left << std::setw(width) << arg << fmt;
701  }
702  else if (precision != 0)
703  {
704  msg << std::setprecision(precision) << arg << fmt;
705  }
706  else if (width != 0)
707  {
708  msg << std::left << std::setw(width) << arg << fmt;
709  }
710  else
711  {
712  msg << arg << fmt;
713  }
714  }
715 
716 
724  void createMessage(std::stringstream& msg, const char* message)
725  {
726  msg << message;
727  }
728 };
729 
739 
741 
742 //just emptying the functions is not sufficiently aggressive in disabling the actual (costly) comparison
743 #if !MERCURYDPM_ASSERTS
744 #define assert(e,...) assert(true,"")
745 #endif
746 
747 #ifdef MERCURYDPM_USE_MPI
748 #include "MpiContainer.h"
749 #endif
750 
751 #endif
#define MERCURYDPM_DEPRECATED
Definition: GeneralDefine.h:16
#define UNUSED
Definition: GeneralDefine.h:18
MatrixXd L
Definition: LLT_example.cpp:6
Logger< CG_LOGLEVEL > cgLogger
LL< Log::VERBOSE > VERBOSE
Verbose information.
Definition: Logger.cc:36
LL< Log::INFO > INFO
Info log level.
Definition: Logger.cc:34
LL< Log::DEBUG > DEBUG
Debug information.
Definition: Logger.cc:37
Log
The different loglevels.
Definition: Logger.h:109
@ FATAL
@ WARN
@ INFO
@ DEFAULT
@ ERROR
@ DEBUG
@ VERBOSE
LL< Log::FATAL > FATAL
Fatal log level.
Definition: Logger.cc:31
Flusher
The Logger class provides ability to write log messages in your own customized format.
Definition: Logger.h:94
#define MERCURYDPM_LOGLEVEL
Definition: Logger.h:17
LL< Log::ERROR > ERROR
Error log level.
Definition: Logger.cc:32
LoggerOutput * loggerOutput
Declaration of the output functions.
Definition: Logger.cc:262
Logger< MERCURYDPM_LOGLEVEL > logger
constexpr bool operator<=(const Log rhs, const Log lhs)
Internally used to filter on loglevel. Do not edit, as this is required for an optimised logger.
Definition: Logger.h:119
LL< Log::WARN > WARN
Warning log level.
Definition: Logger.cc:33
LL< Log::DEFAULT > DEFAULT
Default log level.
Definition: Logger.cc:35
Tag for template metaprogramming.
Definition: Logger.h:174
Default functions for output generation.
Definition: Logger.h:136
std::function< void(std::string, std::string, Flusher)> onVerbose
Definition: Logger.h:142
std::function< void(std::string, std::string, Flusher)> onFatal
Definition: Logger.h:138
std::function< void(std::string, std::string, Flusher)> onWarn
Definition: Logger.h:140
std::function< void(std::string, std::string, Flusher)> onError
Definition: Logger.h:139
std::function< void(std::string, std::string, Flusher)> onInfo
Definition: Logger.h:141
std::function< void(std::string, std::string, Flusher)> onDebug
Definition: Logger.h:143
the Logger class is the main class of the logger implementation. It holds all the functions which inv...
Definition: Logger.h:284
void createMessage(std::stringstream &msg, const char *fmt, Flusher arg, Args &&... args)
Overloaded version of createMessage to catch arguments of Flusher and suppress input flushing via std...
Definition: Logger.h:605
const std::string module
The module name of this actual logger.
Definition: Logger.h:290
void createMessage(std::stringstream &msg, const char *fmt, Arg1 &&arg)
Terminating case / Argument call. Overloaded function for a logger message with only one argument or ...
Definition: Logger.h:631
void createMessage(std::stringstream &msg, const char *message)
Terminating case / no argument call Overloaded function for a logger message without arguments.
Definition: Logger.h:724
Logger(const std::string name)
constructor
Definition: Logger.h:304
~Logger()=default
destructor
std::enable_if<!((L< LOGLEVEL) &&(MERCURYDPM_LOGLEVEL< LOGLEVEL)), void >::type operator()(const LL< LOGLEVEL > log, const char *format UNUSED, Args &&... arg UNUSED)
Log implementation of this function.
Definition: Logger.h:330
Flusher doFlush_
Can prevent the logger from flushing the buffer via std::endl. doFlush_ is set automatically based on...
Definition: Logger.h:295
void createMessage(std::stringstream &msg, const char *fmt, Arg1 &&arg, Args &&... args)
Edits the message to a certain format and writes it to a stringstream by recursively replacing all % ...
Definition: Logger.h:503
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bfloat16 log(const bfloat16 &a)
Definition: BFloat16.h:618
args
Definition: compute_granudrum_aor.py:143
type
Definition: compute_granudrum_aor.py:141
std::string string(const unsigned &i)
Definition: oomph_definitions.cc:286
string name
Definition: plotDoE.py:33
#define ASSERTS(expr)
Definition: oomph_parmetis_3.1.1/macros.h:118
std::string format(const std::string &str, const std::vector< std::string > &find, const std::vector< std::string > &replace)
Definition: openglsupport.cpp:217