clutchlog  0.10
clutchlog.h
Go to the documentation of this file.
1 #pragma once
2 #ifndef CLUTCHLOG_H
3 #define CLUTCHLOG_H
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 "[{name}] {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  #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
263  #ifndef CLUTCHLOG_HFILL_MAX
264  #define CLUTCHLOG_HFILL_MAX 300
265  #endif
266  #endif
267  static inline unsigned short default_hfill_max = CLUTCHLOG_HFILL_MAX;
269 
270  // NOTE: there is no CLUTCHLOG_HFILL_STYLE for defaulting,
271  // but you can still set `hfill_style(...)` on the logger singleton.
272  /* @} */
273 
274 
275  public:
276 
286  static clutchlog& logger()
287  {
288  static clutchlog instance;
289  return instance;
290  }
291 
293  enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
294 
307  class fmt {
308  public:
310  enum class fg {
311  black = 30,
312  red = 31,
313  green = 32,
314  yellow = 33,
315  blue = 34,
316  magenta = 35,
317  cyan = 36,
318  white = 37,
319  none
320  } fore;
321 
323  enum class bg {
324  black = 40,
325  red = 41,
326  green = 42,
327  yellow = 43,
328  blue = 44,
329  magenta = 45,
330  cyan = 46,
331  white = 47,
332  none
333  } back;
334 
336  enum class typo {
337  reset = 0,
338  bold = 1,
339  underline = 4,
340  inverse = 7,
341  none
342  } style;
343 
345  fmt() : fore(fg::none), back(bg::none), style(typo::none) {}
346 
349  fmt( fg f, bg b = bg::none, typo s = typo::none) : fore(f), back(b), style(s) {}
350  fmt( fg f, typo s , bg b = bg::none) : fore(f), back(b), style(s) {}
351  fmt( bg b, fg f = fg::none, typo s = typo::none) : fore(f), back(b), style(s) {}
352  fmt( bg b, typo s , fg f = fg::none) : fore(f), back(b), style(s) {}
353  fmt(typo s, fg f = fg::none, bg b = bg::none) : fore(f), back(b), style(s) {}
354  fmt(typo s, bg b , fg f = fg::none) : fore(f), back(b), style(s) {}
357  protected:
359  std::ostream& print_on( std::ostream& os) const
360  {
361  std::vector<int> codes; codes.reserve(3);
362  if(this->fore != fg::none) { codes.push_back(static_cast<int>(this->fore ));}
363  if(this->back != bg::none) { codes.push_back(static_cast<int>(this->back ));}
364  if(this->style != typo::none) { codes.push_back(static_cast<int>(this->style));}
365  if(codes.size() == 0) {return os;}
366 
367  os << "\033[";
368  assert(codes.size() > 0);
369  os << codes[0];
370  for(size_t i=1; i < codes.size(); ++i) {
371  os << ";" << codes[i];
372  }
373  os << "m";
374  return os;
375  }
376 
377  public:
389  friend std::ostream& operator<<(std::ostream& os, const fmt& fmt)
390  {
391  return fmt.print_on(os);
392  }
393 
404  std::string operator()( const std::string& msg ) const
405  {
406  std::ostringstream os;
407  this->print_on(os);
408  fmt reset(fmt::typo::reset);
409  os << msg;
410  reset.print_on(os);
411  return os.str();
412  }
413  }; // fmt class
414 
421  public:
422  clutchlog(clutchlog const&) = delete;
423  void operator=(clutchlog const&) = delete;
424 
425  private:
426  clutchlog() :
427  // system, main, log
429  _level_word({
430  {level::critical,"Critical"},
431  {level::error ,"Error"},
432  {level::warning ,"Warning"},
433  {level::progress,"Progress"},
434  {level::note ,"Note"},
435  {level::info ,"Info"},
436  {level::debug ,"Debug"},
437  {level::xdebug ,"XDebug"}
438  }),
439  _level_fmt({
440  {level::critical,fmt(fmt::fg::red, fmt::typo::underline)},
441  {level::error ,fmt(fmt::fg::red, fmt::typo::bold)},
442  {level::warning ,fmt(fmt::fg::magenta, fmt::typo::bold)},
443  {level::progress,fmt()},
444  {level::note ,fmt()},
445  {level::info ,fmt()},
446  {level::debug ,fmt()},
447  {level::xdebug ,fmt()}
448  }),
451  #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
452  _hfill_char(clutchlog::default_hfill_char),
453  _hfill_fmt(fmt::fg::none),
454  _hfill_max(clutchlog::default_hfill_max),
455  #endif
456  _out(&std::clog),
457  #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
458  _depth(std::numeric_limits<size_t>::max() - _strip_calls),
459  _depth_mark(clutchlog::default_depth_mark),
460  #endif
461  _stage(level::error),
462  _in_file(".*"),
463  _in_func(".*"),
464  _in_line(".*")
465  {
466  // Reverse the level->word map into a word->level map.
467  for(auto& lw : _level_word) {
468  _word_level[lw.second] = lw.first;
469  }
470 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
471  struct winsize w;
472  ioctl(STDERR_FILENO, TIOCGWINSZ, &w);
473  _nb_columns = std::min(w.ws_col, default_hfill_max);
474 #endif
475  }
476 
477  protected:
479  size_t _strip_calls;
481  const std::map<level,std::string> _level_word;
483  std::map<std::string,level> _word_level;
485  std::map<level,fmt> _level_fmt;
487  std::string _format_log;
489  std::string _format_dump;
490  #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
491 
492  char _hfill_char;
494  fmt _hfill_fmt;
496  unsigned short _hfill_max;
497  #endif
498 
499  std::ostream* _out;
500  #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
501 
502  size_t _depth;
504  std::string _depth_mark;
505  #endif
506 
509  std::regex _in_file;
511  std::regex _in_func;
513  std::regex _in_line;
514 
515 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
516 
517  static const size_t _max_buffer = 4096;
518 #endif
519 
520 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
521 
522  size_t _nb_columns;
523 #endif
524 
526  public:
527 
531  void format(const std::string& format) {_format_log = format;}
534  std::string format() const {return _format_log;}
535 
537  void format_comment(const std::string& format) {_format_dump = format;}
539  std::string format_comment() const {return _format_dump;}
540 
542  void out(std::ostream& out) {_out = &out;}
544  std::ostream& out() {return *_out;}
545 
546 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
547  void depth(size_t d) {_depth = d;}
550  size_t depth() const {return _depth;}
551 
553  void depth_mark(const std::string mark) {_depth_mark = mark;}
555  std::string depth_mark() const {return _depth_mark;}
556 
558  void strip_calls(const size_t n) {_strip_calls = n;}
560  size_t strip_calls() const {return _strip_calls;}
561 #endif
562 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
563  void hfill_mark(const char mark) {_hfill_char = mark;}
566  char hfill_mark() const {return _hfill_char;}
568  void hfill_style(fmt style) {_hfill_fmt = style;}
573  template<class ... FMT>
574  void style(FMT... styles) { this->hfill_style(fmt(styles...)); }
576  fmt hfill_style() const {return _hfill_fmt;}
578  void hfill_max(const size_t nmax) {_hfill_max = nmax;}
580  unsigned short hfill_max() {return _hfill_max;}
581 #endif
582 
584  void threshold(level l) {_stage = l;}
586  void threshold(const std::string& l) {_stage = this->level_of(l);}
588  level threshold() const {return _stage;}
590  const std::map<std::string,level>& levels() const { return _word_level;}
591 
596  level level_of(const std::string name)
597  {
598  const auto ilevel = _word_level.find(name);
599  if( ilevel != std::end(_word_level)) {
600  return ilevel->second;
601  } else {
602  throw std::out_of_range("'" + name + "' is not a valid log level name");
603  }
604  }
605 
607  void file(std::string file) {_in_file = file;}
609  void func(std::string func) {_in_func = func;}
611  void line(std::string line) {_in_line = line;}
612 
614  void location(
615  const std::string& in_file,
616  const std::string& in_function=".*",
617  const std::string& in_line=".*"
618  )
619  {
620  file(in_file);
621  func(in_function);
622  line(in_line);
623  }
624 
629  template<class ... FMT>
630  void style(level stage, FMT... styles) { this->style(stage,fmt(styles...)); }
632  void style(level stage, fmt style) { _level_fmt.at(stage) = style; }
634  fmt style(level stage) const { return _level_fmt.at(stage); }
635 
638  public:
639 
643  struct scope_t {
646  bool matches;
649 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
650 
651  size_t depth;
652 #endif
653 
654  bool there;
657  matches(false),
658  stage(level::xdebug),
660  depth(0),
661 #endif
662  there(false)
663  {}
664  }; // scope_t
665 
666 
669  const level& stage,
670  const std::string& file,
671  const std::string& func,
672  const size_t line
673  ) const
674  {
675  scope_t scope; // False scope by default.
676 
677  /***** Log level stage *****/
678  // Test stage first, because it's fastest.
679  scope.stage = stage;
680  if(not (scope.stage <= _stage)) {
681  // Bypass useless computations if no match
682  // because of the stage.
683  return scope;
684  }
685 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
686  /***** Stack depth *****/
687  // Backtrace in second, quite fast.
688  size_t stack_depth;
689  void *buffer[_max_buffer];
690  stack_depth = backtrace(buffer, _max_buffer);
691  scope.depth = stack_depth;
692  if(not (scope.depth <= _depth + _strip_calls)) {
693  // Bypass if no match.
694  return scope;
695  }
696 #endif
697 
698  /***** Location *****/
699  // Location last, slowest.
700  std::ostringstream sline; sline << line;
701  scope.there =
702  std::regex_search(file, _in_file)
703  and std::regex_search(func, _in_func)
704  and std::regex_search(sline.str(), _in_line);
705 
706  // No need to retest stage and depth, which are true here.
707  scope.matches = scope.there;
708 
709  return scope;
710  } // locate
711 
719  std::string replace(
720  const std::string& form,
721  const std::string& mark,
722  const std::string& tag
723  ) const
724  {
725  // Useless debug code, unless something fancy would be done with name tags.
726  // std::regex re;
727  // try {
728  // re = std::regex(mark);
729  //
730  // } catch(const std::regex_error& e) {
731  // std::cerr << "ERROR with a regular expression \"" << mark << "\": ";
732  // switch(e.code()) {
733  // case std::regex_constants::error_collate:
734  // std::cerr << "the expression contains an invalid collating element name";
735  // break;
736  // case std::regex_constants::error_ctype:
737  // std::cerr << "the expression contains an invalid character class name";
738  // break;
739  // case std::regex_constants::error_escape:
740  // std::cerr << "the expression contains an invalid escaped character or a trailing escape";
741  // break;
742  // case std::regex_constants::error_backref:
743  // std::cerr << "the expression contains an invalid back reference";
744  // break;
745  // case std::regex_constants::error_brack:
746  // std::cerr << "the expression contains mismatched square brackets ('[' and ']')";
747  // break;
748  // case std::regex_constants::error_paren:
749  // std::cerr << "the expression contains mismatched parentheses ('(' and ')')";
750  // break;
751  // case std::regex_constants::error_brace:
752  // std::cerr << "the expression contains mismatched curly braces ('{' and '}')";
753  // break;
754  // case std::regex_constants::error_badbrace:
755  // std::cerr << "the expression contains an invalid range in a {} expression";
756  // break;
757  // case std::regex_constants::error_range:
758  // std::cerr << "the expression contains an invalid character range (e.g. [b-a])";
759  // break;
760  // case std::regex_constants::error_space:
761  // std::cerr << "there was not enough memory to convert the expression into a finite state machine";
762  // break;
763  // case std::regex_constants::error_badrepeat:
764  // std::cerr << "one of *?+{ was not preceded by a valid regular expression";
765  // break;
766  // case std::regex_constants::error_complexity:
767  // std::cerr << "the complexity of an attempted match exceeded a predefined level";
768  // break;
769  // case std::regex_constants::error_stack:
770  // std::cerr << "there was not enough memory to perform a match";
771  // break;
772  // default:
773  // std::cerr << "unknown error";
774  // }
775  // std::cerr << std::endl;
776  // throw;
777  // } // catch
778 
779  const std::regex re(mark);
780  return std::regex_replace(form, re, tag);
781  }
782 
784  std::string replace(
785  const std::string& form,
786  const std::string& mark,
787  const size_t tag
788  ) const
789  {
790  std::ostringstream stag; stag << tag;
791  return replace(form, mark, stag.str());
792  }
793 
795  std::string format(
796  std::string format,
797  const std::string& what,
799  const std::string& name,
800 #endif
801  const level& stage,
802  const std::string& file,
803  const std::string& func,
804  const size_t line
806  ,
807  const size_t depth
808 #endif
809  ) const
810  {
811  format = replace(format, "\\{msg\\}", what);
812  format = replace(format, "\\{file\\}", file);
813  format = replace(format, "\\{func\\}", func);
814  format = replace(format, "\\{line\\}", line);
815 
816  format = replace(format, "\\{level\\}", _level_word.at(stage));
817  std::string letter(1, _level_word.at(stage).at(0)); // char -> string
818  format = replace(format, "\\{level_letter\\}", letter);
819 
820 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
821  format = replace(format, "\\{name\\}", name);
822  format = replace(format, "\\{depth\\}", depth - _strip_calls);
823 
824  std::ostringstream chevrons;
825  for(size_t i = _strip_calls; i < depth; ++i) {
826  chevrons << _depth_mark;
827  }
828  format = replace(format, "\\{depth_marks\\}", chevrons.str());
829 #endif
830 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
831  const std::string hfill_tag = "{hfill}";
832  const size_t hfill_pos = format.find(hfill_tag);
833  if(hfill_pos != std::string::npos) {
834  if(_nb_columns > 0) {
835  const size_t left_len = hfill_pos;
836  const size_t right_len = format.size() - hfill_pos - hfill_tag.size();
837  if(right_len+left_len > _nb_columns) {
838  // The right part would go over the terminal width: add a new line.
839  const std::string hfill(std::max((size_t)0, _nb_columns-right_len), _hfill_char);
840  const std::string hfill_styled = _hfill_fmt(hfill);
841  format = replace(format, "\\{hfill\\}", "\n"+hfill_styled);
842  } else {
843  // There is some space in between left and right parts.
844  const std::string hfill(std::max((size_t)0, _nb_columns - (right_len+left_len)), _hfill_char);
845  const std::string hfill_styled = _hfill_fmt(hfill);
846  format = replace(format, "\\{hfill\\}", hfill_styled);
847  }
848  } else {
849  // We don't know the terminal width.
850  const std::string hfill(1, _hfill_char);
851  const std::string hfill_styled = _hfill_fmt(hfill);
852  format = replace(format, "\\{hfill\\}", hfill_styled);
853  }
854  }
855 #endif
856  return _level_fmt.at(stage)(format);
857  }
858 
860  void log(
861  const level& stage,
862  const std::string& what,
863  const std::string& file, const std::string& func, size_t line
864  ) const
865  {
866  scope_t scope = locate(stage, file, func, line);
867 
868  if(scope.matches) {
869 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
870  *_out << format(_format_log, what, basename(getenv("_")),
871  stage, file, func,
872  line, scope.depth );
873 #else
874  *_out << format(_format_log, what,
875  stage, file, func,
876  line );
877 
878 #endif
879  _out->flush();
880  } // if scopes.matches
881  }
882 
884  template<class In>
885  void dump(
886  const level& stage,
887  const In container_begin, const In container_end,
888  const std::string& file, const std::string& func, size_t line,
889  const std::string& filename_template = "dump_{n}.dat",
890  const std::string sep = dump_default_sep
891  ) const
892  {
893  scope_t scope = locate(stage, file, func, line);
894 
895  if(scope.matches) {
896  const std::string tag = "\\{n\\}";
897  const std::regex re(tag);
898  std::string outfile = "";
899 
900  // If the file name template has the {n} tag.
901  if(std::regex_search(filename_template, re)) {
902  // Increment n until a free one is found.
903  size_t n = 0;
904  do {
905  outfile = replace(filename_template, tag, n);
906  n++;
907  } while( fs::exists( outfile ) );
908 
909  } else {
910  // Use the parameter as is.
911  outfile = filename_template;
912  }
913 
914  std::ofstream fd(outfile);
915 
916  if(_format_dump.size() > 0) {
917 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
918  fd << format(_format_dump, "", basename(getenv("_")),
919  stage, file, func,
920  line, scope.depth );
921 #else
922  fd << format(_format_dump, "",
923  stage, file, func,
924  line );
925 #endif
926  fd << sep; // sep after comment line.
927  }
928 
929  std::copy(container_begin, container_end,
930  std::ostream_iterator<typename In::value_type>(fd, sep.c_str()));
931 
932  fd.close();
933  } // if scopes.matches
934  }
935 
937 };
938 
941 #else // not WITH_CLUTCHLOG
942 
943 
944 /**********************************************************************
945  * Fake implementation
946  **********************************************************************/
947 
948 // Equivalent class with empty methods, will be optimized out
949 // while allowing to actually have calls implemented without WITH_CLUTCHLOG guards.
950 #pragma GCC diagnostic push
951 #pragma GCC diagnostic ignored "-Wreturn-type"
952 class clutchlog
953 {
954  public:
955  static clutchlog& logger() {}
956  enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
957  class fmt {
958  public:
959  enum class fg { black, red, green, yellow, blue, magenta, cyan, white, none } fore;
960  enum class bg { black, red, green, yellow, blue, magenta, cyan, white, none } back;
961  enum class typo { reset, bold, underline, inverse, none } style;
962  fmt() : fore(fg::none), back(bg::none), style(typo::none) {}
963  fmt( fg f, bg b = bg::none, typo s = typo::none) : fore(f), back(b), style(s) {}
964  fmt( fg f, typo s , bg b = bg::none) : fore(f), back(b), style(s) {}
965  fmt( bg b, fg f = fg::none, typo s = typo::none) : fore(f), back(b), style(s) {}
966  fmt( bg b, typo s , fg f = fg::none) : fore(f), back(b), style(s) {}
967  fmt(typo s, fg f = fg::none, bg b = bg::none) : fore(f), back(b), style(s) {}
968  fmt(typo s, bg b , fg f = fg::none) : fore(f), back(b), style(s) {}
969  protected:
970  std::ostream& print_on(std::ostream&) const {}
971  public:
972  friend std::ostream& operator<<(std::ostream&, const fmt&) {}
973  std::string operator()(const std::string&) const {}
974  };
975  public:
976  clutchlog(clutchlog const&) = delete;
977  void operator=(clutchlog const&) = delete;
978  private:
979  clutchlog() {}
980  protected:
981  struct scope_t {};
982  scope_t locate(
983  const level&,
984  const std::string&,
985  const std::string&,
986  const size_t
987  ) const
988  {}
989  public:
990  void format(const std::string&) {}
991  std::string format() const {}
992 
993  void format_comment(const std::string&) {}
994  std::string format_comment() const {}
995 
996  void out(std::ostream&) {}
997  std::ostream& out() {}
998 
999 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
1000  void depth(size_t) {}
1001  size_t depth() const {}
1002 
1003  void depth_mark(const std::string) {}
1004  std::string depth_mark() const {}
1005  void strip_calls(const size_t) {}
1006  size_t strip_calls() const {}
1007 #endif
1008 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
1009  void hfill_mark(const char) {}
1010  char hfill_mark() const {}
1011  void hfill_fmt(fmt) {}
1012  fmt hfill_fmt() const {}
1013  void hfill_max(const size_t) {}
1014  unsigned short hfill_max() {}
1015 #endif
1016 
1017  void threshold(level) {}
1018  void threshold(const std::string&) {}
1019  level threshold() const {}
1020  const std::map<std::string,level> levels() const {}
1021  level level_of(const std::string) {}
1022 
1023  void file(std::string) {}
1024  void func(std::string) {}
1025  void line(std::string) {}
1026 
1027 #pragma GCC diagnostic push
1028 #pragma GCC diagnostic ignored "-Wunused-parameter"
1029  void location(
1030  const std::string&,
1031  const std::string& in_function=".*",
1032  const std::string& in_line=".*"
1033  )
1034  {}
1035 #pragma GCC diagnostic pop
1036  template<class ... FMT>
1037  void style(level, FMT...) {}
1038  void style(level, fmt) {}
1039  fmt style(level) const {}
1040  public:
1041  std::string replace(
1042  const std::string&,
1043  const std::string&,
1044  const std::string&
1045  ) const
1046  {}
1047 
1048  std::string replace(
1049  const std::string&,
1050  const std::string&,
1051  const size_t
1052  ) const
1053  {}
1054 
1055  std::string format(
1056  std::string,
1057  const std::string&,
1059  const std::string&,
1060 #endif
1061  const level&,
1062  const std::string&,
1063  const std::string&,
1064  const size_t
1066  ,
1067  const size_t
1068 #endif
1069  ) const
1070  {}
1071 
1072  void log(
1073  const level&,
1074  const std::string&,
1075  const std::string&, const std::string&, size_t
1076  ) const
1077  {}
1078 
1079  template<class In>
1080  void dump(
1081  const level&,
1082  const In, const In,
1083  const std::string&, const std::string&, size_t,
1084  const std::string&,
1085  const std::string
1086  ) const
1087  {}
1088 };
1089 #pragma GCC diagnostic pop
1090 #endif // WITH_CLUTCHLOG
1091 
1092 #endif // CLUTCHLOG_H
clutchlog::file
void file(std::string file)
Set the regular expression filtering the file location.
Definition: clutchlog.h:607
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:310
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:590
clutchlog::out
void out(std::ostream &out)
Set the output stream on which to print.
Definition: clutchlog.h:542
clutchlog::fmt::fmt
fmt()
&#160;Empty constructor, only useful for a no-op formatter.
Definition: clutchlog.h:345
clutchlog::logger
static clutchlog & logger()
Get the logger instance.
Definition: clutchlog.h:286
clutchlog::line
void line(std::string line)
Set the regular expression filtering the line location.
Definition: clutchlog.h:611
clutchlog::format_comment
void format_comment(const std::string &format)
Set the template string for dumps.
Definition: clutchlog.h:537
clutchlog::fmt::typo
typo
Typographic style codes.
Definition: clutchlog.h:336
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:534
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:860
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:596
clutchlog::default_hfill_max
static unsigned short default_hfill_max
Default maximum number of character used as a filling for right-align the right part of messages with...
Definition: clutchlog.h:268
clutchlog::_format_dump
std::string _format_dump
Current format of the file output.
Definition: clutchlog.h:489
clutchlog::fmt::operator()
std::string operator()(const std::string &msg) const
Format the given string with the currently encoded format.
Definition: clutchlog.h:404
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:795
clutchlog::_format_log
std::string _format_log
Current format of the standard output.
Definition: clutchlog.h:487
clutchlog::level
level
Available log levels.
Definition: clutchlog.h:293
clutchlog::scope_t::stage
level stage
Current log level.
Definition: clutchlog.h:648
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:389
clutchlog::_level_fmt
std::map< level, fmt > _level_fmt
Dictionary of level identifier to their format.
Definition: clutchlog.h:485
clutchlog::_in_line
std::regex _in_line
Current line location filter.
Definition: clutchlog.h:513
clutchlog::fmt::style
enum clutchlog::fmt::typo style
Typographic style.
clutchlog::_stage
level _stage
Current log level.
Definition: clutchlog.h:507
clutchlog::format_comment
std::string format_comment() const
Get the template string for dumps.
Definition: clutchlog.h:539
clutchlog::_strip_calls
size_t _strip_calls
Current number of call stack levels to remove from depth display.
Definition: clutchlog.h:479
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:499
clutchlog::func
void func(std::string func)
Set the regular expression filtering the function location.
Definition: clutchlog.h:609
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:614
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:885
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:784
clutchlog::fmt
Color and style formatter for ANSI terminal escape sequences.
Definition: clutchlog.h:307
clutchlog::_in_file
std::regex _in_file
Current file location filter.
Definition: clutchlog.h:509
clutchlog::out
std::ostream & out()
Get the output stream on which to print.
Definition: clutchlog.h:544
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:719
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::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:586
clutchlog::scope_t::there
bool there
Location is compatible.
Definition: clutchlog.h:654
clutchlog::scope_t
Structure holding a location matching.
Definition: clutchlog.h:644
clutchlog::_in_func
std::regex _in_func
Current function location filter.
Definition: clutchlog.h:511
clutchlog::threshold
void threshold(level l)
Set the log level (below which logs are not printed) with an identifier.
Definition: clutchlog.h:584
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:632
clutchlog::_level_word
const std::map< level, std::string > _level_word
Dictionary of level identifier to their string representation.
Definition: clutchlog.h:481
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:668
clutchlog::scope_t::matches
bool matches
Everything is compatible.
Definition: clutchlog.h:646
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:588
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:359
clutchlog::_word_level
std::map< std::string, level > _word_level
Dictionary of level string to their identifier.
Definition: clutchlog.h:483
clutchlog::style
void style(level stage, FMT... styles)
Set the style (color and typo) of the given log level.
Definition: clutchlog.h:630
clutchlog::scope_t::scope_t
scope_t()
Constructor.
Definition: clutchlog.h:656
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:634
clutchlog::fmt::bg
bg
Background color codes.
Definition: clutchlog.h:323
clutchlog::fmt::back
enum clutchlog::fmt::bg back
Background color.