MercuryDPM  Alpha
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Logger.h
Go to the documentation of this file.
1 //Copyright (c) 2013-2014, The MercuryDPM Developers Team. All rights reserved.
2 //For the list of developers, see <http://www.MercuryDPM.org/Team>.
3 //
4 //Redistribution and use in source and binary forms, with or without
5 //modification, are permitted provided that the following conditions are met:
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above copyright
9 // notice, this list of conditions and the following disclaimer in the
10 // documentation and/or other materials provided with the distribution.
11 // * Neither the name MercuryDPM nor the
12 // names of its contributors may be used to endorse or promote products
13 // derived from this software without specific prior written permission.
14 //
15 //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 //ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 //DISCLAIMED. IN NO EVENT SHALL THE MERCURYDPM DEVELOPERS TEAM BE LIABLE FOR ANY
19 //DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 //(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 //ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 //(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 #ifndef LOGGER_H
27 #define LOGGER_H
28 
29 #include <string>
30 #include <sstream>
31 #include <functional>
32 #include <type_traits>
33 #include "GeneralDefine.h"
34 
35 #ifndef MERCURY_LOGLEVEL
36 #define MERCURY_LOGLEVEL Log::DEFAULT
37 #endif
38 
40 #ifdef assert
41 #undef assert
42 //#error You included assert before the logger. Please use logger.assert() instead.
43 #endif
44 
45 #ifdef MERCURY_FORCE_ASSERTS
46 #define MERCURY_ASSERTS true
47 #else
48 #ifdef MERCURY_NO_ASSERTS
49 #define MERCURY_ASSERTS false
50 #else
51 #ifdef NDEBUG
52 #define MERCURY_ASSERTS false
53 #else
54 #define MERCURY_ASSERTS true
55 #endif
56 #endif
57 #endif
58 
59 /* IMPLEMENTATION DETAIL
60  * - by dducks
61  *
62  * A brief explanation how this class works. Beware, there is black magic
63  * going on here.
64  *
65  * So, the previous version of the Logger used the syntax
66  * Logger<Log::LEVEL> logger;
67  * logger.log(Log::LEVEL, "Message", args...);
68  * However, people didn't like the slightly large amount of log's in a single
69  * statement. Therefore, the operator() has been chosen. The next problem I
70  * personally had with the logger is the fact that it relied on a compile-time
71  * resolvable if-statement which could often (always) be optimised out. The
72  * problem however is that you would need optimisation enabled to have no
73  * speed penalty for loglevels below visibility.
74  *
75  * The problem however, is that we want to generate different code based on
76  * the first parameter; the loglevel. Because we actually want to generate
77  * code and people don't like the preprocessor, we have to use the template
78  * system to do this.
79  *
80  * One of the caveats of the template system is that it is only able to
81  * resolve templates based on parameters, not on values. It also allows you
82  * to ommit template parameters in case where the template parameters can be
83  * deduced.
84  *
85  * Based on these two rules, we now need not a value per loglevel, but a type.
86  * Therefore we use tagging, in which we use the class LL with a template argument
87  * to create the different tags. Our loglevel, the enum class Log, is considered
88  * to be a valid non-type template parameter - it must be either integral, enum
89  * (which is integral) or a pointer - so we can use it to tag a class to create
90  * diffent types.
91  *
92  * Now, our Logger instance has a template argument which is the loglevel
93  * associated with our Logger; anything of lesser priority is ignored; It
94  * also honours the symbol MERCURY_LOGLEVEL, and compares it to the lesser of
95  * the template argument and the preprocessor define.
96  *
97  * The operator() function now should be based on the template parameter of the
98  * logger class (or MERCURY_LOGLEVEL), and the loglevel of the current message.
99  * Because we can't depend on the value but just on the type, we have to do some
100  * magic. Now, we want the function to resolve to something that produces output
101  * if the level is high enough, or nothing at all if the level is of a too low
102  * priority for the current loglevel. For this we utilize std::enable_if<>
103  * to select the right function.
104  *
105  * Because in C++ a templated function which leads to ill-formed code upon
106  * instantiation is rejected and does NOT result in a compile error,
107  * std::enable_if allows us to make either the version WITH output or
108  * WITHOUT output, based on template parameters.
109  *
110  * As you can see below, the class LL is completely empty; However,
111  * there are a few instances; one for every loglevel. Now, remember,
112  * when using the logger, Log::DEFAULT resolves to the enum class value
113  * while DEFAULT resolves to the instance of class LL with template
114  * argument Log::DEFAULT.
115  *
116  * However, LL<Log::DEFAULT> differs in type from LL<Log::ERROR>, as the
117  * template argument is different. Because of deduction, the compiler can
118  * figure out the actual values for the template arguments. In case of
119  * operator(), the first argument is Log loglevel, which is deduced from
120  * the declaration;
121  *
122  * template<Log LogLevel, typename... Args>
123  * void operator(const LL<LogLevel> ll, std::string text, Args... args);
124  *
125  * Now, the template parameter LogLevel gets filled with Log::ERROR in case
126  * we give ERROR as the first argument, or Log::DEBUG with DEBUG as the first
127  * argument. Now, we want to resolve to two different functions, so as the
128  * return type we use std::enable_if which would lead to ill-formed code
129  * in case the predicate of the std::enable_if is false. So, based on the
130  * tag ll we now can select the different implementations.
131  *
132  * It then buffers everything into a stringstream. so as long as operator<<
133  * is defined properly for your object,. This then gets redirected towards the
134  * correct output channel.
135  *
136  * Please note that operator() is an inline, templated function. In case the
137  * function body is empty, this code is very, very likely to not emit any
138  * instructions at all. If you don't give arguments which have only non-const
139  * functions, the function call can be considered invariant which means it
140  * can completely be taken out, in the case it's a seperate function it just lowers
141  * the cost.
142  *
143  * As said, black magic below.
144  * END OF IMPLEMENTATION DETAIL
145  */
146 
155 enum class Log
156  : signed char
157  {
158  FATAL = -20, ERROR = -15, WARN = -10, INFO = -5, DEFAULT = 0, VERBOSE = 5, DEBUG = 10
159 };
160 
165 constexpr bool operator<=(const Log rhs, const Log lhs)
166 {
167  return ((static_cast<signed char>(rhs)) <= (static_cast<signed char>(lhs)));
168 }
169 
183 {
184 public:
185  std::function<void(std::string, std::string)> onFatal;
186  std::function<void(std::string, std::string)> onError;
187  std::function<void(std::string, std::string)> onWarn;
188  std::function<void(std::string, std::string)> onInfo;
189  std::function<void(std::string, std::string)> onVerbose;
190  std::function<void(std::string, std::string)> onDebug;
191 };
192 
201 extern LoggerOutput* loggerOutput;
202 
203 // Forward declaration..
204 template<Log L = Log::DEFAULT, bool ASSERTS = MERCURY_ASSERTS> class Logger;
205 
215 template<Log Level>
216 class LL
217 {
218 public:
219 };
220 
236 extern LL<Log::FATAL> FATAL;
248 extern LL<Log::ERROR> ERROR;
260 extern LL<Log::WARN> WARN;
271 extern LL<Log::INFO> INFO;
299 extern LL<Log::DEBUG> DEBUG;
300 
314 template<Log L, bool ASSERTS>
315 class Logger
316 {
317 private:
321  const std::string module;
322 
323 public:
324 
329  Logger(const std::string name)
330  : module(name)
331  {
332  }
336  ~Logger() {}
337 
338  /*
339  *
340  * \brief Log implementation of this function
341  *
342  * Actual implementation of the log function.
343  * At compile time evaluates to this implementation,
344  * or an empty body implementation, depending on the
345  * loglevel parameter of the Logger itself.
346  *
347  * \arg log Loglevel, either FATAL, ERROR, WARN, INFO, VERBOSE, DEBUG
348  * \arg format Message format, where % can be used as a placeholder for arguments.
349  * \arg arg... Any arguments which needs to be replaced.
350  */
351  template<Log LOGLEVEL, typename ... Args>
352  typename std::enable_if<!((L < LOGLEVEL) && (MERCURY_LOGLEVEL < LOGLEVEL)), void>::type
353  operator()(const LL<LOGLEVEL> log, const char* format, Args&&... arg)
354  {
355  std::stringstream msgstream;
356  createMessage(msgstream, format, arg...);
357  if (LOGLEVEL <= Log::FATAL)
358  {
359  loggerOutput->onFatal(module, msgstream.str());
360  }
361  else if (LOGLEVEL <= Log::ERROR)
362  {
363  loggerOutput->onError(module, msgstream.str());
364  }
365  else if (LOGLEVEL <= Log::WARN)
366  {
367  loggerOutput->onWarn(module, msgstream.str());
368  }
369  else if (LOGLEVEL <= Log::INFO)
370  {
371  loggerOutput->onInfo(module, msgstream.str());
372  }
373  else if (LOGLEVEL <= Log::VERBOSE)
374  {
375  loggerOutput->onVerbose(module, msgstream.str());
376  }
377  else
378  {
379  loggerOutput->onDebug(module, msgstream.str());
380  }
381  }
382 
384 
385  template<Log LOGLEVEL, typename... Args>
386  typename std::enable_if<L < LOGLEVEL || MERCURY_LOGLEVEL < LOGLEVEL, void>::type
387  operator()(const LL<LOGLEVEL> log UNUSED, const std::string& format UNUSED, Args&&... arg UNUSED) {
388 
389  }
390 
391  template<Log LOGLEVEL, typename... Args>
392  typename std::enable_if<L < LOGLEVEL || MERCURY_LOGLEVEL < LOGLEVEL, void>::type
393  operator()(const LL<LOGLEVEL> log UNUSED, const char * format UNUSED, Args&&... arg UNUSED) {
394 
395  }
396 
397 /* template<Log LOGLEVEL, typename... Args>
398  typename std::enable_if<L < LOGLEVEL && MERCURY_LOGLEVEL < LOGLEVEL, void>::type
399  operator()(const LL<LOGLEVEL> log, const char* format, Args&&... arg)
400  {
401 
402  }
403 
404  //std::string is sometimes convenient, but always slow, so where possible, don't convert the const char* to a string before converting it back
405  template<Log LOGLEVEL, typename... Args>
406  void operator()(const LL<LOGLEVEL> log, std::string& format, Args&&... arg)
407  {
408  (*this)(log, format.c_str(), arg...);
409  }*/
410 
411 
412  /*
413  *
414  * \brief Asserts on this logger
415  *
416  * Evaluates an assertion, prints an error message and aborts
417  * in case of a failure. This message can be redirected,
418  * and will be send to loggerOuptput->onFatal.
419  *
420  * \arg assertion An assertion, which must be true
421  * \arg format Message format, where % can be used as a placeholder for arguments.
422  * \arg arg... Any arguments which needs to be replaced.
423  */
424 
425 
426  //the conversion from "" to a std::sting is so slow, it takes 50% of the total run time for a release build...
427  template<typename... Args>
428  typename std::enable_if<(ASSERTS) && (sizeof...(Args) >= 0), void>::type
429  assert(bool assertion, const char* format, Args&&... arg)
430  {
431  assert_always(assertion, format, arg...);
432  }
433 
434  template<typename... Args>
435  typename std::enable_if<!((ASSERTS) && sizeof...(Args) >= 0), void>::type
436  assert(bool assertion, const char* format, Args&&... arg)
437  {
438  }
439 
440  template<typename... Args>
441  void assert(bool assertion, const std::string format, Args&&... arg)
442  {
443  assert(assertion, format.c_str(), arg...);
444  }
445 
446  template<typename... Args>
447  void assert_always(bool assertion, const char* format, Args&&... arg)
448  {
449  if (!assertion)
450  {
451  std::stringstream msgstream;
452  createMessage(msgstream, format, arg...);
453  loggerOutput->onFatal(module, msgstream.str());
454  }
455 
456  }
457 
458  template<typename... Args>
459  void assert_always(bool assertion, const std::string format, Args&&... arg)
460  {
461  assert_always(assertion, format.c_str(), arg...);
462  }
463 
468  template<typename... Args>
469  void log(const Log loglevel, const std::string& format, Args&&... arg)
470  {
471  if (loglevel <= L || loglevel <= MERCURY_LOGLEVEL)
472  {
473  std::stringstream msgstream;
474  createMessage(msgstream, format.c_str(), arg...);
475  if (loglevel <= Log::FATAL)
476  {
477  loggerOutput->onFatal(module, msgstream.str());
478  }
479  else if (loglevel <= Log::ERROR)
480  {
481  loggerOutput->onError(module, msgstream.str());
482  }
483  else if (loglevel <= Log::WARN)
484  {
485  loggerOutput->onWarn(module, msgstream.str());
486  }
487  else if (loglevel <= Log::INFO)
488  {
489  loggerOutput->onInfo(module, msgstream.str());
490  }
491  else if (loglevel <= Log::VERBOSE)
492  {
493  loggerOutput->onVerbose(module, msgstream.str());
494  }
495  else
496  {
497  loggerOutput->onDebug(module, msgstream.str());
498  }
499  }
500  }
501 private:
506  template<typename Arg1, typename... Args>
507  void createMessage(std::stringstream& msg, const char* fmt,
508  Arg1&& arg, Args&&... args)
509  {
510  bool doSkipNext = false;
511  while (*fmt != '%' || doSkipNext)
512  {
513  //Make sure we're not running past the end of our formatting string.
514  if (*fmt == '\0')
515  return;
516 
517  if (*fmt == '\\'&& !doSkipNext)
518  { //Escape for the %sign
519  doSkipNext = true;
520  fmt++;
521  }
522  else
523  {
524  msg << *fmt;
525  fmt++;
526  doSkipNext = false;
527  }
528  }
529 
530  fmt++; //Consume the % sign
531  msg << arg;
532  createMessage(msg, fmt, args...);//and recursively call ourselve / the method below.
533  }
534 
538  template<typename Arg1>
539  void createMessage(std::stringstream& msg, const char* fmt, Arg1&& arg)
540  {
541  bool doSkipNext = false;
542  while (*fmt != '%' || doSkipNext)
543  {
544  if (*fmt == '\0') // End of string
545  return;
546 
547  if (*fmt == '\\' && !doSkipNext)
548  { //Escape for the %sign and the \sign
549  doSkipNext = true;
550  fmt++;
551  }
552  else
553  { //invoke the replacement
554  msg << *fmt;
555  fmt++;
556  doSkipNext = false;
557  }
558  }
559  fmt++; //Consume the % sign
560  msg << arg << fmt;
561  }
562 
566  void createMessage(std::stringstream& msg, const char* message)
567  {
568  msg << message;
569  }
570 };
571 
581 
582 //just emptying the functions is not sufficiently aggressive in disabling the actual (costly) comparison
583 #if !MERCURY_ASSERTS
584 #define assert(e,...) assert(true,"")
585 #endif
586 
587 #endif
LoggerOutput * loggerOutput
Declaration of the output functions. If the output needs to be redirected, please swap the loggerOutp...
Definition: Logger.cc:143
See How to use the logger for details on how to use the logger.
Definition: Logger.h:204
LL< Log::INFO > INFO
Info log level.
Definition: Logger.cc:53
LL< Log::DEBUG > DEBUG
Debug information.
Definition: Logger.cc:56
LL< Log::ERROR > ERROR
Error log level.
Definition: Logger.cc:51
void createMessage(std::stringstream &msg, const char *message)
Terminating case / no argument call.
Definition: Logger.h:566
~Logger()
destructor
Definition: Logger.h:336
std::enable_if<!((L< LOGLEVEL)&&(MERCURY_LOGLEVEL< LOGLEVEL)), void >::type operator()(const LL< LOGLEVEL > log, const char *format, Args &&...arg)
Definition: Logger.h:353
std::function< void(std::string, std::string)> onError
Definition: Logger.h:186
void createMessage(std::stringstream &msg, const char *fmt, Arg1 &&arg)
Terminating case / argument call.
Definition: Logger.h:539
std::function< void(std::string, std::string)> onDebug
Definition: Logger.h:190
Logger(const std::string name)
constructor
Definition: Logger.h:329
std::function< void(std::string, std::string)> onFatal
Definition: Logger.h:185
Mdouble log(Mdouble Power)
Definition: ExtendedMath.cc:97
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:165
const std::string module
The module name of this actual logger.
Definition: Logger.h:321
std::function< void(std::string, std::string)> onVerbose
Definition: Logger.h:189
Default functions for output generation.
Definition: Logger.h:182
LL< Log::DEFAULT > DEFAULT
Default log level.
Definition: Logger.cc:54
#define UNUSED
Definition: GeneralDefine.h:39
std::function< void(std::string, std::string)> onWarn
Definition: Logger.h:187
LL< Log::VERBOSE > VERBOSE
Verbose information.
Definition: Logger.cc:55
std::function< void(std::string, std::string)> onInfo
Definition: Logger.h:188
#define assert(e,...)
Definition: Logger.h:584
Logger< MERCURY_LOGLEVEL > logger
Log
The different loglevels.
Definition: Logger.h:155
#define MERCURY_LOGLEVEL
Definition: Logger.h:36
LL< Log::FATAL > FATAL
Fatal log level.
Definition: Logger.cc:50
void createMessage(std::stringstream &msg, const char *fmt, Arg1 &&arg, Args &&...args)
Actual implementation to recursively replace all the '' signs by actual values.
Definition: Logger.h:507
LL< Log::WARN > WARN
Warning log level.
Definition: Logger.cc:52
Tag for template metaprogramming.
Definition: Logger.h:216