From 2936c0363b75032608910ed75f04be07ddb1390e Mon Sep 17 00:00:00 2001 From: nojhan Date: Sun, 18 Oct 2020 17:20:14 +0200 Subject: [PATCH] add macros to the doc --- README.md | 2 +- clutchlog/clutchlog.h | 23 ++- docs/annotated.html | 2 +- docs/classclutchlog.html | 14 +- docs/classclutchlog_1_1fmt.html | 2 +- docs/clutchlog_8h.html | 147 ++++++++++++++++ docs/clutchlog_8h__incl.map | 2 + docs/clutchlog_8h__incl.md5 | 1 + docs/clutchlog_8h__incl.png | Bin 0 -> 18192 bytes docs/clutchlog_8h_source.html | 69 ++++---- docs/dir_000036_000000.html | 74 ++++++++ .../dir_59425e443f801f1f2fd8bbe4959a3ccf.html | 10 ++ ...r_59425e443f801f1f2fd8bbe4959a3ccf_dep.map | 5 + ...r_59425e443f801f1f2fd8bbe4959a3ccf_dep.md5 | 1 + ...r_59425e443f801f1f2fd8bbe4959a3ccf_dep.png | Bin 0 -> 2402 bytes .../dir_c318bd5cf14aaa5601e6029e0b5b4048.html | 6 + docs/files.html | 2 +- docs/globals.html | 90 ++++++++++ docs/globals_defs.html | 90 ++++++++++ docs/group__DefaultConfigMacros.html | 99 +++++++++++ docs/group__Formating.html | 6 +- docs/group__Main.html | 88 ++++++++++ docs/group__UseMacros.html | 163 ++++++++++++++++++ docs/index.html | 8 +- docs/menudata.js | 6 +- docs/modules.html | 12 +- docs/search/all_1.js | 8 +- docs/search/all_2.js | 1 + docs/search/all_3.js | 1 + docs/search/all_4.js | 8 +- docs/search/all_5.js | 10 +- docs/search/all_6.js | 2 +- docs/search/all_7.js | 4 +- docs/search/all_8.js | 3 +- docs/search/all_9.js | 3 +- docs/search/all_a.js | 3 +- docs/search/all_b.js | 4 +- docs/search/files_0.html | 26 +++ docs/search/files_0.js | 4 + docs/search/groups_0.js | 2 +- docs/search/groups_1.js | 2 +- docs/search/groups_2.js | 2 +- docs/search/groups_3.js | 2 +- docs/search/searchdata.js | 26 +-- 44 files changed, 935 insertions(+), 98 deletions(-) create mode 100644 docs/clutchlog_8h.html create mode 100644 docs/clutchlog_8h__incl.map create mode 100644 docs/clutchlog_8h__incl.md5 create mode 100644 docs/clutchlog_8h__incl.png create mode 100644 docs/dir_000036_000000.html create mode 100644 docs/dir_59425e443f801f1f2fd8bbe4959a3ccf_dep.map create mode 100644 docs/dir_59425e443f801f1f2fd8bbe4959a3ccf_dep.md5 create mode 100644 docs/dir_59425e443f801f1f2fd8bbe4959a3ccf_dep.png create mode 100644 docs/globals.html create mode 100644 docs/globals_defs.html create mode 100644 docs/group__DefaultConfigMacros.html create mode 100644 docs/group__Main.html create mode 100644 docs/group__UseMacros.html create mode 100644 docs/search/files_0.html create mode 100644 docs/search/files_0.js diff --git a/README.md b/README.md index 2d24980..3e3de94 100644 --- a/README.md +++ b/README.md @@ -305,7 +305,7 @@ Log levels use a classical semantics for a human skilled in the art, in decreasi - *Debug*: data which would help debugging the program if there was a bug later on. - *XDebug*: debugging information which would be heavy to read. -Note: the log levels constants are lower case (for example: `clutchlog::level:xdebug`), but their string representation is not (e.g. "XDebug", this should be taken into account when using `level_of`). +Note: the log levels constants are lower case (for example: `clutchlog::level::xdebug`), but their string representation is not (e.g. "XDebug", this should be taken into account when using `level_of`). Limitations diff --git a/clutchlog/clutchlog.h b/clutchlog/clutchlog.h index 252168f..8c45f49 100644 --- a/clutchlog/clutchlog.h +++ b/clutchlog/clutchlog.h @@ -2,6 +2,8 @@ #define __CLUTCHLOG_H__ #pragma once +/** @file */ + #include #include #include @@ -36,6 +38,9 @@ **********************************************************************/ #ifdef WITH_CLUTCHLOG +/** @addtogroup DefaultConfigMacros Default configuration macros + * @{ **/ + #ifndef CLUTCHLOG_DEFAULT_FORMAT //! Default format of the messages. #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1 @@ -67,6 +72,11 @@ #define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress #endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT +/** @} */ + +/** @addtogroup UseMacros High-level API macros + * @{ */ + //! Handy shortcuts to location. #define CLUTCHLOC __FILE__, __FUNCTION__, __LINE__ @@ -104,6 +114,8 @@ } #endif // NDEBUG +/** @} */ + #else // not WITH_CLUTCHLOG // Disabled macros can still be used in Release builds. #define CLUTCHLOG( LEVEL, WHAT ) { do {/*nothing*/} while(false); } @@ -115,9 +127,12 @@ **********************************************************************/ #ifdef WITH_CLUTCHLOG -/** Single class that holds everything. +/** The single class which holds everything. * * This is a Singleton class. + * + * @addtogroup Main Main class + * @{ */ class clutchlog { @@ -142,6 +157,9 @@ class clutchlog /** @} */ + /** @addtogroup Formating Formating tools + * @{ */ + /** @name Formating API * @{ */ @@ -258,6 +276,7 @@ class clutchlog }; // fmt class /** @} */ + /** @} */ /** @name Internal details * @{ */ @@ -682,6 +701,8 @@ class clutchlog /** @} */ }; +/** @} */ + #else // not WITH_CLUTCHLOG diff --git a/docs/annotated.html b/docs/annotated.html index 01dcd07..e0b9a27 100644 --- a/docs/annotated.html +++ b/docs/annotated.html @@ -65,7 +65,7 @@ $(function() {
Here are the classes, structs, unions and interfaces with brief descriptions:
[detail level 12]
- +
 CclutchlogSingle class that holds everything
 Cclutchlog
 CfmtColor and style formatter for ANSI terminal escape sequences
 Cscope_tStructure holding a location matching
diff --git a/docs/classclutchlog.html b/docs/classclutchlog.html index 92de73a..171ae1f 100644 --- a/docs/classclutchlog.html +++ b/docs/classclutchlog.html @@ -63,14 +63,9 @@ $(function() { Classes | List of all members
-
clutchlog Class Reference
+
clutchlog Class Reference
- -

Single class that holds everything. - More...

- -

#include <clutchlog.h>

@@ -165,7 +160,7 @@ void  - +

Classes

 
template<class In >
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=CLUTCHDUMP_DEFAULT_SEP) const
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=CLUTCHDUMP_DEFAULT_SEP) const
 Dump a serializable container after a comment line with log information.
 
@@ -235,10 +230,7 @@ void 
operator= ( Gather information on the current location of the call.
 
-

Detailed Description

-

Single class that holds everything.

-

This is a Singleton class.

-

Member Function Documentation

+

Member Function Documentation

◆ level_of()

diff --git a/docs/classclutchlog_1_1fmt.html b/docs/classclutchlog_1_1fmt.html index 3ca24e1..fb69a38 100644 --- a/docs/classclutchlog_1_1fmt.html +++ b/docs/classclutchlog_1_1fmt.html @@ -71,7 +71,7 @@ $(function() { Friends | List of all members
-
clutchlog::fmt Class Reference
+
clutchlog::fmt Class Reference
diff --git a/docs/clutchlog_8h.html b/docs/clutchlog_8h.html new file mode 100644 index 0000000..c114ced --- /dev/null +++ b/docs/clutchlog_8h.html @@ -0,0 +1,147 @@ + + + + + + + +clutchlog: clutchlog/clutchlog.h File Reference + + + + + + + + + +
+
+ + + + + + +
+
clutchlog +  0.5.0 +
+
+
+ + + + + + + + +
+
+ + +
+ +
+ + +
+
+ +
+
clutchlog.h File Reference
+
+
+
#include <filesystem>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <cassert>
+#include <cstdlib>
+#include <string>
+#include <limits>
+#include <regex>
+#include <map>
+
+Include dependency graph for clutchlog.h:
+
+
+
+
+

Go to the source code of this file.

+ + + + + + + + + + +

+Classes

class  clutchlog
 
class  clutchlog::fmt
 Color and style formatter for ANSI terminal escape sequences. More...
 
struct  clutchlog::scope_t
 Structure holding a location matching. More...
 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +

+Macros

+#define CLUTCHLOG_HAVE_UNIX_SYSINFO   0
 
+#define WITH_CLUTCHLOG
 
+#define CLUTCHLOG_DEFAULT_FORMAT   "{level_letter} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
 Default format of the messages.
 
+#define CLUTCHDUMP_DEFAULT_FORMAT   "# {level} in {func} @ {file}:{line}"
 Default format of the comment line in file dump.
 
+#define CLUTCHDUMP_DEFAULT_SEP   "\n"
 Default item separator for dump.
 
+#define CLUTCHLOG_DEFAULT_DEPTH_MARK   ">"
 
+#define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG   clutchlog::level::progress
 
+#define CLUTCHLOC   __FILE__, __FUNCTION__, __LINE__
 Handy shortcuts to location.
 
#define CLUTCHLOG(LEVEL, WHAT)
 Log a message at the given level. More...
 
#define CLUTCHDUMP(LEVEL, CONTAINER, FILENAME)
 Dump the given container. More...
 
+
+ + + + diff --git a/docs/clutchlog_8h__incl.map b/docs/clutchlog_8h__incl.map new file mode 100644 index 0000000..896c405 --- /dev/null +++ b/docs/clutchlog_8h__incl.map @@ -0,0 +1,2 @@ + + diff --git a/docs/clutchlog_8h__incl.md5 b/docs/clutchlog_8h__incl.md5 new file mode 100644 index 0000000..6b2b480 --- /dev/null +++ b/docs/clutchlog_8h__incl.md5 @@ -0,0 +1 @@ +8a99df5205b118d2ea433982b8d2959e \ No newline at end of file diff --git a/docs/clutchlog_8h__incl.png b/docs/clutchlog_8h__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..0df2d529cf80c9238bb6aab4aef85a1ca345b205 GIT binary patch literal 18192 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 R!6R-UvXV*?rQ&Zt{x79FSI+clutchlog.h
-
1 #ifndef __CLUTCHLOG_H__
2 #define __CLUTCHLOG_H__
3 #pragma once
4 
5 #include <filesystem>
6 #include <iostream>
7 #include <sstream>
8 #include <fstream>
9 #include <cassert>
10 #include <cstdlib>
11 #include <string>
12 #include <limits>
13 #include <regex>
14 #include <map>
15 
16 #if __has_include(<execinfo.h>) && __has_include(<stdlib.h>) && __has_include(<libgen.h>)
17 #include <execinfo.h> // execinfo
18 #include <stdlib.h> // getenv
19 #include <libgen.h> // basename
20 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 1
21 #else
22 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 0
23 #endif
24 
25 /**********************************************************************
26  * Enable by default in Debug builds.
27  **********************************************************************/
28 #ifndef WITH_CLUTCHLOG
29 #ifndef NDEBUG
30 #define WITH_CLUTCHLOG
31 #endif
32 #endif
33 
34 /**********************************************************************
35  * Macros definitions
36  **********************************************************************/
37 #ifdef WITH_CLUTCHLOG
38 
39 #ifndef CLUTCHLOG_DEFAULT_FORMAT
40 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
42 #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
43 #else
44 #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
45 #endif
46 #endif // CLUTCHLOG_DEFAULT_FORMAT
47 
48 #ifndef CLUTCHDUMP_DEFAULT_FORMAT
49 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
51 #define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth}) @ {file}:{line}"
52 #else
53 #define CLUTCHDUMP_DEFAULT_FORMAT "# {level} in {func} @ {file}:{line}"
54 #endif
55 #endif // CLUTCHDUMP_DEFAULT_FORMAT
56 
57 #ifndef CLUTCHDUMP_DEFAULT_SEP
58 #define CLUTCHDUMP_DEFAULT_SEP "\n"
60 #endif // CLUTCHDUMP_DEFAULT_SEP
61 
62 #ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
63 #define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
64 #endif // CLUTCHLOG_DEFAULT_DEPTH_MARK
65 
66 #ifndef CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG
67 #define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress
68 #endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT
69 
71 #define CLUTCHLOC __FILE__, __FUNCTION__, __LINE__
72 
74 #ifndef NDEBUG
75 #define CLUTCHLOG( LEVEL, WHAT ) { \
76  auto& logger = clutchlog::logger(); \
77  std::ostringstream msg ; msg << WHAT; \
78  logger.log(clutchlog::level::LEVEL, msg.str(), CLUTCHLOC); \
79 }
80 #else // not Debug build.
81 #define CLUTCHLOG( LEVEL, WHAT ) { \
82  if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
83  auto& logger = clutchlog::logger(); \
84  std::ostringstream msg ; msg << WHAT; \
85  logger.log(clutchlog::level::LEVEL, msg.str(), CLUTCHLOC); \
86  } \
87 }
88 #endif // NDEBUG
89 
91 #ifndef NDEBUG
92 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { \
93  auto& logger = clutchlog::logger(); \
94  logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
95  CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
96 }
97 #else // not Debug build.
98 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { \
99  if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
100  auto& logger = clutchlog::logger(); \
101  logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
102  CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
103  } \
104 }
105 #endif // NDEBUG
106 
107 #else // not WITH_CLUTCHLOG
108 // Disabled macros can still be used in Release builds.
109 #define CLUTCHLOG( LEVEL, WHAT ) { do {/*nothing*/} while(false); }
110 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { do {/*nothing*/} while(false); }
111 #endif // WITH_CLUTCHLOG
112 
113 /**********************************************************************
114  * Implementation
115  **********************************************************************/
116 
117 #ifdef WITH_CLUTCHLOG
118 
123 {
124  public:
134  static clutchlog& logger()
135  {
136  static clutchlog instance;
137  return instance;
138  }
139 
141  enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
142 
152  class fmt {
153  public:
155  enum class fg {
156  black = 30,
157  red = 31,
158  green = 32,
159  yellow = 33,
160  blue = 34,
161  magenta = 35,
162  cyan = 36,
163  white = 37,
164  none
165  } fore;
166 
168  enum class bg {
169  black = 40,
170  red = 41,
171  green = 42,
172  yellow = 43,
173  blue = 44,
174  magenta = 45,
175  cyan = 46,
176  white = 47,
177  none
178  } back;
179 
181  enum class typo {
182  reset = 0,
183  bold = 1,
184  underline = 4,
185  inverse = 7,
186  none
187  } style;
188 
190  fmt() : fore(fg::none), back(bg::none), style(typo::none) { }
191 
194  fmt( fg f, bg b = bg::none, typo s = typo::none) : fore(f), back(b), style(s) { }
195  fmt( fg f, typo s , bg b = bg::none) : fore(f), back(b), style(s) { }
196  fmt( bg b, fg f = fg::none, typo s = typo::none) : fore(f), back(b), style(s) { }
197  fmt( bg b, typo s , fg f = fg::none) : fore(f), back(b), style(s) { }
198  fmt(typo s, fg f = fg::none, bg b = bg::none) : fore(f), back(b), style(s) { }
199  fmt(typo s, bg b , fg f = fg::none) : fore(f), back(b), style(s) { }
202  protected:
204  std::ostream& print_on( std::ostream& os) const
205  {
206  std::vector<int> codes; codes.reserve(3);
207  if(this->fore != fg::none) { codes.push_back(static_cast<int>(this->fore ));}
208  if(this->back != bg::none) { codes.push_back(static_cast<int>(this->back ));}
209  if(this->style != typo::none) { codes.push_back(static_cast<int>(this->style));}
210  if(codes.size() == 0) {return os;}
211 
212  os << "\033[";
213  assert(codes.size() > 0);
214  os << codes[0];
215  for(size_t i=1; i < codes.size(); ++i) {
216  os << ";" << codes[i];
217  }
218  os << "m";
219  return os;
220  }
221 
222  public:
234  friend std::ostream& operator<<(std::ostream& os, const fmt& fmt)
235  {
236  return fmt.print_on(os);
237  }
238 
249  std::string operator()( const std::string& msg ) const
250  {
251  std::ostringstream os;
252  this->print_on(os);
253  fmt reset(fmt::typo::reset);
254  os << msg;
255  reset.print_on(os);
256  return os.str();
257  }
258  }; // fmt class
259 
265  public:
266  clutchlog(clutchlog const&) = delete;
267  void operator=(clutchlog const&) = delete;
268 
269  private:
270  clutchlog() :
271  // system, main, log
272  _strip_calls(5),
273  _level_word({
274  {level::critical,"Critical"},
275  {level::error ,"Error"},
276  {level::warning ,"Warning"},
277  {level::progress,"Progress"},
278  {level::note ,"Note"},
279  {level::info ,"Info"},
280  {level::debug ,"Debug"},
281  {level::xdebug ,"XDebug"}
282  }),
283  _level_fmt({
284  {level::critical,fmt(fmt::fg::red, fmt::typo::underline)},
285  {level::error ,fmt(fmt::fg::red, fmt::typo::bold)},
286  {level::warning ,fmt(fmt::fg::magenta, fmt::typo::bold)},
287  {level::progress,fmt()},
288  {level::note ,fmt()},
289  {level::info ,fmt()},
290  {level::debug ,fmt()},
291  {level::xdebug ,fmt()}
292  }),
293  _format_log(CLUTCHLOG_DEFAULT_FORMAT),
294  _format_dump(CLUTCHDUMP_DEFAULT_FORMAT),
295  _out(&std::clog),
296 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
297  _depth(std::numeric_limits<size_t>::max() - _strip_calls),
298  _depth_mark(CLUTCHLOG_DEFAULT_DEPTH_MARK),
299 #endif
300  _stage(level::error),
301  _in_file(".*"),
302  _in_func(".*"),
303  _in_line(".*")
304  {
305  // Reverse the level->word map into a word->level map.
306  for(auto& lw : _level_word) {
307  _word_level[lw.second] = lw.first;
308  }
309  }
310 
311  protected:
312  const size_t _strip_calls;
313  const std::map<level,std::string> _level_word;
314  std::map<std::string,level> _word_level;
315  std::map<level,fmt> _level_fmt;
316  std::string _format_log;
317  std::string _format_dump;
318  std::ostream* _out;
319 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
320  size_t _depth;
321  std::string _depth_mark;
322 #endif
323  level _stage;
324  std::regex _in_file;
325  std::regex _in_func;
326  std::regex _in_line;
327 
329  struct scope_t {
330  bool matches; // everything is compatible
331  level stage; // current log level
332 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
333  size_t depth; // current depth
334 #endif
335  bool there; // location is compatible
336  scope_t() :
337  matches(false),
338  stage(level::xdebug),
339 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
340  depth(0),
341 #endif
342  there(false)
343  {}
344  };
345 
348  const level& stage,
349  const std::string& file,
350  const std::string& func,
351  const size_t line
352  ) const
353  {
354  scope_t scope; // False scope by default.
355 
356  /***** Log level stage *****/
357  // Test stage first, because it's fastest.
358  scope.stage = stage;
359  if(not (scope.stage <= _stage)) {
360  // Bypass useless computations if no match
361  // because of the stage.
362  return scope;
363  }
364 
365 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
366  /***** Stack depth *****/
367  // Backtrace in second, quite fast.
368  const size_t max_buffer = 4096;
369  size_t stack_depth;
370  void *buffer[max_buffer];
371  stack_depth = backtrace(buffer, max_buffer);
372  scope.depth = stack_depth;
373  if(not (scope.depth <= _depth + _strip_calls)) {
374  // Bypass if no match.
375  return scope;
376  }
377 #endif
378 
379  /***** Location *****/
380  // Location last, slowest.
381  std::ostringstream sline; sline << line;
382  scope.there =
383  std::regex_search(file, _in_file)
384  and std::regex_search(func, _in_func)
385  and std::regex_search(sline.str(), _in_line);
386 
387  // No need to retest stage and depth, which are true here.
388  scope.matches = scope.there;
389 
390  return scope;
391  }
392 
395  public:
396 
400  void format(const std::string& format) {_format_log = format;}
403  std::string format() const {return _format_log;}
404 
406  void format_comment(const std::string& format) {_format_dump = format;}
408  std::string format_comment() const {return _format_dump;}
409 
411  void out(std::ostream& out) {_out = &out;}
413  std::ostream& out() {return *_out;}
414 
415 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
416  void depth(size_t d) {_depth = d;}
419  size_t depth() const {return _depth;}
420 
422  void depth_mark(std::string mark) {_depth_mark = mark;}
424  std::string depth_mark() const {return _depth_mark;}
425 #endif
426 
428  void threshold(level l) {_stage = l;}
430  level threshold() const {return _stage;}
431 
433  void file(std::string file) {_in_file = file;}
435  void func(std::string func) {_in_func = func;}
437  void line(std::string line) {_in_line = line;}
438 
440  void location(
441  const std::string& in_file,
442  const std::string& in_function=".*",
443  const std::string& in_line=".*"
444  )
445  {
446  file(in_file);
447  func(in_function);
448  line(in_line);
449  }
450 
455  template<class ... FMT>
456  void style(level stage, FMT... styles) { this->style(stage,fmt(styles...)); }
458  void style(level stage, fmt style) { _level_fmt.at(stage) = style; }
460  fmt style(level stage) const { return _level_fmt.at(stage); }
461 
466  level level_of(const std::string name)
467  {
468  const auto ilevel = _word_level.find(name);
469  if( ilevel != std::end(_word_level)) {
470  return ilevel->second;
471  } else {
472  throw std::out_of_range("'" + name + "' is not a valid log level name");
473  }
474  }
475 
478  public:
479 
490  std::string replace(
491  const std::string& form,
492  const std::string& mark,
493  const std::string& tag
494  ) const
495  {
496  // Useless debug code, unless something fancy would be done with name tags.
497  // std::regex re;
498  // try {
499  // re = std::regex(mark);
500  //
501  // } catch(const std::regex_error& e) {
502  // std::cerr << "ERROR with a regular expression \"" << mark << "\": ";
503  // switch(e.code()) {
504  // case std::regex_constants::error_collate:
505  // std::cerr << "the expression contains an invalid collating element name";
506  // break;
507  // case std::regex_constants::error_ctype:
508  // std::cerr << "the expression contains an invalid character class name";
509  // break;
510  // case std::regex_constants::error_escape:
511  // std::cerr << "the expression contains an invalid escaped character or a trailing escape";
512  // break;
513  // case std::regex_constants::error_backref:
514  // std::cerr << "the expression contains an invalid back reference";
515  // break;
516  // case std::regex_constants::error_brack:
517  // std::cerr << "the expression contains mismatched square brackets ('[' and ']')";
518  // break;
519  // case std::regex_constants::error_paren:
520  // std::cerr << "the expression contains mismatched parentheses ('(' and ')')";
521  // break;
522  // case std::regex_constants::error_brace:
523  // std::cerr << "the expression contains mismatched curly braces ('{' and '}')";
524  // break;
525  // case std::regex_constants::error_badbrace:
526  // std::cerr << "the expression contains an invalid range in a {} expression";
527  // break;
528  // case std::regex_constants::error_range:
529  // std::cerr << "the expression contains an invalid character range (e.g. [b-a])";
530  // break;
531  // case std::regex_constants::error_space:
532  // std::cerr << "there was not enough memory to convert the expression into a finite state machine";
533  // break;
534  // case std::regex_constants::error_badrepeat:
535  // std::cerr << "one of *?+{ was not preceded by a valid regular expression";
536  // break;
537  // case std::regex_constants::error_complexity:
538  // std::cerr << "the complexity of an attempted match exceeded a predefined level";
539  // break;
540  // case std::regex_constants::error_stack:
541  // std::cerr << "there was not enough memory to perform a match";
542  // break;
543  // default:
544  // std::cerr << "unknown error";
545  // }
546  // std::cerr << std::endl;
547  // throw;
548  // } // catch
549 
550  const std::regex re(mark);
551  return std::regex_replace(form, re, tag);
552  }
553 
555  std::string replace(
556  const std::string& form,
557  const std::string& mark,
558  const size_t tag
559  ) const
560  {
561  std::ostringstream stag; stag << tag;
562  return replace(form, mark, stag.str());
563  }
564 
566  std::string format(
567  std::string format,
568  const std::string& what,
569 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
570  const std::string& name,
571 #endif
572  const level& stage,
573  const std::string& file,
574  const std::string& func,
575  const size_t line
576 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
577  ,
578  const size_t depth
579 #endif
580  ) const
581  {
582  format = replace(format, "\\{msg\\}", what);
583  format = replace(format, "\\{file\\}", file);
584  format = replace(format, "\\{func\\}", func);
585  format = replace(format, "\\{line\\}", line);
586 
587  format = replace(format, "\\{level\\}", _level_word.at(stage));
588  std::string letter(1, _level_word.at(stage).at(0)); // char -> string
589  format = replace(format, "\\{level_letter\\}", letter);
590 
591 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
592  format = replace(format, "\\{name\\}", name);
593  format = replace(format, "\\{depth\\}", depth);
594 
595  std::ostringstream chevrons;
596  for(size_t i = _strip_calls; i < depth; ++i) {
597  chevrons << _depth_mark;
598  }
599  format = replace(format, "\\{depth_marks\\}", chevrons.str());
600 #endif
601 
602  return _level_fmt.at(stage)(format);
603  }
604 
606  void log(
607  const level& stage,
608  const std::string& what,
609  const std::string& file, const std::string& func, size_t line
610  ) const
611  {
612  scope_t scope = locate(stage, file, func, line);
613 
614  if(scope.matches) {
615 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
616  *_out << format(_format_log, what, basename(getenv("_")),
617  stage, file, func,
618  line, scope.depth );
619 #else
620  *_out << format(_format_log, what,
621  stage, file, func,
622  line );
623 
624 #endif
625  _out->flush();
626  } // if scopes.matches
627  }
628 
630  template<class In>
631  void dump(
632  const level& stage,
633  const In container_begin, const In container_end,
634  const std::string& file, const std::string& func, size_t line,
635  const std::string& filename_template="dump_{n}.dat",
636  const std::string sep=CLUTCHDUMP_DEFAULT_SEP
637  ) const
638  {
639  scope_t scope = locate(stage, file, func, line);
640 
641  if(scope.matches) {
642  const std::string tag = "\\{n\\}";
643  const std::regex re(tag);
644  std::string outfile = "";
645 
646  // If the file name template has the {n} tag.
647  if(std::regex_search(filename_template, re)) {
648  // Increment n until a free one is found.
649  size_t n = 0;
650  do {
651  outfile = replace(filename_template, tag, n);
652  n++;
653  } while( std::filesystem::exists( outfile ) );
654 
655  } else {
656  // Use the parameter as is.
657  outfile = filename_template;
658  }
659 
660  std::ofstream fd(outfile);
661 
662  if(_format_dump.size() > 0) {
663 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
664  fd << format(_format_dump, "", basename(getenv("_")),
665  stage, file, func,
666  line, scope.depth );
667 #else
668  fd << format(_format_dump, "",
669  stage, file, func,
670  line );
671 #endif
672  fd << sep; // sep after comment line.
673  }
674 
675  std::copy(container_begin, container_end,
676  std::ostream_iterator<typename In::value_type>(fd, sep.c_str()));
677 
678  fd.close();
679  } // if scopes.matches
680  }
681 
683 };
684 
685 #else // not WITH_CLUTCHLOG
686 
687 
688 /**********************************************************************
689  * Fake implementation
690  **********************************************************************/
691 
692 // Equivalent class with empty methods, will be optimized out
693 // while allowing to actually have calls implemented without WITH_CLUTCHLOG guards.
694 #pragma GCC diagnostic push
695 #pragma GCC diagnostic ignored "-Wreturn-type"
696 class clutchlog
697 {
698  public:
699  static clutchlog& logger() { }
700  enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
701  class fmt {
702  public:
703  enum class fg { black, red, green, yellow, blue, magenta, cyan, white, none } fore;
704  enum class bg { black, red, green, yellow, blue, magenta, cyan, white, none } back;
705  enum class typo { reset, bold, underline, inverse, none } style;
706  fmt() : fore(fg::none), back(bg::none), style(typo::none) { }
707  fmt( fg f, bg b = bg::none, typo s = typo::none) : fore(f), back(b), style(s) { }
708  fmt( fg f, typo s , bg b = bg::none) : fore(f), back(b), style(s) { }
709  fmt( bg b, fg f = fg::none, typo s = typo::none) : fore(f), back(b), style(s) { }
710  fmt( bg b, typo s , fg f = fg::none) : fore(f), back(b), style(s) { }
711  fmt(typo s, fg f = fg::none, bg b = bg::none) : fore(f), back(b), style(s) { }
712  fmt(typo s, bg b , fg f = fg::none) : fore(f), back(b), style(s) { }
713  protected:
714  std::ostream& print_on(std::ostream&) const { }
715  public:
716  friend std::ostream& operator<<(std::ostream&, const fmt&) { }
717  std::string operator()(const std::string&) const { }
718  };
719  public:
720  clutchlog(clutchlog const&) = delete;
721  void operator=(clutchlog const&) = delete;
722  private:
723  clutchlog() {}
724  protected:
725  struct scope_t {};
726  scope_t locate(
727  const level&,
728  const std::string&,
729  const std::string&,
730  const size_t
731  ) const
732  { }
733  public:
734  void format(const std::string&) {}
735  std::string format() const {}
736 
737  void format_comment(const std::string&) {}
738  std::string format_comment() const {}
739 
740  void out(std::ostream&) {}
741  std::ostream& out() {}
742 
743 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
744  void depth(size_t) {}
745  size_t depth() const {}
746 
747  void depth_mark(std::string) {}
748  std::string depth_mark() const {}
749 #endif
750 
751  void threshold(level) {}
752  level threshold() const {}
753 
754  void file(std::string) {}
755  void func(std::string) {}
756  void line(std::string) {}
757 
758 #pragma GCC diagnostic push
759 #pragma GCC diagnostic ignored "-Wunused-parameter"
760  void location(
761  const std::string&,
762  const std::string& in_function=".*",
763  const std::string& in_line=".*"
764  )
765  { }
766 #pragma GCC diagnostic pop
767  void style(level, fmt) { }
768  fmt style(level) const { }
769  level level_of(const std::string) { }
770  public:
771  std::string replace(
772  const std::string&,
773  const std::string&,
774  const std::string&
775  ) const
776  { }
777 
778  std::string replace(
779  const std::string&,
780  const std::string&,
781  const size_t
782  ) const
783  { }
784 
785  std::string format(
786  std::string,
787  const std::string&,
788 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
789  const std::string&,
790 #endif
791  const level&,
792  const std::string&,
793  const std::string&,
794  const size_t
795 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
796  ,
797  const size_t
798 #endif
799  ) const
800  { }
801 
802  void log(
803  const level&,
804  const std::string&,
805  const std::string&, const std::string&, size_t
806  ) const
807  { }
808 
809  template<class In>
810  void dump(
811  const level&,
812  const In, const In,
813  const std::string&, const std::string&, size_t,
814  const std::string&,
815  const std::string
816  ) const
817  { }
818 };
819 #pragma GCC diagnostic pop
820 #endif // WITH_CLUTCHLOG
821 
822 #endif // __CLUTCHLOG_H__
void threshold(level l)
Set the log level below which logs are not printed.
Definition: clutchlog.h:428
-
void out(std::ostream &out)
Set the output stream on which to print.
Definition: clutchlog.h:411
-
Single class that holds everything.
Definition: clutchlog.h:122
-
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:606
-
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:490
-
void func(std::string func)
Set the regular expression filtering the function location.
Definition: clutchlog.h:435
-
std::string format() const
Get the template string.
Definition: clutchlog.h:403
-
void line(std::string line)
Set the regular expression filtering the line location.
Definition: clutchlog.h:437
-
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:440
-
void format_comment(const std::string &format)
Set the template string for dumps.
Definition: clutchlog.h:406
-
std::string format_comment() const
Get the template string for dumps.
Definition: clutchlog.h:408
-
fmt()
 Empty constructor, only useful for a no-op formatter.
Definition: clutchlog.h:190
-
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:566
-
Structure holding a location matching.
Definition: clutchlog.h:329
-
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:347
-
fg
Foreground color codes.
Definition: clutchlog.h:155
-
level
Available log levels.
Definition: clutchlog.h:141
-
std::ostream & out()
Get the output stream on which to print.
Definition: clutchlog.h:413
-
friend std::ostream & operator<<(std::ostream &os, const fmt &fmt)
Output stream overload.
Definition: clutchlog.h:234
-
level level_of(const std::string name)
Return the log level tag corresponding to the given pre-configured name.
Definition: clutchlog.h:466
-
void style(level stage, FMT... styles)
Set the style (color and typo) of the given log level.
Definition: clutchlog.h:456
-
std::string operator()(const std::string &msg) const
Format the given string with the currently encoded format.
Definition: clutchlog.h:249
-
void style(level stage, fmt style)
Set the style (color and typo) of the given log level, passing a fmt instance.
Definition: clutchlog.h:458
-
std::ostream & print_on(std::ostream &os) const
Print the currently encoded format escape code on the given output stream.
Definition: clutchlog.h:204
-
static clutchlog & logger()
Get the logger instance.
Definition: clutchlog.h:134
-
typo
Typographic style codes.
Definition: clutchlog.h:181
-
bg
Background color codes.
Definition: clutchlog.h:168
-
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=CLUTCHDUMP_DEFAULT_SEP) const
Dump a serializable container after a comment line with log information.
Definition: clutchlog.h:631
-
void file(std::string file)
Set the regular expression filtering the file location.
Definition: clutchlog.h:433
-
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:555
-
fmt style(level stage) const
Get the configured fmt instance of the given log level.
Definition: clutchlog.h:460
-
level threshold() const
Get the log level below which logs are not printed.
Definition: clutchlog.h:430
-
Color and style formatter for ANSI terminal escape sequences.
Definition: clutchlog.h:152
+Go to the documentation of this file.
1 #ifndef __CLUTCHLOG_H__
2 #define __CLUTCHLOG_H__
3 #pragma once
4 
7 #include <filesystem>
8 #include <iostream>
9 #include <sstream>
10 #include <fstream>
11 #include <cassert>
12 #include <cstdlib>
13 #include <string>
14 #include <limits>
15 #include <regex>
16 #include <map>
17 
18 #if __has_include(<execinfo.h>) && __has_include(<stdlib.h>) && __has_include(<libgen.h>)
19 #include <execinfo.h> // execinfo
20 #include <stdlib.h> // getenv
21 #include <libgen.h> // basename
22 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 1
23 #else
24 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 0
25 #endif
26 
27 /**********************************************************************
28  * Enable by default in Debug builds.
29  **********************************************************************/
30 #ifndef WITH_CLUTCHLOG
31 #ifndef NDEBUG
32 #define WITH_CLUTCHLOG
33 #endif
34 #endif
35 
36 /**********************************************************************
37  * Macros definitions
38  **********************************************************************/
39 #ifdef WITH_CLUTCHLOG
40 
44 #ifndef CLUTCHLOG_DEFAULT_FORMAT
45 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
47 #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
48 #else
49 #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
50 #endif
51 #endif // CLUTCHLOG_DEFAULT_FORMAT
52 
53 #ifndef CLUTCHDUMP_DEFAULT_FORMAT
54 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
56 #define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth}) @ {file}:{line}"
57 #else
58 #define CLUTCHDUMP_DEFAULT_FORMAT "# {level} in {func} @ {file}:{line}"
59 #endif
60 #endif // CLUTCHDUMP_DEFAULT_FORMAT
61 
62 #ifndef CLUTCHDUMP_DEFAULT_SEP
63 #define CLUTCHDUMP_DEFAULT_SEP "\n"
65 #endif // CLUTCHDUMP_DEFAULT_SEP
66 
67 #ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
68 #define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
69 #endif // CLUTCHLOG_DEFAULT_DEPTH_MARK
70 
71 #ifndef CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG
72 #define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress
73 #endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT
74 
80 #define CLUTCHLOC __FILE__, __FUNCTION__, __LINE__
82 
84 #ifndef NDEBUG
85 #define CLUTCHLOG( LEVEL, WHAT ) { \
86  auto& logger = clutchlog::logger(); \
87  std::ostringstream msg ; msg << WHAT; \
88  logger.log(clutchlog::level::LEVEL, msg.str(), CLUTCHLOC); \
89 }
90 #else // not Debug build.
91 #define CLUTCHLOG( LEVEL, WHAT ) { \
92  if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
93  auto& logger = clutchlog::logger(); \
94  std::ostringstream msg ; msg << WHAT; \
95  logger.log(clutchlog::level::LEVEL, msg.str(), CLUTCHLOC); \
96  } \
97 }
98 #endif // NDEBUG
99 
101 #ifndef NDEBUG
102 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { \
103  auto& logger = clutchlog::logger(); \
104  logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
105  CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
106 }
107 #else // not Debug build.
108 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { \
109  if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
110  auto& logger = clutchlog::logger(); \
111  logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
112  CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
113  } \
114 }
115 #endif // NDEBUG
116 
119 #else // not WITH_CLUTCHLOG
120 // Disabled macros can still be used in Release builds.
121 #define CLUTCHLOG( LEVEL, WHAT ) { do {/*nothing*/} while(false); }
122 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { do {/*nothing*/} while(false); }
123 #endif // WITH_CLUTCHLOG
124 
125 /**********************************************************************
126  * Implementation
127  **********************************************************************/
128 
129 #ifdef WITH_CLUTCHLOG
130 
138 {
139  public:
149  static clutchlog& logger()
150  {
151  static clutchlog instance;
152  return instance;
153  }
154 
156  enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
157 
170  class fmt {
171  public:
173  enum class fg {
174  black = 30,
175  red = 31,
176  green = 32,
177  yellow = 33,
178  blue = 34,
179  magenta = 35,
180  cyan = 36,
181  white = 37,
182  none
183  } fore;
184 
186  enum class bg {
187  black = 40,
188  red = 41,
189  green = 42,
190  yellow = 43,
191  blue = 44,
192  magenta = 45,
193  cyan = 46,
194  white = 47,
195  none
196  } back;
197 
199  enum class typo {
200  reset = 0,
201  bold = 1,
202  underline = 4,
203  inverse = 7,
204  none
205  } style;
206 
208  fmt() : fore(fg::none), back(bg::none), style(typo::none) { }
209 
212  fmt( fg f, bg b = bg::none, typo s = typo::none) : fore(f), back(b), style(s) { }
213  fmt( fg f, typo s , bg b = bg::none) : fore(f), back(b), style(s) { }
214  fmt( bg b, fg f = fg::none, typo s = typo::none) : fore(f), back(b), style(s) { }
215  fmt( bg b, typo s , fg f = fg::none) : fore(f), back(b), style(s) { }
216  fmt(typo s, fg f = fg::none, bg b = bg::none) : fore(f), back(b), style(s) { }
217  fmt(typo s, bg b , fg f = fg::none) : fore(f), back(b), style(s) { }
220  protected:
222  std::ostream& print_on( std::ostream& os) const
223  {
224  std::vector<int> codes; codes.reserve(3);
225  if(this->fore != fg::none) { codes.push_back(static_cast<int>(this->fore ));}
226  if(this->back != bg::none) { codes.push_back(static_cast<int>(this->back ));}
227  if(this->style != typo::none) { codes.push_back(static_cast<int>(this->style));}
228  if(codes.size() == 0) {return os;}
229 
230  os << "\033[";
231  assert(codes.size() > 0);
232  os << codes[0];
233  for(size_t i=1; i < codes.size(); ++i) {
234  os << ";" << codes[i];
235  }
236  os << "m";
237  return os;
238  }
239 
240  public:
252  friend std::ostream& operator<<(std::ostream& os, const fmt& fmt)
253  {
254  return fmt.print_on(os);
255  }
256 
267  std::string operator()( const std::string& msg ) const
268  {
269  std::ostringstream os;
270  this->print_on(os);
271  fmt reset(fmt::typo::reset);
272  os << msg;
273  reset.print_on(os);
274  return os.str();
275  }
276  }; // fmt class
277 
284  public:
285  clutchlog(clutchlog const&) = delete;
286  void operator=(clutchlog const&) = delete;
287 
288  private:
289  clutchlog() :
290  // system, main, log
291  _strip_calls(5),
292  _level_word({
293  {level::critical,"Critical"},
294  {level::error ,"Error"},
295  {level::warning ,"Warning"},
296  {level::progress,"Progress"},
297  {level::note ,"Note"},
298  {level::info ,"Info"},
299  {level::debug ,"Debug"},
300  {level::xdebug ,"XDebug"}
301  }),
302  _level_fmt({
303  {level::critical,fmt(fmt::fg::red, fmt::typo::underline)},
304  {level::error ,fmt(fmt::fg::red, fmt::typo::bold)},
305  {level::warning ,fmt(fmt::fg::magenta, fmt::typo::bold)},
306  {level::progress,fmt()},
307  {level::note ,fmt()},
308  {level::info ,fmt()},
309  {level::debug ,fmt()},
310  {level::xdebug ,fmt()}
311  }),
312  _format_log(CLUTCHLOG_DEFAULT_FORMAT),
313  _format_dump(CLUTCHDUMP_DEFAULT_FORMAT),
314  _out(&std::clog),
315 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
316  _depth(std::numeric_limits<size_t>::max() - _strip_calls),
317  _depth_mark(CLUTCHLOG_DEFAULT_DEPTH_MARK),
318 #endif
319  _stage(level::error),
320  _in_file(".*"),
321  _in_func(".*"),
322  _in_line(".*")
323  {
324  // Reverse the level->word map into a word->level map.
325  for(auto& lw : _level_word) {
326  _word_level[lw.second] = lw.first;
327  }
328  }
329 
330  protected:
331  const size_t _strip_calls;
332  const std::map<level,std::string> _level_word;
333  std::map<std::string,level> _word_level;
334  std::map<level,fmt> _level_fmt;
335  std::string _format_log;
336  std::string _format_dump;
337  std::ostream* _out;
338 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
339  size_t _depth;
340  std::string _depth_mark;
341 #endif
342  level _stage;
343  std::regex _in_file;
344  std::regex _in_func;
345  std::regex _in_line;
346 
348  struct scope_t {
349  bool matches; // everything is compatible
350  level stage; // current log level
351 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
352  size_t depth; // current depth
353 #endif
354  bool there; // location is compatible
355  scope_t() :
356  matches(false),
357  stage(level::xdebug),
358 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
359  depth(0),
360 #endif
361  there(false)
362  {}
363  };
364 
367  const level& stage,
368  const std::string& file,
369  const std::string& func,
370  const size_t line
371  ) const
372  {
373  scope_t scope; // False scope by default.
374 
375  /***** Log level stage *****/
376  // Test stage first, because it's fastest.
377  scope.stage = stage;
378  if(not (scope.stage <= _stage)) {
379  // Bypass useless computations if no match
380  // because of the stage.
381  return scope;
382  }
383 
384 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
385  /***** Stack depth *****/
386  // Backtrace in second, quite fast.
387  const size_t max_buffer = 4096;
388  size_t stack_depth;
389  void *buffer[max_buffer];
390  stack_depth = backtrace(buffer, max_buffer);
391  scope.depth = stack_depth;
392  if(not (scope.depth <= _depth + _strip_calls)) {
393  // Bypass if no match.
394  return scope;
395  }
396 #endif
397 
398  /***** Location *****/
399  // Location last, slowest.
400  std::ostringstream sline; sline << line;
401  scope.there =
402  std::regex_search(file, _in_file)
403  and std::regex_search(func, _in_func)
404  and std::regex_search(sline.str(), _in_line);
405 
406  // No need to retest stage and depth, which are true here.
407  scope.matches = scope.there;
408 
409  return scope;
410  }
411 
414  public:
415 
419  void format(const std::string& format) {_format_log = format;}
422  std::string format() const {return _format_log;}
423 
425  void format_comment(const std::string& format) {_format_dump = format;}
427  std::string format_comment() const {return _format_dump;}
428 
430  void out(std::ostream& out) {_out = &out;}
432  std::ostream& out() {return *_out;}
433 
434 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
435  void depth(size_t d) {_depth = d;}
438  size_t depth() const {return _depth;}
439 
441  void depth_mark(std::string mark) {_depth_mark = mark;}
443  std::string depth_mark() const {return _depth_mark;}
444 #endif
445 
447  void threshold(level l) {_stage = l;}
449  level threshold() const {return _stage;}
450 
452  void file(std::string file) {_in_file = file;}
454  void func(std::string func) {_in_func = func;}
456  void line(std::string line) {_in_line = line;}
457 
459  void location(
460  const std::string& in_file,
461  const std::string& in_function=".*",
462  const std::string& in_line=".*"
463  )
464  {
465  file(in_file);
466  func(in_function);
467  line(in_line);
468  }
469 
474  template<class ... FMT>
475  void style(level stage, FMT... styles) { this->style(stage,fmt(styles...)); }
477  void style(level stage, fmt style) { _level_fmt.at(stage) = style; }
479  fmt style(level stage) const { return _level_fmt.at(stage); }
480 
485  level level_of(const std::string name)
486  {
487  const auto ilevel = _word_level.find(name);
488  if( ilevel != std::end(_word_level)) {
489  return ilevel->second;
490  } else {
491  throw std::out_of_range("'" + name + "' is not a valid log level name");
492  }
493  }
494 
497  public:
498 
509  std::string replace(
510  const std::string& form,
511  const std::string& mark,
512  const std::string& tag
513  ) const
514  {
515  // Useless debug code, unless something fancy would be done with name tags.
516  // std::regex re;
517  // try {
518  // re = std::regex(mark);
519  //
520  // } catch(const std::regex_error& e) {
521  // std::cerr << "ERROR with a regular expression \"" << mark << "\": ";
522  // switch(e.code()) {
523  // case std::regex_constants::error_collate:
524  // std::cerr << "the expression contains an invalid collating element name";
525  // break;
526  // case std::regex_constants::error_ctype:
527  // std::cerr << "the expression contains an invalid character class name";
528  // break;
529  // case std::regex_constants::error_escape:
530  // std::cerr << "the expression contains an invalid escaped character or a trailing escape";
531  // break;
532  // case std::regex_constants::error_backref:
533  // std::cerr << "the expression contains an invalid back reference";
534  // break;
535  // case std::regex_constants::error_brack:
536  // std::cerr << "the expression contains mismatched square brackets ('[' and ']')";
537  // break;
538  // case std::regex_constants::error_paren:
539  // std::cerr << "the expression contains mismatched parentheses ('(' and ')')";
540  // break;
541  // case std::regex_constants::error_brace:
542  // std::cerr << "the expression contains mismatched curly braces ('{' and '}')";
543  // break;
544  // case std::regex_constants::error_badbrace:
545  // std::cerr << "the expression contains an invalid range in a {} expression";
546  // break;
547  // case std::regex_constants::error_range:
548  // std::cerr << "the expression contains an invalid character range (e.g. [b-a])";
549  // break;
550  // case std::regex_constants::error_space:
551  // std::cerr << "there was not enough memory to convert the expression into a finite state machine";
552  // break;
553  // case std::regex_constants::error_badrepeat:
554  // std::cerr << "one of *?+{ was not preceded by a valid regular expression";
555  // break;
556  // case std::regex_constants::error_complexity:
557  // std::cerr << "the complexity of an attempted match exceeded a predefined level";
558  // break;
559  // case std::regex_constants::error_stack:
560  // std::cerr << "there was not enough memory to perform a match";
561  // break;
562  // default:
563  // std::cerr << "unknown error";
564  // }
565  // std::cerr << std::endl;
566  // throw;
567  // } // catch
568 
569  const std::regex re(mark);
570  return std::regex_replace(form, re, tag);
571  }
572 
574  std::string replace(
575  const std::string& form,
576  const std::string& mark,
577  const size_t tag
578  ) const
579  {
580  std::ostringstream stag; stag << tag;
581  return replace(form, mark, stag.str());
582  }
583 
585  std::string format(
586  std::string format,
587  const std::string& what,
588 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
589  const std::string& name,
590 #endif
591  const level& stage,
592  const std::string& file,
593  const std::string& func,
594  const size_t line
595 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
596  ,
597  const size_t depth
598 #endif
599  ) const
600  {
601  format = replace(format, "\\{msg\\}", what);
602  format = replace(format, "\\{file\\}", file);
603  format = replace(format, "\\{func\\}", func);
604  format = replace(format, "\\{line\\}", line);
605 
606  format = replace(format, "\\{level\\}", _level_word.at(stage));
607  std::string letter(1, _level_word.at(stage).at(0)); // char -> string
608  format = replace(format, "\\{level_letter\\}", letter);
609 
610 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
611  format = replace(format, "\\{name\\}", name);
612  format = replace(format, "\\{depth\\}", depth);
613 
614  std::ostringstream chevrons;
615  for(size_t i = _strip_calls; i < depth; ++i) {
616  chevrons << _depth_mark;
617  }
618  format = replace(format, "\\{depth_marks\\}", chevrons.str());
619 #endif
620 
621  return _level_fmt.at(stage)(format);
622  }
623 
625  void log(
626  const level& stage,
627  const std::string& what,
628  const std::string& file, const std::string& func, size_t line
629  ) const
630  {
631  scope_t scope = locate(stage, file, func, line);
632 
633  if(scope.matches) {
634 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
635  *_out << format(_format_log, what, basename(getenv("_")),
636  stage, file, func,
637  line, scope.depth );
638 #else
639  *_out << format(_format_log, what,
640  stage, file, func,
641  line );
642 
643 #endif
644  _out->flush();
645  } // if scopes.matches
646  }
647 
649  template<class In>
650  void dump(
651  const level& stage,
652  const In container_begin, const In container_end,
653  const std::string& file, const std::string& func, size_t line,
654  const std::string& filename_template="dump_{n}.dat",
655  const std::string sep=CLUTCHDUMP_DEFAULT_SEP
656  ) const
657  {
658  scope_t scope = locate(stage, file, func, line);
659 
660  if(scope.matches) {
661  const std::string tag = "\\{n\\}";
662  const std::regex re(tag);
663  std::string outfile = "";
664 
665  // If the file name template has the {n} tag.
666  if(std::regex_search(filename_template, re)) {
667  // Increment n until a free one is found.
668  size_t n = 0;
669  do {
670  outfile = replace(filename_template, tag, n);
671  n++;
672  } while( std::filesystem::exists( outfile ) );
673 
674  } else {
675  // Use the parameter as is.
676  outfile = filename_template;
677  }
678 
679  std::ofstream fd(outfile);
680 
681  if(_format_dump.size() > 0) {
682 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
683  fd << format(_format_dump, "", basename(getenv("_")),
684  stage, file, func,
685  line, scope.depth );
686 #else
687  fd << format(_format_dump, "",
688  stage, file, func,
689  line );
690 #endif
691  fd << sep; // sep after comment line.
692  }
693 
694  std::copy(container_begin, container_end,
695  std::ostream_iterator<typename In::value_type>(fd, sep.c_str()));
696 
697  fd.close();
698  } // if scopes.matches
699  }
700 
702 };
703 
706 #else // not WITH_CLUTCHLOG
707 
708 
709 /**********************************************************************
710  * Fake implementation
711  **********************************************************************/
712 
713 // Equivalent class with empty methods, will be optimized out
714 // while allowing to actually have calls implemented without WITH_CLUTCHLOG guards.
715 #pragma GCC diagnostic push
716 #pragma GCC diagnostic ignored "-Wreturn-type"
717 class clutchlog
718 {
719  public:
720  static clutchlog& logger() { }
721  enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
722  class fmt {
723  public:
724  enum class fg { black, red, green, yellow, blue, magenta, cyan, white, none } fore;
725  enum class bg { black, red, green, yellow, blue, magenta, cyan, white, none } back;
726  enum class typo { reset, bold, underline, inverse, none } style;
727  fmt() : fore(fg::none), back(bg::none), style(typo::none) { }
728  fmt( fg f, bg b = bg::none, typo s = typo::none) : fore(f), back(b), style(s) { }
729  fmt( fg f, typo s , bg b = bg::none) : fore(f), back(b), style(s) { }
730  fmt( bg b, fg f = fg::none, typo s = typo::none) : fore(f), back(b), style(s) { }
731  fmt( bg b, typo s , fg f = fg::none) : fore(f), back(b), style(s) { }
732  fmt(typo s, fg f = fg::none, bg b = bg::none) : fore(f), back(b), style(s) { }
733  fmt(typo s, bg b , fg f = fg::none) : fore(f), back(b), style(s) { }
734  protected:
735  std::ostream& print_on(std::ostream&) const { }
736  public:
737  friend std::ostream& operator<<(std::ostream&, const fmt&) { }
738  std::string operator()(const std::string&) const { }
739  };
740  public:
741  clutchlog(clutchlog const&) = delete;
742  void operator=(clutchlog const&) = delete;
743  private:
744  clutchlog() {}
745  protected:
746  struct scope_t {};
747  scope_t locate(
748  const level&,
749  const std::string&,
750  const std::string&,
751  const size_t
752  ) const
753  { }
754  public:
755  void format(const std::string&) {}
756  std::string format() const {}
757 
758  void format_comment(const std::string&) {}
759  std::string format_comment() const {}
760 
761  void out(std::ostream&) {}
762  std::ostream& out() {}
763 
764 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
765  void depth(size_t) {}
766  size_t depth() const {}
767 
768  void depth_mark(std::string) {}
769  std::string depth_mark() const {}
770 #endif
771 
772  void threshold(level) {}
773  level threshold() const {}
774 
775  void file(std::string) {}
776  void func(std::string) {}
777  void line(std::string) {}
778 
779 #pragma GCC diagnostic push
780 #pragma GCC diagnostic ignored "-Wunused-parameter"
781  void location(
782  const std::string&,
783  const std::string& in_function=".*",
784  const std::string& in_line=".*"
785  )
786  { }
787 #pragma GCC diagnostic pop
788  void style(level, fmt) { }
789  fmt style(level) const { }
790  level level_of(const std::string) { }
791  public:
792  std::string replace(
793  const std::string&,
794  const std::string&,
795  const std::string&
796  ) const
797  { }
798 
799  std::string replace(
800  const std::string&,
801  const std::string&,
802  const size_t
803  ) const
804  { }
805 
806  std::string format(
807  std::string,
808  const std::string&,
809 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
810  const std::string&,
811 #endif
812  const level&,
813  const std::string&,
814  const std::string&,
815  const size_t
816 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
817  ,
818  const size_t
819 #endif
820  ) const
821  { }
822 
823  void log(
824  const level&,
825  const std::string&,
826  const std::string&, const std::string&, size_t
827  ) const
828  { }
829 
830  template<class In>
831  void dump(
832  const level&,
833  const In, const In,
834  const std::string&, const std::string&, size_t,
835  const std::string&,
836  const std::string
837  ) const
838  { }
839 };
840 #pragma GCC diagnostic pop
841 #endif // WITH_CLUTCHLOG
842 
843 #endif // __CLUTCHLOG_H__
void threshold(level l)
Set the log level below which logs are not printed.
Definition: clutchlog.h:447
+
void out(std::ostream &out)
Set the output stream on which to print.
Definition: clutchlog.h:430
+
Definition: clutchlog.h:137
+
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:625
+
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:509
+
void func(std::string func)
Set the regular expression filtering the function location.
Definition: clutchlog.h:454
+
std::string format() const
Get the template string.
Definition: clutchlog.h:422
+
void line(std::string line)
Set the regular expression filtering the line location.
Definition: clutchlog.h:456
+
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:459
+
void format_comment(const std::string &format)
Set the template string for dumps.
Definition: clutchlog.h:425
+
std::string format_comment() const
Get the template string for dumps.
Definition: clutchlog.h:427
+
fmt()
 Empty constructor, only useful for a no-op formatter.
Definition: clutchlog.h:208
+
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:585
+
Structure holding a location matching.
Definition: clutchlog.h:348
+
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:366
+
#define CLUTCHLOG_DEFAULT_FORMAT
Default format of the messages.
Definition: clutchlog.h:49
+
fg
Foreground color codes.
Definition: clutchlog.h:173
+
level
Available log levels.
Definition: clutchlog.h:156
+
#define CLUTCHDUMP_DEFAULT_FORMAT
Default format of the comment line in file dump.
Definition: clutchlog.h:58
+
std::ostream & out()
Get the output stream on which to print.
Definition: clutchlog.h:432
+
friend std::ostream & operator<<(std::ostream &os, const fmt &fmt)
Output stream overload.
Definition: clutchlog.h:252
+
level level_of(const std::string name)
Return the log level tag corresponding to the given pre-configured name.
Definition: clutchlog.h:485
+
void style(level stage, FMT... styles)
Set the style (color and typo) of the given log level.
Definition: clutchlog.h:475
+
std::string operator()(const std::string &msg) const
Format the given string with the currently encoded format.
Definition: clutchlog.h:267
+
void style(level stage, fmt style)
Set the style (color and typo) of the given log level, passing a fmt instance.
Definition: clutchlog.h:477
+
std::ostream & print_on(std::ostream &os) const
Print the currently encoded format escape code on the given output stream.
Definition: clutchlog.h:222
+
static clutchlog & logger()
Get the logger instance.
Definition: clutchlog.h:149
+
typo
Typographic style codes.
Definition: clutchlog.h:199
+
bg
Background color codes.
Definition: clutchlog.h:186
+
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=CLUTCHDUMP_DEFAULT_SEP) const
Dump a serializable container after a comment line with log information.
Definition: clutchlog.h:650
+
void file(std::string file)
Set the regular expression filtering the file location.
Definition: clutchlog.h:452
+
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:574
+
fmt style(level stage) const
Get the configured fmt instance of the given log level.
Definition: clutchlog.h:479
+
#define CLUTCHDUMP_DEFAULT_SEP
Default item separator for dump.
Definition: clutchlog.h:64
+
level threshold() const
Get the log level below which logs are not printed.
Definition: clutchlog.h:449
+
Color and style formatter for ANSI terminal escape sequences.
Definition: clutchlog.h:170
+
+Directory dependency graph for tests:
+
+
tests
+ + + + + +