clutchlog  0.8
clutchlog.h
Go to the documentation of this file.
1 #ifndef __CLUTCHLOG_H__
2 #define __CLUTCHLOG_H__
3 #pragma once
4 
6 #include <ciso646>
7  #ifdef FSEXPERIMENTAL
8  #include <experimental/filesystem>
9  namespace fs = std::experimental::filesystem;
10 #else
11  #include <filesystem>
12  namespace fs = std::filesystem;
13 #endif
14 
15 #include <iostream>
16 #include <sstream>
17 #include <fstream>
18 #include <cassert>
19 #include <cstdlib>
20 #include <string>
21 #include <limits>
22 #include <regex>
23 #include <map>
24 
26 #if __has_include(<execinfo.h>) && __has_include(<stdlib.h>) && __has_include(<libgen.h>)
27  #include <execinfo.h> // execinfo
28  #include <stdlib.h> // getenv
29  #include <libgen.h> // basename
30  #define CLUTCHLOG_HAVE_UNIX_SYSINFO 1
31 #else
32  #define CLUTCHLOG_HAVE_UNIX_SYSINFO 0
33 #endif
34 
35 #if __has_include(<sys/ioctl.h>) && __has_include(<stdio.h>) && __has_include(<unistd.h>)
36  #include <sys/ioctl.h>
37  #include <stdio.h>
38  #include <unistd.h>
39  #define CLUTCHLOG_HAVE_UNIX_SYSIOCTL 1
40 #else
41  #define CLUTCHLOG_HAVE_UNIX_SYSIOCTL 0
42 #endif
43 
44 
45 /**********************************************************************
46  * Enable by default in Debug builds.
47  **********************************************************************/
48 #ifndef WITH_CLUTCHLOG
49  #ifndef NDEBUG
50  #define WITH_CLUTCHLOG
52  #endif
53 #endif
54 
55 /**********************************************************************
56  * Macros definitions
57  **********************************************************************/
58 #ifdef WITH_CLUTCHLOG
59 
63 #ifndef CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG
64  #define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress
66 #endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT
67 
74 #define CLUTCHLOC __FILE__, __FUNCTION__, __LINE__
76 
78 #ifndef NDEBUG
79  #define CLUTCHLOG( LEVEL, WHAT ) do { \
80  auto& clutchlog__logger = clutchlog::logger(); \
81  std::ostringstream clutchlog__msg ; clutchlog__msg << WHAT; \
82  clutchlog__logger.log(clutchlog::level::LEVEL, clutchlog__msg.str(), CLUTCHLOC); \
83  } while(0)
84 #else // not Debug build.
85  #define CLUTCHLOG( LEVEL, WHAT ) do { \
86  if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
87  auto& clutchlog__logger = clutchlog::logger(); \
88  std::ostringstream clutchlog__msg ; clutchlog__msg << WHAT; \
89  clutchlog__logger.log(clutchlog::level::LEVEL, clutchlog__msg.str(), CLUTCHLOC); \
90  } \
91  } while(0)
92 #endif // NDEBUG
93 
95 #ifndef NDEBUG
96  #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do { \
97  auto& clutchlog__logger = clutchlog::logger(); \
98  clutchlog__logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
99  CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
100  } while(0)
101 #else // not Debug build.
102  #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do { \
103  if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
104  auto& clutchlog__logger = clutchlog::logger(); \
105  clutchlog__logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
106  CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
107  } \
108  } while(0)
109 #endif // NDEBUG
110 
112 #ifndef NDEBUG
113  #define CLUTCHFUNC( LEVEL, FUNC, ... ) do { \
114  auto& clutchlog__logger = clutchlog::logger(); \
115  clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
116  if(clutchlog__scope.matches) { \
117  FUNC(__VA_ARGS__); \
118  } \
119  } while(0)
120 #else // not Debug build.
121  #define CLUTCHFUNC( LEVEL, FUNC, ... ) do { \
122  if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
123  auto& clutchlog__logger = clutchlog::logger(); \
124  clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
125  if(clutchlog__scope.matches) { \
126  FUNC(__VA_ARGS__); \
127  } \
128  } \
129  } while(0)
130 #endif // NDEBUG
131 
133 #ifndef NDEBUG
134  #define CLUTCHCODE( LEVEL, ... ) do { \
135  auto& clutchlog__logger = clutchlog::logger(); \
136  clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
137  if(clutchlog__scope.matches) { \
138  __VA_ARGS__ \
139  } \
140  } while(0)
141 #else // not Debug build.
142  #define CLUTCHCODE( LEVEL, CODE ) do { \
143  if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
144  auto& clutchlog__logger = clutchlog::logger(); \
145  clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
146  if(clutchlog__scope.matches) { \
147  CODE \
148  } \
149  } \
150  } while(0)
151 #endif // NDEBUG
152 
155 #else // not WITH_CLUTCHLOG
156  // Disabled macros can still be called in Release builds.
157  #define CLUTCHLOG( LEVEL, WHAT ) do {/*nothing*/} while(0)
158  #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do {/*nothing*/} while(0)
159  #define CLUTCHFUNC( LEVEL, FUNC, ... ) do {/*nothing*/} while(0)
160  #define CLUTCHCODE( LEVEL, CODE ) do {/*nothing*/} while(0)
161 #endif // WITH_CLUTCHLOG
162 
163 /**********************************************************************
164  * Implementation
165  **********************************************************************/
166 
167 #ifdef WITH_CLUTCHLOG
168 
176 {
177  protected:
178 
181  #ifndef NDEBUG
182  #ifndef CLUTCHLOG_DEFAULT_FORMAT
183  #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1 // Enables: name, depth and depth_marks
185  #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1 // Enables: hfill
186  #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg} {hfill} {func} @ {file}:{line}\n"
187  #else
188  #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
189  #endif
190  #else
191  #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
192  #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg} {hfill} {func} @ {file}:{line}\n"
193  #else
194  #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
195  #endif
196  #endif
197  #endif
198  #else
199  #ifndef CLUTCHLOG_DEFAULT_FORMAT
200  #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
202  #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter}:{depth_marks} {msg} {hfill} {func}\n"
203  #else
204  #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg}\t\t\t\t\t{func}\n"
205  #endif
206  #endif
207  #endif
208  static inline std::string default_format = CLUTCHLOG_DEFAULT_FORMAT;
210 
211  #ifndef NDEBUG
212  #ifndef CLUTCHDUMP_DEFAULT_FORMAT
213  #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
215  #define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth}) @ {file}:{line}"
216  #else
217  #define CLUTCHDUMP_DEFAULT_FORMAT "# {level} in {func} @ {file}:{line}"
218  #endif
219  #endif // CLUTCHDUMP_DEFAULT_FORMAT
220  #else
221  #ifndef CLUTCHDUMP_DEFAULT_FORMAT
222  #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
224  #define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth})"
225  #else
226  #define CLUTCHDUMP_DEFAULT_FORMAT "# {level} in {func}"
227  #endif
228  #endif // CLUTCHDUMP_DEFAULT_FORMAT
229  #endif
230  static inline std::string dump_default_format = CLUTCHDUMP_DEFAULT_FORMAT;
232 
233  #ifndef CLUTCHDUMP_DEFAULT_SEP
234  #define CLUTCHDUMP_DEFAULT_SEP "\n"
236  #endif // CLUTCHDUMP_DEFAULT_SEP
237  static inline std::string dump_default_sep = CLUTCHDUMP_DEFAULT_SEP;
239 
240  #ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
241  #define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
243  #endif // CLUTCHLOG_DEFAULT_DEPTH_MARK
244  static inline std::string default_depth_mark = CLUTCHLOG_DEFAULT_DEPTH_MARK;
246 
247  #ifndef CLUTCHLOG_STRIP_CALLS
248  #define CLUTCHLOG_STRIP_CALLS 5
250  #endif // CLUTCHLOG_STRIP_CALLS
251  static inline unsigned int default_strip_calls = CLUTCHLOG_STRIP_CALLS;
253 
254  #ifndef CLUTCHLOG_HFILL_MARK
255  #define CLUTCHLOG_HFILL_MARK '.'
257  #endif // CLUTCHLOG_HFILL_MARK
258  static inline char default_hfill_char = CLUTCHLOG_HFILL_MARK;
260  /* @} */
261 
262 
263  public:
273  static clutchlog& logger()
274  {
275  static clutchlog instance;
276  return instance;
277  }
278 
280  enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
281 
294  class fmt {
295  public:
297  enum class fg {
298  black = 30,
299  red = 31,
300  green = 32,
301  yellow = 33,
302  blue = 34,
303  magenta = 35,
304  cyan = 36,
305  white = 37,
306  none
307  } fore;
308 
310  enum class bg {
311  black = 40,
312  red = 41,
313  green = 42,
314  yellow = 43,
315  blue = 44,
316  magenta = 45,
317  cyan = 46,
318  white = 47,
319  none
320  } back;
321 
323  enum class typo {
324  reset = 0,
325  bold = 1,
326  underline = 4,
327  inverse = 7,
328  none
329  } style;
330 
332  fmt() : fore(fg::none), back(bg::none), style(typo::none) {}
333 
336  fmt( fg f, bg b = bg::none, typo s = typo::none) : fore(f), back(b), style(s) {}
337  fmt( fg f, typo s , bg b = bg::none) : fore(f), back(b), style(s) {}
338  fmt( bg b, fg f = fg::none, typo s = typo::none) : fore(f), back(b), style(s) {}
339  fmt( bg b, typo s , fg f = fg::none) : fore(f), back(b), style(s) {}
340  fmt(typo s, fg f = fg::none, bg b = bg::none) : fore(f), back(b), style(s) {}
341  fmt(typo s, bg b , fg f = fg::none) : fore(f), back(b), style(s) {}
344  protected:
346  std::ostream& print_on( std::ostream& os) const
347  {
348  std::vector<int> codes; codes.reserve(3);
349  if(this->fore != fg::none) { codes.push_back(static_cast<int>(this->fore ));}
350  if(this->back != bg::none) { codes.push_back(static_cast<int>(this->back ));}
351  if(this->style != typo::none) { codes.push_back(static_cast<int>(this->style));}
352  if(codes.size() == 0) {return os;}
353 
354  os << "\033[";
355  assert(codes.size() > 0);
356  os << codes[0];
357  for(size_t i=1; i < codes.size(); ++i) {
358  os << ";" << codes[i];
359  }
360  os << "m";
361  return os;
362  }
363 
364  public:
376  friend std::ostream& operator<<(std::ostream& os, const fmt& fmt)
377  {
378  return fmt.print_on(os);
379  }
380 
391  std::string operator()( const std::string& msg ) const
392  {
393  std::ostringstream os;
394  this->print_on(os);
395  fmt reset(fmt::typo::reset);
396  os << msg;
397  reset.print_on(os);
398  return os.str();
399  }
400  }; // fmt class
401 
408  public:
409  clutchlog(clutchlog const&) = delete;
410  void operator=(clutchlog const&) = delete;
411 
412  private:
413  clutchlog() :
414  // system, main, log
416  _level_word({
417  {level::critical,"Critical"},
418  {level::error ,"Error"},
419  {level::warning ,"Warning"},
420  {level::progress,"Progress"},
421  {level::note ,"Note"},
422  {level::info ,"Info"},
423  {level::debug ,"Debug"},
424  {level::xdebug ,"XDebug"}
425  }),
426  _level_fmt({
427  {level::critical,fmt(fmt::fg::red, fmt::typo::underline)},
428  {level::error ,fmt(fmt::fg::red, fmt::typo::bold)},
429  {level::warning ,fmt(fmt::fg::magenta, fmt::typo::bold)},
430  {level::progress,fmt()},
431  {level::note ,fmt()},
432  {level::info ,fmt()},
433  {level::debug ,fmt()},
434  {level::xdebug ,fmt()}
435  }),
439  _out(&std::clog),
440 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
441  _depth(std::numeric_limits<size_t>::max() - _strip_calls),
442  _depth_mark(clutchlog::default_depth_mark),
443 #endif
444  _stage(level::error),
445  _in_file(".*"),
446  _in_func(".*"),
447  _in_line(".*")
448  {
449  // Reverse the level->word map into a word->level map.
450  for(auto& lw : _level_word) {
451  _word_level[lw.second] = lw.first;
452  }
453 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
454  struct winsize w;
455  ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
456  _nb_columns = w.ws_col;
457 #endif
458  }
459 
460  protected:
462  const size_t _strip_calls;
464  const std::map<level,std::string> _level_word;
466  std::map<std::string,level> _word_level;
468  std::map<level,fmt> _level_fmt;
470  std::string _format_log;
472  std::string _format_dump;
476  std::ostream* _out;
477 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
478 
479  size_t _depth;
481  std::string _depth_mark;
482 #endif
483 
486  std::regex _in_file;
488  std::regex _in_func;
490  std::regex _in_line;
491 
492 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
493 
494  static const size_t _max_buffer = 4096;
495 #endif
496 
497 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
498 
499  size_t _nb_columns;
500 #endif
501 
503  public:
504 
508  void format(const std::string& format) {_format_log = format;}
511  std::string format() const {return _format_log;}
512 
514  void format_comment(const std::string& format) {_format_dump = format;}
516  std::string format_comment() const {return _format_dump;}
517 
519  void out(std::ostream& out) {_out = &out;}
521  std::ostream& out() {return *_out;}
522 
523 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
524  void depth(size_t d) {_depth = d;}
527  size_t depth() const {return _depth;}
528 
530  void depth_mark(const std::string mark) {_depth_mark = mark;}
532  std::string depth_mark() const {return _depth_mark;}
533 #endif
534 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
535  void hfill_mark(const char mark) {_hfill_char = mark;}
538  char hfill_mark() const {return _hfill_char;}
539 #endif
540 
542  void threshold(level l) {_stage = l;}
544  void threshold(const std::string& l) {_stage = this->level_of(l);}
546  level threshold() const {return _stage;}
548  const std::map<std::string,level>& levels() const { return _word_level;}
549 
554  level level_of(const std::string name)
555  {
556  const auto ilevel = _word_level.find(name);
557  if( ilevel != std::end(_word_level)) {
558  return ilevel->second;
559  } else {
560  throw std::out_of_range("'" + name + "' is not a valid log level name");
561  }
562  }
563 
565  void file(std::string file) {_in_file = file;}
567  void func(std::string func) {_in_func = func;}
569  void line(std::string line) {_in_line = line;}
570 
572  void location(
573  const std::string& in_file,
574  const std::string& in_function=".*",
575  const std::string& in_line=".*"
576  )
577  {
578  file(in_file);
579  func(in_function);
580  line(in_line);
581  }
582 
587  template<class ... FMT>
588  void style(level stage, FMT... styles) { this->style(stage,fmt(styles...)); }
590  void style(level stage, fmt style) { _level_fmt.at(stage) = style; }
592  fmt style(level stage) const { return _level_fmt.at(stage); }
593 
596  public:
597 
601  struct scope_t {
604  bool matches;
607 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
608 
609  size_t depth;
610 #endif
611 
612  bool there;
615  matches(false),
616  stage(level::xdebug),
618  depth(0),
619 #endif
620  there(false)
621  {}
622  }; // scope_t
623 
624 
627  const level& stage,
628  const std::string& file,
629  const std::string& func,
630  const size_t line
631  ) const
632  {
633  scope_t scope; // False scope by default.
634 
635  /***** Log level stage *****/
636  // Test stage first, because it's fastest.
637  scope.stage = stage;
638  if(not (scope.stage <= _stage)) {
639  // Bypass useless computations if no match
640  // because of the stage.
641  return scope;
642  }
643 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
644  /***** Stack depth *****/
645  // Backtrace in second, quite fast.
646  size_t stack_depth;
647  void *buffer[_max_buffer];
648  stack_depth = backtrace(buffer, _max_buffer);
649  scope.depth = stack_depth;
650  if(not (scope.depth <= _depth + _strip_calls)) {
651  // Bypass if no match.
652  return scope;
653  }
654 #endif
655 
656  /***** Location *****/
657  // Location last, slowest.
658  std::ostringstream sline; sline << line;
659  scope.there =
660  std::regex_search(file, _in_file)
661  and std::regex_search(func, _in_func)
662  and std::regex_search(sline.str(), _in_line);
663 
664  // No need to retest stage and depth, which are true here.
665  scope.matches = scope.there;
666 
667  return scope;
668  } // locate
669 
677  std::string replace(
678  const std::string& form,
679  const std::string& mark,
680  const std::string& tag
681  ) const
682  {
683  // Useless debug code, unless something fancy would be done with name tags.
684  // std::regex re;
685  // try {
686  // re = std::regex(mark);
687  //
688  // } catch(const std::regex_error& e) {
689  // std::cerr << "ERROR with a regular expression \"" << mark << "\": ";
690  // switch(e.code()) {
691  // case std::regex_constants::error_collate:
692  // std::cerr << "the expression contains an invalid collating element name";
693  // break;
694  // case std::regex_constants::error_ctype:
695  // std::cerr << "the expression contains an invalid character class name";
696  // break;
697  // case std::regex_constants::error_escape:
698  // std::cerr << "the expression contains an invalid escaped character or a trailing escape";
699  // break;
700  // case std::regex_constants::error_backref:
701  // std::cerr << "the expression contains an invalid back reference";
702  // break;
703  // case std::regex_constants::error_brack:
704  // std::cerr << "the expression contains mismatched square brackets ('[' and ']')";
705  // break;
706  // case std::regex_constants::error_paren:
707  // std::cerr << "the expression contains mismatched parentheses ('(' and ')')";
708  // break;
709  // case std::regex_constants::error_brace:
710  // std::cerr << "the expression contains mismatched curly braces ('{' and '}')";
711  // break;
712  // case std::regex_constants::error_badbrace:
713  // std::cerr << "the expression contains an invalid range in a {} expression";
714  // break;
715  // case std::regex_constants::error_range:
716  // std::cerr << "the expression contains an invalid character range (e.g. [b-a])";
717  // break;
718  // case std::regex_constants::error_space:
719  // std::cerr << "there was not enough memory to convert the expression into a finite state machine";
720  // break;
721  // case std::regex_constants::error_badrepeat:
722  // std::cerr << "one of *?+{ was not preceded by a valid regular expression";
723  // break;
724  // case std::regex_constants::error_complexity:
725  // std::cerr << "the complexity of an attempted match exceeded a predefined level";
726  // break;
727  // case std::regex_constants::error_stack:
728  // std::cerr << "there was not enough memory to perform a match";
729  // break;
730  // default:
731  // std::cerr << "unknown error";
732  // }
733  // std::cerr << std::endl;
734  // throw;
735  // } // catch
736 
737  const std::regex re(mark);
738  return std::regex_replace(form, re, tag);
739  }
740 
742  std::string replace(
743  const std::string& form,
744  const std::string& mark,
745  const size_t tag
746  ) const
747  {
748  std::ostringstream stag; stag << tag;
749  return replace(form, mark, stag.str());
750  }
751 
753  std::string format(
754  std::string format,
755  const std::string& what,
757  const std::string& name,
758 #endif
759  const level& stage,
760  const std::string& file,
761  const std::string& func,
762  const size_t line
764  ,
765  const size_t depth
766 #endif
767  ) const
768  {
769  format = replace(format, "\\{msg\\}", what);
770  format = replace(format, "\\{file\\}", file);
771  format = replace(format, "\\{func\\}", func);
772  format = replace(format, "\\{line\\}", line);
773 
774  format = replace(format, "\\{level\\}", _level_word.at(stage));
775  std::string letter(1, _level_word.at(stage).at(0)); // char -> string
776  format = replace(format, "\\{level_letter\\}", letter);
777 
778 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
779  format = replace(format, "\\{name\\}", name);
780  format = replace(format, "\\{depth\\}", depth - _strip_calls);
781 
782  std::ostringstream chevrons;
783  for(size_t i = _strip_calls; i < depth; ++i) {
784  chevrons << _depth_mark;
785  }
786  format = replace(format, "\\{depth_marks\\}", chevrons.str());
787 #endif
788 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
789  const std::string hfill_tag = "{hfill}";
790  const size_t hfill_pos = format.find(hfill_tag);
791  if(hfill_pos != std::string::npos) {
792  if(_nb_columns > 0) {
793  const size_t left_len = hfill_pos;
794  const size_t right_len = format.size() - hfill_pos - hfill_tag.size();
795  if(right_len+left_len > _nb_columns) {
796  // The right part would go over the terminal width: add a new line.
797  const std::string hfill(std::max((size_t)0, _nb_columns-right_len), _hfill_char);
798  format = replace(format, "\\{hfill\\}", "\n"+hfill);
799  } else {
800  // There is some space in between left and right parts.
801  const std::string hfill(std::max((size_t)0, _nb_columns - (right_len+left_len)), _hfill_char);
802  format = replace(format, "\\{hfill\\}", hfill);
803  }
804  } else {
805  // We don't know the terminal width.
806  format = replace(format, "\\{hfill\\}", _hfill_char);
807  }
808  }
809 #endif
810  return _level_fmt.at(stage)(format);
811  }
812 
814  void log(
815  const level& stage,
816  const std::string& what,
817  const std::string& file, const std::string& func, size_t line
818  ) const
819  {
820  scope_t scope = locate(stage, file, func, line);
821 
822  if(scope.matches) {
823 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
824  *_out << format(_format_log, what, basename(getenv("_")),
825  stage, file, func,
826  line, scope.depth );
827 #else
828  *_out << format(_format_log, what,
829  stage, file, func,
830  line );
831 
832 #endif
833  _out->flush();
834  } // if scopes.matches
835  }
836 
838  template<class In>
839  void dump(
840  const level& stage,
841  const In container_begin, const In container_end,
842  const std::string& file, const std::string& func, size_t line,
843  const std::string& filename_template = "dump_{n}.dat",
844  const std::string sep = dump_default_sep
845  ) const
846  {
847  scope_t scope = locate(stage, file, func, line);
848 
849  if(scope.matches) {
850  const std::string tag = "\\{n\\}";
851  const std::regex re(tag);
852  std::string outfile = "";
853 
854  // If the file name template has the {n} tag.
855  if(std::regex_search(filename_template, re)) {
856  // Increment n until a free one is found.
857  size_t n = 0;
858  do {
859  outfile = replace(filename_template, tag, n);
860  n++;
861  } while( fs::exists( outfile ) );
862 
863  } else {
864  // Use the parameter as is.
865  outfile = filename_template;
866  }
867 
868  std::ofstream fd(outfile);
869 
870  if(_format_dump.size() > 0) {
871 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
872  fd << format(_format_dump, "", basename(getenv("_")),
873  stage, file, func,
874  line, scope.depth );
875 #else
876  fd << format(_format_dump, "",
877  stage, file, func,
878  line );
879 #endif
880  fd << sep; // sep after comment line.
881  }
882 
883  std::copy(container_begin, container_end,
884  std::ostream_iterator<typename In::value_type>(fd, sep.c_str()));
885 
886  fd.close();
887  } // if scopes.matches
888  }
889 
891 };
892 
895 #else // not WITH_CLUTCHLOG
896 
897 
898 /**********************************************************************
899  * Fake implementation
900  **********************************************************************/
901 
902 // Equivalent class with empty methods, will be optimized out
903 // while allowing to actually have calls implemented without WITH_CLUTCHLOG guards.
904 #pragma GCC diagnostic push
905 #pragma GCC diagnostic ignored "-Wreturn-type"
906 class clutchlog
907 {
908  public:
909  static clutchlog& logger() {}
910  enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
911  class fmt {
912  public:
913  enum class fg { black, red, green, yellow, blue, magenta, cyan, white, none } fore;
914  enum class bg { black, red, green, yellow, blue, magenta, cyan, white, none } back;
915  enum class typo { reset, bold, underline, inverse, none } style;
916  fmt() : fore(fg::none), back(bg::none), style(typo::none) {}
917  fmt( fg f, bg b = bg::none, typo s = typo::none) : fore(f), back(b), style(s) {}
918  fmt( fg f, typo s , bg b = bg::none) : fore(f), back(b), style(s) {}
919  fmt( bg b, fg f = fg::none, typo s = typo::none) : fore(f), back(b), style(s) {}
920  fmt( bg b, typo s , fg f = fg::none) : fore(f), back(b), style(s) {}
921  fmt(typo s, fg f = fg::none, bg b = bg::none) : fore(f), back(b), style(s) {}
922  fmt(typo s, bg b , fg f = fg::none) : fore(f), back(b), style(s) {}
923  protected:
924  std::ostream& print_on(std::ostream&) const {}
925  public:
926  friend std::ostream& operator<<(std::ostream&, const fmt&) {}
927  std::string operator()(const std::string&) const {}
928  };
929  public:
930  clutchlog(clutchlog const&) = delete;
931  void operator=(clutchlog const&) = delete;
932  private:
933  clutchlog() {}
934  protected:
935  struct scope_t {};
936  scope_t locate(
937  const level&,
938  const std::string&,
939  const std::string&,
940  const size_t
941  ) const
942  {}
943  public:
944  void format(const std::string&) {}
945  std::string format() const {}
946 
947  void format_comment(const std::string&) {}
948  std::string format_comment() const {}
949 
950  void out(std::ostream&) {}
951  std::ostream& out() {}
952 
953 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
954  void depth(size_t) {}
955  size_t depth() const {}
956 
957  void depth_mark(const std::string) {}
958  std::string depth_mark() const {}
959 #endif
960 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
961  void hfill_mark(const char) {}
962  char hfill_mark() const {}
963 #endif
964 
965  void threshold(level) {}
966  void threshold(const std::string&) {}
967  level threshold() const {}
968  const std::map<std::string,level> levels() const {};
969  level level_of(const std::string) {}
970 
971  void file(std::string) {}
972  void func(std::string) {}
973  void line(std::string) {}
974 
975 #pragma GCC diagnostic push
976 #pragma GCC diagnostic ignored "-Wunused-parameter"
977  void location(
978  const std::string&,
979  const std::string& in_function=".*",
980  const std::string& in_line=".*"
981  )
982  {}
983 #pragma GCC diagnostic pop
984  void style(level, fmt) {}
985  fmt style(level) const {}
986  public:
987  std::string replace(
988  const std::string&,
989  const std::string&,
990  const std::string&
991  ) const
992  {}
993 
994  std::string replace(
995  const std::string&,
996  const std::string&,
997  const size_t
998  ) const
999  {}
1000 
1001  std::string format(
1002  std::string,
1003  const std::string&,
1005  const std::string&,
1006 #endif
1007  const level&,
1008  const std::string&,
1009  const std::string&,
1010  const size_t
1012  ,
1013  const size_t
1014 #endif
1015  ) const
1016  {}
1017 
1018  void log(
1019  const level&,
1020  const std::string&,
1021  const std::string&, const std::string&, size_t
1022  ) const
1023  {}
1024 
1025  template<class In>
1026  void dump(
1027  const level&,
1028  const In, const In,
1029  const std::string&, const std::string&, size_t,
1030  const std::string&,
1031  const std::string
1032  ) const
1033  {}
1034 };
1035 #pragma GCC diagnostic pop
1036 #endif // WITH_CLUTCHLOG
1037 
1038 #endif // __CLUTCHLOG_H__
clutchlog::file
void file(std::string file)
Set the regular expression filtering the file location.
Definition: clutchlog.h:565
CLUTCHDUMP_DEFAULT_FORMAT
#define CLUTCHDUMP_DEFAULT_FORMAT
Compile-time default format of the comment line in file dump.
Definition: clutchlog.h:217
clutchlog::fmt::fg
fg
Foreground color codes.
Definition: clutchlog.h:297
clutchlog::dump_default_format
static std::string dump_default_format
Default format of the comment line in file dump.
Definition: clutchlog.h:231
clutchlog::levels
const std::map< std::string, level > & levels() const
Get the map of available log levels string representations toward their identifier....
Definition: clutchlog.h:548
clutchlog::out
void out(std::ostream &out)
Set the output stream on which to print.
Definition: clutchlog.h:519
clutchlog::fmt::fmt
fmt()
&#160;Empty constructor, only useful for a no-op formatter.
Definition: clutchlog.h:332
clutchlog::logger
static clutchlog & logger()
Get the logger instance.
Definition: clutchlog.h:273
clutchlog::line
void line(std::string line)
Set the regular expression filtering the line location.
Definition: clutchlog.h:569
clutchlog::format_comment
void format_comment(const std::string &format)
Set the template string for dumps.
Definition: clutchlog.h:514
clutchlog::fmt::typo
typo
Typographic style codes.
Definition: clutchlog.h:323
CLUTCHDUMP_DEFAULT_SEP
#define CLUTCHDUMP_DEFAULT_SEP
Compile-time default item separator for dump.
Definition: clutchlog.h:235
CLUTCHLOG_HFILL_MARK
#define CLUTCHLOG_HFILL_MARK
Character used as a filling for right-align the right part of messages with "{hfill}".
Definition: clutchlog.h:256
clutchlog::default_depth_mark
static std::string default_depth_mark
Default mark for stack depth.
Definition: clutchlog.h:245
CLUTCHLOG_DEFAULT_FORMAT
#define CLUTCHLOG_DEFAULT_FORMAT
Compile-time default format of the messages (debug mode: with absolute location).
Definition: clutchlog.h:194
clutchlog::format
std::string format() const
Get the template string.
Definition: clutchlog.h:511
clutchlog::log
void log(const level &stage, const std::string &what, const std::string &file, const std::string &func, size_t line) const
Print a log message IF the location matches the given one.
Definition: clutchlog.h:814
clutchlog::level_of
level level_of(const std::string name)
Return the log level tag corresponding to the given pre-configured name.
Definition: clutchlog.h:554
clutchlog::_format_dump
std::string _format_dump
Current format of the file output.
Definition: clutchlog.h:472
clutchlog::fmt::operator()
std::string operator()(const std::string &msg) const
Format the given string with the currently encoded format.
Definition: clutchlog.h:391
clutchlog::format
std::string format(std::string format, const std::string &what, const level &stage, const std::string &file, const std::string &func, const size_t line) const
Substitute all tags in the format string with the corresponding information and apply the style corre...
Definition: clutchlog.h:753
clutchlog::_format_log
std::string _format_log
Current format of the standard output.
Definition: clutchlog.h:470
clutchlog::level
level
Available log levels.
Definition: clutchlog.h:280
clutchlog::scope_t::stage
level stage
Current log level.
Definition: clutchlog.h:606
CLUTCHLOG_DEFAULT_DEPTH_MARK
#define CLUTCHLOG_DEFAULT_DEPTH_MARK
Compile-time default mark for stack depth.
Definition: clutchlog.h:242
clutchlog::fmt::operator<<
friend std::ostream & operator<<(std::ostream &os, const fmt &fmt)
Output stream overload.
Definition: clutchlog.h:376
clutchlog::_level_fmt
std::map< level, fmt > _level_fmt
Dictionary of level identifier to their format.
Definition: clutchlog.h:468
clutchlog::_in_line
std::regex _in_line
Current line location filter.
Definition: clutchlog.h:490
clutchlog::fmt::style
enum clutchlog::fmt::typo style
Typographic style.
clutchlog::_stage
level _stage
Current log level.
Definition: clutchlog.h:484
clutchlog::_strip_calls
const size_t _strip_calls
Current number of call stack levels to remove from depth display.
Definition: clutchlog.h:462
clutchlog::format_comment
std::string format_comment() const
Get the template string for dumps.
Definition: clutchlog.h:516
CLUTCHLOG_STRIP_CALLS
#define CLUTCHLOG_STRIP_CALLS
Compile-time number of call stack levels to remove from depth display by default.
Definition: clutchlog.h:249
clutchlog::_out
std::ostream * _out
Standard output.
Definition: clutchlog.h:476
clutchlog::func
void func(std::string func)
Set the regular expression filtering the function location.
Definition: clutchlog.h:567
clutchlog::location
void location(const std::string &in_file, const std::string &in_function=".*", const std::string &in_line=".*")
Set the regular expressions filtering the location.
Definition: clutchlog.h:572
clutchlog::default_hfill_char
static char default_hfill_char
Default character used as a filling for right-align the right part of messages with "{hfill}".
Definition: clutchlog.h:259
clutchlog::dump
void dump(const level &stage, const In container_begin, const In container_end, const std::string &file, const std::string &func, size_t line, const std::string &filename_template="dump_{n}.dat", const std::string sep=dump_default_sep) const
Dump a serializable container after a comment line with log information.
Definition: clutchlog.h:839
clutchlog::replace
std::string replace(const std::string &form, const std::string &mark, const size_t tag) const
Replace mark by tag in form, converting tag to its string representation first.
Definition: clutchlog.h:742
clutchlog::fmt
Color and style formatter for ANSI terminal escape sequences.
Definition: clutchlog.h:294
clutchlog::_in_file
std::regex _in_file
Current file location filter.
Definition: clutchlog.h:486
clutchlog::out
std::ostream & out()
Get the output stream on which to print.
Definition: clutchlog.h:521
clutchlog::replace
std::string replace(const std::string &form, const std::string &mark, const std::string &tag) const
Replace mark by tag in form.
Definition: clutchlog.h:677
clutchlog::default_strip_calls
static unsigned int default_strip_calls
Number of call stack levels to remove from depth display by default.
Definition: clutchlog.h:252
clutchlog::_hfill_char
char _hfill_char
Character for filling.
Definition: clutchlog.h:474
clutchlog::default_format
static std::string default_format
Default format of the messages.
Definition: clutchlog.h:209
clutchlog::fmt::fore
enum clutchlog::fmt::fg fore
Foreground color.
clutchlog::threshold
void threshold(const std::string &l)
Set the log level (below which logs are not printed) with a string.
Definition: clutchlog.h:544
clutchlog::scope_t::there
bool there
Location is compatible.
Definition: clutchlog.h:612
clutchlog::scope_t
Structure holding a location matching.
Definition: clutchlog.h:602
clutchlog::_in_func
std::regex _in_func
Current function location filter.
Definition: clutchlog.h:488
clutchlog::threshold
void threshold(level l)
Set the log level (below which logs are not printed) with an identifier.
Definition: clutchlog.h:542
clutchlog::style
void style(level stage, fmt style)
Set the style (color and typo) of the given log level, passing a fmt instance.
Definition: clutchlog.h:590
clutchlog::_level_word
const std::map< level, std::string > _level_word
Dictionary of level identifier to their string representation.
Definition: clutchlog.h:464
clutchlog::locate
scope_t locate(const level &stage, const std::string &file, const std::string &func, const size_t line) const
Gather information on the current location of the call.
Definition: clutchlog.h:626
clutchlog::scope_t::matches
bool matches
Everything is compatible.
Definition: clutchlog.h:604
clutchlog::dump_default_sep
static std::string dump_default_sep
Default item separator for dump.
Definition: clutchlog.h:238
clutchlog::threshold
level threshold() const
Get the log level below which logs are not printed.
Definition: clutchlog.h:546
clutchlog::fmt::print_on
std::ostream & print_on(std::ostream &os) const
Print the currently encoded format escape code on the given output stream.
Definition: clutchlog.h:346
clutchlog::_word_level
std::map< std::string, level > _word_level
Dictionary of level string to their identifier.
Definition: clutchlog.h:466
clutchlog::style
void style(level stage, FMT... styles)
Set the style (color and typo) of the given log level.
Definition: clutchlog.h:588
clutchlog::scope_t::scope_t
scope_t()
Constructor.
Definition: clutchlog.h:614
CLUTCHLOG_HAVE_UNIX_SYSINFO
#define CLUTCHLOG_HAVE_UNIX_SYSINFO
POSIX headers necessary for stack depth management are available.
Definition: clutchlog.h:32
clutchlog
Definition: clutchlog.h:175
clutchlog::style
fmt style(level stage) const
Get the configured fmt instance of the given log level.
Definition: clutchlog.h:592
clutchlog::fmt::bg
bg
Background color codes.
Definition: clutchlog.h:310
clutchlog::fmt::back
enum clutchlog::fmt::bg back
Background color.