Version: SMASH-3.1
einhard.hpp
Go to the documentation of this file.
1 
61 #include <assert.h>
62 #include <iostream>
63 #include <iomanip>
64 #include <ctime>
65 #include <cstring>
66 #include <sstream>
67 #include <bitset>
68 
69 #include "stacktrace.h"
70 
71 // This C header is sadly required to check whether writing to a terminal or a file
72 #include <cstdio>
73 
74 #include <unistd.h> // for isatty
75 
76 #ifdef __GNUC__
77 #define EINHARD_ALWAYS_INLINE_ __attribute__((always_inline))
78 #else
79 #define EINHARD_ALWAYS_INLINE_
80 #endif
81 
82 // Error on MacOS:
83 // “thread-local storage is unsupported for the current target”
84 // To enable a workaround the EINHARD_NO_THREAD_LOCAL macro must be defined.
85 #ifdef __APPLE__
86 #define EINHARD_NO_THREAD_LOCAL 1
87 #endif
88 
92 namespace einhard
93 {
97  extern char const VERSION[];
98 
104  enum LogLevel
105  {
106  ALL,
113  OFF
114  };
115 
121  template <LogLevel> const char *getLogLevelString() noexcept;
125  const char *getLogLevelString( LogLevel level );
126 
135  LogLevel getLogLevel( const std::string &level );
136 
137  template <LogLevel> const char *colorForLogLevel() noexcept;
138 
142  template <typename Parent> class Color
143  {
144  public:
146  EINHARD_ALWAYS_INLINE_ Color() noexcept : reset( true )
147  {
148  }
152  {
153  return {false};
154  }
155  EINHARD_ALWAYS_INLINE_ char const *ansiCode() const noexcept
156  {
157  return Parent::ANSI();
158  }
159  EINHARD_ALWAYS_INLINE_ bool resetColor() const noexcept
160  {
161  return reset;
162  }
163 
164  private:
165  EINHARD_ALWAYS_INLINE_ Color( bool r ) noexcept : reset( r )
166  {
167  }
168  bool reset;
169  };
170 #define _COLOR( name, code ) \
171  struct name##_t_ \
172  { \
173  static char const *ANSI() noexcept \
174  { \
175  return "\33[" code "m"; \
176  } \
177  }; \
178  typedef Color<name##_t_> name
179 
180  _COLOR(DGray, "01;30");
181  _COLOR(Black, "00;30");
182  _COLOR(Red, "01;31");
183  _COLOR(DRed, "00;31");
184  _COLOR(Green, "01;32");
185  _COLOR(DGreen, "00;32");
186  _COLOR(Yellow, "01;33");
187  _COLOR(Orange, "00;33");
188  _COLOR(Blue, "01;34");
189  _COLOR(DBlue, "00;34");
190  _COLOR(Magenta, "01;35");
191  _COLOR(DMagenta,"00;35");
192  _COLOR(Cyan, "01;36");
193  _COLOR(DCyan, "00;36");
194  _COLOR(White, "01;37");
195  _COLOR(Gray, "00;37");
196  _COLOR(NoColor, "0" );
197 #undef _COLOR
198 
204  {
205  template <typename T> EINHARD_ALWAYS_INLINE_ DummyOutputFormatter &operator<<( const T & ) noexcept
206  {
207  return *this;
208  }
210  std::ostream &( * )( std::ostream & ) ) noexcept
211  {
212  return *this;
213  }
214  };
215 
217  {
218  private:
219  // Pointer to the thread_local stringstream (if enabled)
220  std::ostringstream *out;
221 #ifdef EINHARD_NO_THREAD_LOCAL
222  // without thread_local we simply use a local stringstream object
223  std::ostringstream realOut;
224 #endif
225  // The number of chars required for aligning
226  unsigned char indent;
227  // Whether to colorize the output
228  const bool colorize;
229  // Whether the color needs to be reset with the next operator<<
230  bool resetColor = false;
231 
232  public:
233  template <LogLevel VERBOSITY>
234  EINHARD_ALWAYS_INLINE_ UnconditionalOutput( bool colorize_, const char *areaName,
235  std::integral_constant<LogLevel, VERBOSITY> )
236  : colorize( colorize_ )
237  {
238  doInit<VERBOSITY>( areaName );
239  }
240 
241  template <typename T> UnconditionalOutput &operator<<( const Color<T> &col )
242  {
243  if( colorize )
244  {
245  *out << col.ansiCode();
246  resetColor = col.resetColor();
247  }
248  return *this;
249  }
250 
251  EINHARD_ALWAYS_INLINE_ UnconditionalOutput &operator<<( std::ostream &( *manip )( std::ostream & ) )
252  {
253  *out << manip;
254  return *this;
255  }
256 
257  template <typename T> EINHARD_ALWAYS_INLINE_ UnconditionalOutput &operator<<( const T &msg )
258  {
259  *out << msg;
260  checkColorReset();
261  return *this;
262  }
263 
264  void doCleanup(std::FILE *outfile = stdout) noexcept;
265 
266  protected:
267  EINHARD_ALWAYS_INLINE_ UnconditionalOutput( bool colorize_ ) : colorize( colorize_ )
268  {
269  }
270  template <LogLevel VERBOSITY> void doInit( const char *areaName );
272  };
277  {
278  private:
279  // the output file (normally used to switch from stdout to stderr)
280  std::FILE *const outfile;
281  // Whether output is enabled
282  const bool enabled;
283 
284  public:
285  OutputFormatter( const OutputFormatter & ) = delete;
287 
288  template <LogLevel VERBOSITY>
289  EINHARD_ALWAYS_INLINE_ OutputFormatter( bool enabled_, bool const colorize_,
290  const char *areaName,
291  std::integral_constant<LogLevel, VERBOSITY>,
292  std::FILE *outfile_ = stdout )
293  : UnconditionalOutput( colorize_ ), enabled( enabled_ ), outfile( outfile_ )
294  {
295  if( enabled )
296  {
297  doInit<VERBOSITY>( areaName );
298  }
299  }
300 
301  template <typename T> OutputFormatter &operator<<( const Color<T> &col )
302  {
303  if( enabled )
304  {
306  }
307  return *this;
308  }
309 
310  EINHARD_ALWAYS_INLINE_ OutputFormatter &operator<<( std::ostream &( *manip )( std::ostream & ) )
311  {
312  if( enabled )
313  {
315  }
316  return *this;
317  }
318 
319  template <typename T> EINHARD_ALWAYS_INLINE_ OutputFormatter &operator<<( const T &msg )
320  {
321  if( enabled )
322  {
324  }
325  return *this;
326  }
327 
330  {
331  if( enabled )
332  {
333  doCleanup( outfile );
334  }
335  }
336  };
337 
347  template<LogLevel MAX = ALL> class Logger
348  {
349  private:
350  char areaName[32 - sizeof( LogLevel ) - sizeof( bool )] = {'\0'};
354 
355  public:
363  {
364  // use some, sadly not c++-ways to figure out whether we are writing ot a terminal
365  // only colorize when we are writing ot a terminal
366  colorize_stdout = isatty( fileno( stdout ) );
367  colorize_stderr = isatty( fileno( stderr ) );
368  };
375  Logger( const LogLevel verbosity, const bool colorize )
376  : verbosity( verbosity ), colorize_stdout( colorize ), colorize_stderr( colorize ) {};
377 
387  void setAreaName( const char *name )
388  {
389  assert( name );
390  std::strncpy( &areaName[0], name, sizeof( areaName ) - 1 );
391  areaName[sizeof( areaName ) - 1] = '\0';
392  }
394  void setAreaName( const std::string &name )
395  {
396  setAreaName(name.c_str());
397  }
398 
400 #ifdef NDEBUG
401  DummyOutputFormatter trace() const noexcept
402  {
403  return DummyOutputFormatter();
404  }
405 
406  template <typename... Ts> void trace( Ts &&... ) const noexcept
407  {
408  }
409 #else
411  {
412  return {isEnabled<TRACE>(), colorize_stdout, areaName,
413  std::integral_constant<LogLevel, TRACE>()};
414  }
415 
416  template <typename... Ts> void trace( Ts &&... args ) const noexcept
417  {
418  if( isEnabled<TRACE>() )
419  {
421  std::integral_constant<LogLevel, TRACE>()};
422  auto &&unused = {&( o << args )...};
423  o.doCleanup();
424  }
425  }
426 #endif
428 #ifdef NDEBUG
429  DummyOutputFormatter debug() const noexcept
430  {
431  return DummyOutputFormatter();
432  }
433  template <typename... Ts> void debug( Ts &&... ) const noexcept
434  {
435  }
436 #else
438  {
439  return {isEnabled<DEBUG>(), colorize_stdout, areaName,
440  std::integral_constant<LogLevel, DEBUG>()};
441  }
442  template <typename... Ts> void debug( Ts &&... args ) const noexcept
443  {
444  if( isEnabled<DEBUG>() )
445  {
447  std::integral_constant<LogLevel, DEBUG>()};
448  auto &&unused = {&( o << args )...};
449  o.doCleanup();
450  }
451  }
452 #endif
455  {
456  return {isEnabled<INFO>(), colorize_stdout, areaName,
457  std::integral_constant<LogLevel, INFO>()};
458  }
459  template <typename... Ts> void info( Ts &&... args ) const noexcept
460  {
461  if( isEnabled<INFO>() )
462  {
464  std::integral_constant<LogLevel, INFO>()};
465  auto &&unused = {&( o << args )...};
466  o.doCleanup();
467  }
468  }
471  {
472  return {isEnabled<WARN>(), colorize_stderr, areaName,
473  std::integral_constant<LogLevel, WARN>(), stderr};
474  }
475  template <typename... Ts> void warn( Ts &&... args ) const noexcept
476  {
477  if( isEnabled<WARN>() )
478  {
480  std::integral_constant<LogLevel, WARN>()};
481  auto &&unused = {&( o << args )...};
482  o.doCleanup(stderr);
483  }
484  }
487  {
488  return {isEnabled<ERROR>(), colorize_stderr, areaName,
489  std::integral_constant<LogLevel, ERROR>(), stderr};
490  }
491  template <typename... Ts> void error( Ts &&... args ) const noexcept
492  {
493  if( isEnabled<ERROR>() )
494  {
496  std::integral_constant<LogLevel, ERROR>()};
497  auto &&unused = {&( o << args )...};
498  o.doCleanup(stderr);
499  }
500  }
503  {
504  return {isEnabled<FATAL>(), colorize_stderr, areaName,
505  std::integral_constant<LogLevel, FATAL>(), stderr};
506  }
508  template <typename... Ts> void fatal( Ts &&... args ) const noexcept
509  {
510  if( isEnabled<FATAL>() )
511  {
513  std::integral_constant<LogLevel, FATAL>()};
514  auto &&unused = {&( o << args )...};
515  o.doCleanup(stderr);
516  print_stacktrace(stderr, 63, 2);
517  }
518  }
519 
520  template <LogLevel LEVEL> bool isEnabled() const noexcept
521  {
522 #ifdef NDEBUG
523  if( LEVEL == DEBUG || LEVEL == TRACE ) {
524  return false;
525  }
526 #endif
527  return ( MAX <= LEVEL && verbosity <= LEVEL );
528  }
529 
535  inline void setVerbosity( LogLevel verbosity ) noexcept
536  {
537  this->verbosity = verbosity;
538  }
545  inline LogLevel getVerbosity() const noexcept
546  {
547  return this->verbosity;
548  }
552  inline char const * getVerbosityString( ) const
553  {
554  return getLogLevelString(verbosity);
555  }
559  void setColorize( bool colorize ) noexcept
560  {
561  this->colorize_stdout = colorize;
562  this->colorize_stderr = colorize;
563  }
567  bool getColorize() const noexcept
568  {
569  return this->colorize_stdout || this->colorize_stderr;
570  }
571  };
572 }
573 
574 // vim: ts=4 sw=4 tw=100 noet
A stream modifier that allows to colorize the log output.
Definition: einhard.hpp:143
EINHARD_ALWAYS_INLINE_ Color(bool r) noexcept
Definition: einhard.hpp:165
EINHARD_ALWAYS_INLINE_ bool resetColor() const noexcept
Definition: einhard.hpp:159
EINHARD_ALWAYS_INLINE_ Color() noexcept
The default color modifier only affects the next object in the stream.
Definition: einhard.hpp:146
EINHARD_ALWAYS_INLINE_ char const * ansiCode() const noexcept
Definition: einhard.hpp:155
EINHARD_ALWAYS_INLINE_ Color< Parent > operator~() const noexcept
With the ~ operator the color modifier affects the rest of the stream (or until another color object ...
Definition: einhard.hpp:151
A Logger object can be used to output messages to stdout.
Definition: einhard.hpp:348
Logger(const LogLevel verbosity=WARN)
Create a new Logger object.
Definition: einhard.hpp:362
EINHARD_ALWAYS_INLINE_ void setAreaName(const std::string &name)
Definition: einhard.hpp:394
void info(Ts &&... args) const noexcept
Definition: einhard.hpp:459
void setVerbosity(LogLevel verbosity) noexcept
Modify the verbosity of the Logger.
Definition: einhard.hpp:535
bool colorize_stderr
Definition: einhard.hpp:353
OutputFormatter error() const
Access to the error message stream.
Definition: einhard.hpp:486
LogLevel verbosity
Definition: einhard.hpp:351
void fatal(Ts &&... args) const noexcept
Will print stacktrace.
Definition: einhard.hpp:508
void debug(Ts &&... args) const noexcept
Definition: einhard.hpp:442
Logger(const LogLevel verbosity, const bool colorize)
Create a new Logger object explicitly selecting whether to colorize the output or not.
Definition: einhard.hpp:375
OutputFormatter fatal() const
Access to the fatal message stream.
Definition: einhard.hpp:502
char const * getVerbosityString() const
Retrieve a human readable representation of the current log level.
Definition: einhard.hpp:552
OutputFormatter info() const
Access to the info message stream.
Definition: einhard.hpp:454
void error(Ts &&... args) const noexcept
Definition: einhard.hpp:491
bool isEnabled() const noexcept
Definition: einhard.hpp:520
void warn(Ts &&... args) const noexcept
Definition: einhard.hpp:475
LogLevel getVerbosity() const noexcept
Retrieve the current log level.
Definition: einhard.hpp:545
OutputFormatter debug() const
Access to the debug message stream.
Definition: einhard.hpp:437
OutputFormatter trace() const
Access to the trace message stream.
Definition: einhard.hpp:410
void setAreaName(const char *name)
Set an area name.
Definition: einhard.hpp:387
char areaName[32 - sizeof(LogLevel) - sizeof(bool)]
Definition: einhard.hpp:350
void setColorize(bool colorize) noexcept
Select whether the output stream should be colorized.
Definition: einhard.hpp:559
OutputFormatter warn() const
Access to the warning message stream.
Definition: einhard.hpp:470
void trace(Ts &&... args) const noexcept
Definition: einhard.hpp:416
bool colorize_stdout
Definition: einhard.hpp:352
bool getColorize() const noexcept
Check whether the output stream is colorized.
Definition: einhard.hpp:567
A wrapper for the output stream taking care proper formatting and colorization of the output.
Definition: einhard.hpp:277
OutputFormatter(const OutputFormatter &)=delete
EINHARD_ALWAYS_INLINE_ OutputFormatter(bool enabled_, bool const colorize_, const char *areaName, std::integral_constant< LogLevel, VERBOSITY >, std::FILE *outfile_=stdout)
Definition: einhard.hpp:289
std::FILE *const outfile
Definition: einhard.hpp:280
OutputFormatter & operator<<(const Color< T > &col)
Definition: einhard.hpp:301
OutputFormatter(OutputFormatter &&)=default
EINHARD_ALWAYS_INLINE_ OutputFormatter & operator<<(const T &msg)
Definition: einhard.hpp:319
EINHARD_ALWAYS_INLINE_ ~OutputFormatter()
Definition: einhard.hpp:329
EINHARD_ALWAYS_INLINE_ OutputFormatter & operator<<(std::ostream &(*manip)(std::ostream &))
Definition: einhard.hpp:310
std::ostringstream * out
Definition: einhard.hpp:220
EINHARD_ALWAYS_INLINE_ UnconditionalOutput(bool colorize_, const char *areaName, std::integral_constant< LogLevel, VERBOSITY >)
Definition: einhard.hpp:234
EINHARD_ALWAYS_INLINE_ UnconditionalOutput(bool colorize_)
Definition: einhard.hpp:267
void doInit(const char *areaName)
EINHARD_ALWAYS_INLINE_ UnconditionalOutput & operator<<(const T &msg)
Definition: einhard.hpp:257
EINHARD_ALWAYS_INLINE_ UnconditionalOutput & operator<<(std::ostream &(*manip)(std::ostream &))
Definition: einhard.hpp:251
void doCleanup(std::FILE *outfile=stdout) noexcept
UnconditionalOutput & operator<<(const Color< T > &col)
Definition: einhard.hpp:241
#define EINHARD_ALWAYS_INLINE_
Definition: einhard.hpp:79
#define _COLOR(name, code)
Definition: einhard.hpp:170
This namespace contains all objects required for logging using Einhard.
Definition: einhard.hpp:93
LogLevel getLogLevel(const std::string &level)
Compares the string level against the strings for LogLevel and returns the one it matches.
const char * colorForLogLevel() noexcept
char const VERSION[]
Version string of the Einhard library.
LogLevel
Specification of the message severity.
Definition: einhard.hpp:105
@ TRACE
The lowes severity for messages describing the program flow.
Definition: einhard.hpp:107
@ OFF
If selected no messages will be output.
Definition: einhard.hpp:113
@ WARN
Warning messages.
Definition: einhard.hpp:110
@ ALL
Log all message.
Definition: einhard.hpp:106
@ ERROR
Non-fatal errors.
Definition: einhard.hpp:111
@ DEBUG
Debug messages.
Definition: einhard.hpp:108
@ FATAL
Messages that indicate terminal application failure.
Definition: einhard.hpp:112
@ INFO
Messages of informational nature, expected processing time e.g.
Definition: einhard.hpp:109
const char * getLogLevelString() noexcept
Retrieve a human readable representation of the given log level value.
A minimal class that implements the output stream operator to do nothing.
Definition: einhard.hpp:204
EINHARD_ALWAYS_INLINE_ DummyOutputFormatter & operator<<(std::ostream &(*)(std::ostream &)) noexcept
Definition: einhard.hpp:209
EINHARD_ALWAYS_INLINE_ DummyOutputFormatter & operator<<(const T &) noexcept
Definition: einhard.hpp:205