clutchlog 0.17
clutchlog.h
Go to the documentation of this file.
1#pragma once
2#ifndef CLUTCHLOG_H
4#define CLUTCHLOG_H
5
7#include <ciso646>
8 #ifdef FSEXPERIMENTAL
9 #include <experimental/filesystem>
10 namespace fs = std::experimental::filesystem;
11#else
12 #include <filesystem>
13 namespace fs = std::filesystem;
14#endif
15
16#include <iterator>
17#include <iostream>
18#include <sstream>
19#include <fstream>
20#include <cassert>
21#include <cstdlib>
22#include <string>
23#include <limits>
24#include <regex>
25#include <map>
26
28#if __has_include(<execinfo.h>) && __has_include(<stdlib.h>) && __has_include(<libgen.h>)
29 #include <execinfo.h> // execinfo
30 #include <stdlib.h> // getenv
31 #include <libgen.h> // basename
32 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 1
33#else
34 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 0
35#endif
36
38#if __has_include(<sys/ioctl.h>) && __has_include(<stdio.h>) && __has_include(<unistd.h>)
39 #include <sys/ioctl.h>
40 #include <stdio.h>
41 #include <unistd.h>
42 #define CLUTCHLOG_HAVE_UNIX_SYSIOCTL 1
43#else
44 #define CLUTCHLOG_HAVE_UNIX_SYSIOCTL 0
45#endif
46
47
48/**********************************************************************
49 * Enable by default in Debug builds.
50 **********************************************************************/
51#ifndef WITH_CLUTCHLOG
52 #ifndef NDEBUG
54 #define WITH_CLUTCHLOG
55 #endif
56#endif
57
58/**********************************************************************
59 * Macros definitions
60 **********************************************************************/
61#ifdef WITH_CLUTCHLOG
62
66#ifndef CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG
68 #define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress
69#endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT
70
78#define CLUTCHLOC __FILE__, __FUNCTION__, __LINE__
79
81#ifndef NDEBUG
82 #define CLUTCHLOGD( LEVEL, WHAT, DEPTH_DELTA ) do { \
83 auto& clutchlog__logger = clutchlog::logger(); \
84 std::ostringstream clutchlog__msg ; clutchlog__msg << WHAT; \
85 clutchlog__logger.log(clutchlog::level::LEVEL, clutchlog__msg.str(), CLUTCHLOC, DEPTH_DELTA); \
86 } while(0)
87#else // not Debug build.
88 #define CLUTCHLOGD( LEVEL, WHAT, DEPTH_DELTA ) do { \
89 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
90 auto& clutchlog__logger = clutchlog::logger(); \
91 std::ostringstream clutchlog__msg ; clutchlog__msg << WHAT; \
92 clutchlog__logger.log(clutchlog::level::LEVEL, clutchlog__msg.str(), CLUTCHLOC, DEPTH_DELTA); \
93 } \
94 } while(0)
95#endif // NDEBUG
96
98#ifndef NDEBUG
99 #define CLUTCHLOG( LEVEL, WHAT ) \
100 CLUTCHLOGD(LEVEL, WHAT, 0)
101#else // not Debug build.
102 #define CLUTCHLOG( LEVEL, WHAT ) \
103 CLUTCHLOGD(LEVEL, WHAT, 0)
104#endif // NDEBUG
105
107#ifndef NDEBUG
108 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do { \
109 auto& clutchlog__logger = clutchlog::logger(); \
110 clutchlog__logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
111 CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
112 } while(0)
113#else // not Debug build.
114 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do { \
115 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
116 auto& clutchlog__logger = clutchlog::logger(); \
117 clutchlog__logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
118 CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
119 } \
120 } while(0)
121#endif // NDEBUG
122
124#ifndef NDEBUG
125 #define CLUTCHFUNC( LEVEL, FUNC, ... ) do { \
126 auto& clutchlog__logger = clutchlog::logger(); \
127 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
128 if(clutchlog__scope.matches) { \
129 FUNC(__VA_ARGS__); \
130 } \
131 } while(0)
132#else // not Debug build.
133 #define CLUTCHFUNC( LEVEL, FUNC, ... ) do { \
134 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
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 FUNC(__VA_ARGS__); \
139 } \
140 } \
141 } while(0)
142#endif // NDEBUG
143
145#ifndef NDEBUG
146 #define CLUTCHCODE( LEVEL, ... ) do { \
147 auto& clutchlog__logger = clutchlog::logger(); \
148 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
149 if(clutchlog__scope.matches) { \
150 __VA_ARGS__ \
151 } \
152 } while(0)
153#else // not Debug build.
154 #define CLUTCHCODE( LEVEL, CODE ) do { \
155 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
156 auto& clutchlog__logger = clutchlog::logger(); \
157 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
158 if(clutchlog__scope.matches) { \
159 CODE \
160 } \
161 } \
162 } while(0)
163#endif // NDEBUG
164
167#else // not WITH_CLUTCHLOG
168 // Disabled macros can still be called in Release builds.
169 #define CLUTCHLOG( LEVEL, WHAT ) do {/*nothing*/} while(0)
170 #define CLUTCHLOGD( LEVEL, WHAT, DEPTH_DELTA ) do {/*nothing*/} while(0)
171 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do {/*nothing*/} while(0)
172 #define CLUTCHFUNC( LEVEL, FUNC, ... ) do {/*nothing*/} while(0)
173 #define CLUTCHCODE( LEVEL, CODE ) do {/*nothing*/} while(0)
174#endif // WITH_CLUTCHLOG
175
176/**********************************************************************
177 * Implementation
178 **********************************************************************/
179
180#ifdef WITH_CLUTCHLOG
189{
190 protected:
191
196 #ifndef NDEBUG
197 #ifndef CLUTCHLOG_DEFAULT_FORMAT
199 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1 // Enables: name, depth and depth_marks
200 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1 // Enables: hfill
201 #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg} {hfill} {func} @ {file}:{line}\n"
202 #else
203 #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
204 #endif
205 #else
206 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
207 #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg} {hfill} {func} @ {file}:{line}\n"
208 #else
209 #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
210 #endif
211 #endif
212 #endif
213 #else
214 #ifndef CLUTCHLOG_DEFAULT_FORMAT
216 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
217 #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg} {hfill} {func}\n"
218 #else
219 #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg}\t\t\t\t\t{func}\n"
220 #endif
221 #endif
222 #endif
224 static inline std::string default_format = CLUTCHLOG_DEFAULT_FORMAT;
225
226 #ifndef NDEBUG
227 #ifndef CLUTCHDUMP_DEFAULT_FORMAT
229 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
230 #define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth}) @ {file}:{line}"
231 #else
232 #define CLUTCHDUMP_DEFAULT_FORMAT "# {level} in {func} @ {file}:{line}"
233 #endif
234 #endif // CLUTCHDUMP_DEFAULT_FORMAT
235 #else
236 #ifndef CLUTCHDUMP_DEFAULT_FORMAT
238 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
239 #define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth})"
240 #else
241 #define CLUTCHDUMP_DEFAULT_FORMAT "# {level} in {func}"
242 #endif
243 #endif // CLUTCHDUMP_DEFAULT_FORMAT
244 #endif
247
248 #ifndef CLUTCHDUMP_DEFAULT_SEP
250 #define CLUTCHDUMP_DEFAULT_SEP "\n"
251 #endif // CLUTCHDUMP_DEFAULT_SEP
253 static inline std::string dump_default_sep = CLUTCHDUMP_DEFAULT_SEP;
254
255 #ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
257 #define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
258 #endif // CLUTCHLOG_DEFAULT_DEPTH_MARK
260 static inline std::string default_depth_mark = CLUTCHLOG_DEFAULT_DEPTH_MARK;
261
262 #ifndef CLUTCHLOG_STRIP_CALLS
264 #define CLUTCHLOG_STRIP_CALLS 5
265 #endif // CLUTCHLOG_STRIP_CALLS
267 static inline unsigned int default_strip_calls = CLUTCHLOG_STRIP_CALLS;
268
269 #ifndef CLUTCHLOG_DEFAULT_HFILL_MARK
271 #define CLUTCHLOG_DEFAULT_HFILL_MARK '.'
272 #endif // CLUTCHLOG_DEFAULT_HFILL_MARK
274 static inline char default_hfill_char = CLUTCHLOG_DEFAULT_HFILL_MARK;
275
276
277 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
278 #ifndef CLUTCHLOG_DEFAULT_HFILL_MAX
279 #define CLUTCHLOG_DEFAULT_HFILL_MAX 300
280 #endif
281 #ifndef CLUTCHLOG_DEFAULT_HFILL_MIN
282 #define CLUTCHLOG_DEFAULT_HFILL_MIN 150
283 #endif
284 #endif
286 static inline size_t default_hfill_max = CLUTCHLOG_DEFAULT_HFILL_MAX;
288 static inline size_t default_hfill_min = CLUTCHLOG_DEFAULT_HFILL_MIN;
289
290 // NOTE: there is no CLUTCHLOG_HFILL_STYLE for defaulting,
291 // but you can still set `hfill_style(...)` on the logger singleton.
292 /* @} DefaultConfig */
293 /* @} */
294
295
296 public:
297
308 {
309 static clutchlog instance;
310 return instance;
311 }
312
314 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
315
317 enum filename {path, base, dir, dirbase, stem, dirstem};
318
380 class fmt {
381 public:
383 enum class ansi {
385 colors_16 = -1, // Not supposed to be casted.
387 colors_256 = 5, // Casted as short in color::operator<<.
389 colors_16M = 2 // Casted as short in color::operator<<
391
393 enum class typo {
394 reset = 0,
395 bold = 1,
396 underline = 4,
397 inverse = 7,
398 none = -1
400
404 enum class fg {
405 black = 30,
406 red = 31,
407 green = 32,
408 yellow = 33,
409 blue = 34,
410 magenta = 35,
411 cyan = 36,
412 white = 37,
413 bright_black = 90,
414 bright_red = 91,
415 bright_green = 92,
416 bright_yellow = 93,
417 bright_blue = 94,
418 bright_magenta = 95,
419 bright_cyan = 96,
420 bright_white = 97,
421 none = -1
423
425 enum class bg {
426 black = 40,
427 red = 41,
428 green = 42,
429 yellow = 43,
430 blue = 44,
431 magenta = 45,
432 cyan = 46,
433 white = 47,
434 bright_black = 100,
435 bright_red = 101,
436 bright_green = 102,
437 bright_yellow = 103,
438 bright_blue = 104,
439 bright_magenta = 105,
440 bright_cyan = 106,
441 bright_white = 107,
442 none = -1
444
445 protected:
447 friend std::ostream& operator<<(std::ostream& os, const std::tuple<fg,bg,typo>& fbs)
448 {
449 auto [f,b,s] = fbs;
450 std::vector<short> codes; codes.reserve(3);
451 if(f != fg::none) { codes.push_back(static_cast<short>(f));}
452 if(b != bg::none) { codes.push_back(static_cast<short>(b));}
453 if(s != typo::none) { codes.push_back(static_cast<short>(s));}
454 if(codes.size() == 0) {
455 return os;
456
457 } else {
458 os << "\033[";
459 os << codes[0];
460 for(size_t i=1; i < codes.size(); ++i) {
461 os << ";" << codes[i];
462 }
463 os << "m";
464 }
465 return os;
466 }
467
469 friend std::ostream& operator<<(std::ostream& os, const typo& s)
470 {
471 if(s != typo::none) {
472 os << "\033[" << static_cast<short>(s) << "m";
473 }
474 return os;
475 }
476
479 protected:
484 struct color {
485 ansi mode; // Not const to allow for the implicit copy assignemnt operator.
486
488 enum class ground { // idem.
489 fore = 38,
490 back = 48
492
498 color(ansi a, ground g) : mode(a), type(g) {}
499
501 virtual bool is_set() const = 0;
502
504 virtual std::ostream& print_on( std::ostream& os) const = 0;
505
507 friend std::ostream& operator<<(std::ostream& os, const color& c)
508 {
509 if(c.is_set()) {
510 os << "\033[" << static_cast<short>(c.type) << ";" << static_cast<short>(c.mode) << ";";
511 c.print_on(os);
512 os << "m";
513 }
514 return os;
515 }
516 };
517
518 // There is no color_16 because it would be the same as color_256, only with different indices,
519 // hence making it more complicated for the user to select the right constructor.
520 // Here, we just use enum for 16 colors, and indices for 256 colors.
521
523 struct color_256 : public color {
527 short index;
528
532 color_256(ground t) : color(ansi::colors_256, t), index(-1) {}
533
539 color_256(ground t, const short i) : color(ansi::colors_256, t), index(i) {assert(-1 <= i and i <= 255);}
540
542 bool is_set() const {return index > -1;}
543
545 std::ostream& print_on( std::ostream& os) const
546 {
547 os << index;
548 return os;
549 }
550 };
551
553 struct fg_256 : public color_256 {
555 fg_256() : color_256(ground::fore) {}
556
560 fg_256(const short f) : color_256(ground::fore, f) {}
561
565 fg_256(const fg&) : color_256(ground::fore, -1) {}
566
568
570 struct bg_256 : public color_256 {
572 bg_256() : color_256(ground::back) {}
573
577 bg_256(const short b) : color_256(ground::back, b) {}
578
582 bg_256(const bg&) : color_256(ground::back, -1) {}
583
585
587 struct color_16M : public color {
591 short red, green, blue;
592
596 color_16M(ground t) : color(ansi::colors_16M, t), red(-1), green(-1), blue(-1) {}
597
605 color_16M(ground t, short r, short g, short b)
606 : color(ansi::colors_16M, t), red(r), green(g), blue(b) {}
607
615 color_16M(ground t, const std::string& srgb) : color(ansi::colors_16M, t)
616 {
617 assert(srgb.size() == 7);
618 if(srgb.size() != 7) {
619 red = -1;
620 green = -1;
621 blue = -1;
622 } else {
623 char i = 0;
624 if(srgb.at(0) == '#') {
625 i = 1;
626 }
627 std::istringstream(srgb.substr(0+i,2)) >> std::hex >> red;
628 std::istringstream(srgb.substr(2+i,2)) >> std::hex >> green;
629 std::istringstream(srgb.substr(4+i,2)) >> std::hex >> blue;
630 }
631 assert(-1 <= red and red <= 255);
632 assert(-1 <= green and green <= 255);
633 assert(-1 <= blue and blue <= 255);
634 }
635
637 bool is_set() const {return red > -1 and green > -1 and blue > -1;}
638
640 std::ostream& print_on( std::ostream& os) const
641 {
642 os << red << ";" << green << ";" << blue;
643 return os;
644 }
645 };
646
648 struct fg_16M : public color_16M {
650 fg_16M() : color_16M(ground::fore) {}
651
660 fg_16M(short r, short g, short b) : color_16M(ground::fore, r,g,b) {}
661
668 fg_16M(const std::string& srgb) : color_16M(ground::fore, srgb) {}
669
673 fg_16M(const fg&) : color_16M(ground::fore, -1,-1,-1) {}
674
676
678 struct bg_16M : public color_16M {
680 bg_16M() : color_16M(ground::back) {}
681
690 bg_16M(short r, short g, short b) : color_16M(ground::back, r,g,b) {}
691
698 bg_16M(const std::string& srgb) : color_16M(ground::back, srgb) {}
699
703 bg_16M(const bg&) : color_16M(ground::back, -1,-1,-1) {}
704
706
709 public:
711 fmt() : mode(ansi::colors_16), style(typo::none), fore(fg::none), back(bg::none) {}
712
715 explicit fmt( fg f, bg b = bg::none, typo s = typo::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
716 explicit fmt( fg f, typo s , bg b = bg::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
717 explicit fmt( bg b, fg f = fg::none, typo s = typo::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
718 explicit fmt( bg b, typo s , fg f = fg::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
719 explicit fmt(typo s, fg f = fg::none, bg b = bg::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
720 explicit fmt(typo s, bg b , fg f = fg::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
725 explicit fmt(const short f, const short b, typo s = typo::none) : mode(ansi::colors_256), style(s), fore_256(f), back_256(b) {}
726 explicit fmt(const short f, typo s = typo::none) : mode(ansi::colors_256), style(s), fore_256(f), back_256(bg::none) {}
727 explicit fmt(fg, const short b, typo s = typo::none) : mode(ansi::colors_256), style(s), fore_256(fg::none), back_256(b) {}
728 explicit fmt(const short f, bg, typo s = typo::none) : mode(ansi::colors_256), style(s), fore_256(f), back_256(bg::none) {}
733 explicit fmt(const short fr, const short fg, const short fb,
734 const short gr, const short gg, const short gb,
735 typo s = typo::none)
736 : mode(ansi::colors_16M), style(s), fore_16M(fr,fg,fb), back_16M(gr,gg,gb) {}
737 explicit fmt(fg,
738 const short gr, const short gg, const short gb,
739 typo s = typo::none)
740 : mode(ansi::colors_16M), style(s), fore_16M(fg::none), back_16M(gr,gg,gb) {}
741 explicit fmt(const short fr, const short fg, const short fb,
742 bg, typo s = typo::none)
743 : mode(ansi::colors_16M), style(s), fore_16M(fr,fg,fb), back_16M(bg::none) {}
744 explicit fmt(const short fr, const short fg, const short fb,
745 typo s = typo::none)
746 : mode(ansi::colors_16M), style(s), fore_16M(fr,fg,fb), back_16M(bg::none) {}
747
748 explicit fmt(const std::string& f, const std::string& b, typo s = typo::none)
749 : mode(ansi::colors_16M), style(s), fore_16M(f), back_16M(b) {}
750 explicit fmt(fg, const std::string& b, typo s = typo::none)
751 : mode(ansi::colors_16M), style(s), fore_16M(fg::none), back_16M(b) {}
752 explicit fmt(const std::string& f, bg, typo s = typo::none)
753 : mode(ansi::colors_16M), style(s), fore_16M(f), back_16M(bg::none) {}
754 explicit fmt(const std::string& f, typo s = typo::none)
755 : mode(ansi::colors_16M), style(s), fore_16M(f), back_16M(bg::none) {}
758 protected:
759
761 std::ostream& print_on( std::ostream& os) const
762 {
763 if(mode == ansi::colors_16) {
764 // Print all in a single escape.
765 os << std::make_tuple(fore,back,style);
766
767 } else {
768 // 256 or 16M: always print separated escapes for foreground/background.
769 if(mode == ansi::colors_256) {
770 os << fore_256;
771 os << back_256;
772
773 } else if(mode == ansi::colors_16M) {
774 os << fore_16M;
775 os << back_16M;
776 }
777 // In any case, print the style separately.
778 os << style;
779 }
780 return os;
781 }
782
783 public:
795 friend std::ostream& operator<<(std::ostream& os, const fmt& fmt)
796 {
797 return fmt.print_on(os);
798 }
799
810 std::string operator()( const std::string& msg ) const
811 {
812 std::ostringstream os;
813 this->print_on(os);
814 fmt reset(fmt::typo::reset);
815 os << msg;
816 reset.print_on(os);
817 return os.str();
818 }
819
822 std::string str() const
823 {
824 std::ostringstream os;
825 this->print_on(os);
826 return os.str();
827 }
828
829 static fmt hash( const std::string& str, const std::vector<fmt> domain = {})
830 {
831 size_t h = std::hash<std::string>{}(str);
832 if(domain.size() == 0) {
833 return fmt(static_cast<short>(h % 256));
834 } else {
835 return domain[h % domain.size()];
836 }
837 }
838 }; // fmt class
839
845 public:
846 clutchlog(clutchlog const&) = delete;
847 void operator=(clutchlog const&) = delete;
848
849 private:
850 clutchlog() :
851 // system, main, log
854 {level::critical,"Critical"},
855 {level::error ,"Error"},
856 {level::warning ,"Warning"},
857 {level::progress,"Progress"},
858 {level::note ,"Note"},
859 {level::info ,"Info"},
860 {level::debug ,"Debug"},
861 {level::xdebug ,"XDebug"}
862 }),
864 {level::critical, "Crit"},
865 {level::error , "Erro"},
866 {level::warning , "Warn"},
867 {level::progress, "Prog"},
868 {level::note , "Note"},
869 {level::info , "Info"},
870 {level::debug , "Dbug"},
871 {level::xdebug , "XDbg"}
872 }),
873 _level_fmt({
874 {level::critical,fmt(fmt::fg::red, fmt::typo::underline)},
875 {level::error ,fmt(fmt::fg::red, fmt::typo::bold)},
876 {level::warning ,fmt(fmt::fg::magenta, fmt::typo::bold)},
877 {level::progress,fmt()},
878 {level::note ,fmt()},
879 {level::info ,fmt()},
880 {level::debug ,fmt()},
881 {level::xdebug ,fmt()}
882 }),
885 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
887 _hfill_fmt(fmt::fg::none),
890 #endif
891 _out(&std::clog),
892 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
893 _depth(std::numeric_limits<size_t>::max() - _strip_calls),
895 #endif
896 _stage(level::error),
897 _in_file(".*"),
898 _in_func(".*"),
899 _in_line(".*"),
900 // Empty vectors by default:
901 // _filehash_fmts
902 // _funchash_fmts
903 // _depth_fmts
904 _filename(filename::path)
905 {
906 // Reverse the level->word map into a word->level map.
907 for(auto& lw : _level_word) {
908 _word_level[lw.second] = lw.first;
909 }
910#if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
911 struct winsize w;
912 ioctl(STDERR_FILENO, TIOCGWINSZ, &w);
913 _nb_columns = std::max(std::min((size_t)w.ws_col, default_hfill_max), default_hfill_min);
914#endif
915 }
916
917 protected:
921 const std::map<level,std::string> _level_word;
923 std::map<std::string,level> _word_level;
925 std::map<level,std::string> _level_short;
927 std::map<level,fmt> _level_fmt;
929 std::string _format_log;
931 std::string _format_dump;
932 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
934 char _hfill_char;
936 fmt _hfill_fmt;
938 size_t _hfill_max;
940 size_t _hfill_min;
941 #endif
943 std::ostream* _out;
944 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
946 size_t _depth;
948 std::string _depth_mark;
949 #endif
953 std::regex _in_file;
955 std::regex _in_func;
957 std::regex _in_line;
958
960 std::vector<fmt> _filehash_fmts;
962 std::vector<fmt> _funchash_fmts;
963
964#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
966 static const size_t _max_buffer = 4096;
968 std::vector<fmt> _depth_fmts;
969#endif
970
971#if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
973 size_t _nb_columns;
974#endif
975
980 public:
981
986 void format(const std::string& format) {_format_log = format;}
988 std::string format() const {return _format_log;}
989
991 void format_comment(const std::string& format) {_format_dump = format;}
993 std::string format_comment() const {return _format_dump;}
994
996 void out(std::ostream& out) {_out = &out;}
998 std::ostream& out() {return *_out;}
999
1000#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
1002 void depth(size_t d) {_depth = d;}
1004 size_t depth() const {return _depth;}
1005
1007 void depth_mark(const std::string mark) {_depth_mark = mark;}
1009 std::string depth_mark() const {return _depth_mark;}
1010
1012 void strip_calls(const size_t n) {_strip_calls = n;}
1014 size_t strip_calls() const {return _strip_calls;}
1015#endif
1016#if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
1018 void hfill_mark(const char mark) {_hfill_char = mark;}
1020 char hfill_mark() const {return _hfill_char;}
1022 void hfill_style(fmt style) {_hfill_fmt = style;}
1027 template<class ... FMT>
1028 void hfill_style(FMT... styles) { this->hfill_style(fmt(styles...)); }
1030 fmt hfill_style() const {return _hfill_fmt;}
1032 void hfill_max(const size_t nmax) {_hfill_max = nmax;}
1034 size_t hfill_max() {return _hfill_max;}
1036 void hfill_min(const size_t nmin) {_hfill_min = nmin;}
1038 size_t hfill_min() {return _hfill_min;}
1039#endif
1047 void filehash_styles(std::vector<fmt> styles) {_filehash_fmts = styles;}
1055 void funchash_styles(std::vector<fmt> styles) {_funchash_fmts = styles;}
1064 void depth_styles(std::vector<fmt> styles) {_depth_fmts = styles;}
1065
1067 void threshold(level l) {_stage = l;}
1069 void threshold(const std::string& l) {_stage = this->level_of(l);}
1071 level threshold() const {return _stage;}
1073 const std::map<std::string,level>& levels() const { return _word_level;}
1074
1079 level level_of(const std::string name)
1080 {
1081 const auto ilevel = _word_level.find(name);
1082 if( ilevel != std::end(_word_level)) {
1083 return ilevel->second;
1084 } else {
1085 throw std::out_of_range("'" + name + "' is not a valid log level name");
1086 }
1087 }
1088
1090 void file(std::string file) {_in_file = file;}
1092 void func(std::string func) {_in_func = func;}
1094 void line(std::string line) {_in_line = line;}
1095
1098 const std::string& in_file,
1099 const std::string& in_function=".*",
1100 const std::string& in_line=".*"
1101 )
1102 {
1103 file(in_file);
1104 func(in_function);
1105 line(in_line);
1106 }
1107
1112 template<class ... FMT>
1113 void style(level stage, FMT... styles) { this->style(stage,fmt(styles...)); }
1115 void style(level stage, fmt style) { _level_fmt.at(stage) = style; }
1117 fmt style(level stage) const { return _level_fmt.at(stage); }
1118
1121
1124 public:
1125
1130 struct scope_t {
1135#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
1137 size_t depth;
1138#endif
1140 bool there;
1143 matches(false),
1144 stage(level::xdebug),
1146 depth(0),
1147#endif
1148 there(false)
1149 {}
1150 }; // scope_t
1151
1152
1155 const level& stage,
1156 const std::string& file,
1157 const std::string& func,
1158 const size_t line
1159 ) const
1160 {
1161 scope_t scope; // False scope by default.
1162
1163 /***** Log level stage *****/
1164 // Test stage first, because it's fastest.
1165 scope.stage = stage;
1166 if(not (scope.stage <= _stage)) {
1167 // Bypass useless computations if no match
1168 // because of the stage.
1169 return scope;
1170 }
1171#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
1172 /***** Stack depth *****/
1173 // Backtrace in second, quite fast.
1174 size_t stack_depth;
1175 void *buffer[_max_buffer];
1176 stack_depth = backtrace(buffer, _max_buffer);
1177 scope.depth = stack_depth;
1178 if(not (scope.depth <= _depth + _strip_calls)) {
1179 // Bypass if no match.
1180 return scope;
1181 }
1182#endif
1183
1184 /***** Location *****/
1185 // Location last, slowest.
1186 std::ostringstream sline; sline << line;
1187 scope.there =
1188 std::regex_search(file, _in_file)
1189 and std::regex_search(func, _in_func)
1190 and std::regex_search(sline.str(), _in_line);
1191
1192 // No need to retest stage and depth, which are true here.
1193 scope.matches = scope.there;
1194
1195 return scope;
1196 } // locate
1197
1205 std::string replace(
1206 const std::string& form,
1207 const std::string& mark,
1208 const std::string& tag
1209 ) const
1210 {
1211 // Useless debug code, unless something fancy would be done with name tags.
1212 // std::regex re;
1213 // try {
1214 // re = std::regex(mark);
1215 //
1216 // } catch(const std::regex_error& e) {
1217 // std::cerr << "ERROR with a regular expression \"" << mark << "\": ";
1218 // switch(e.code()) {
1219 // case std::regex_constants::error_collate:
1220 // std::cerr << "the expression contains an invalid collating element name";
1221 // break;
1222 // case std::regex_constants::error_ctype:
1223 // std::cerr << "the expression contains an invalid character class name";
1224 // break;
1225 // case std::regex_constants::error_escape:
1226 // std::cerr << "the expression contains an invalid escaped character or a trailing escape";
1227 // break;
1228 // case std::regex_constants::error_backref:
1229 // std::cerr << "the expression contains an invalid back reference";
1230 // break;
1231 // case std::regex_constants::error_brack:
1232 // std::cerr << "the expression contains mismatched square brackets ('[' and ']')";
1233 // break;
1234 // case std::regex_constants::error_paren:
1235 // std::cerr << "the expression contains mismatched parentheses ('(' and ')')";
1236 // break;
1237 // case std::regex_constants::error_brace:
1238 // std::cerr << "the expression contains mismatched curly braces ('{' and '}')";
1239 // break;
1240 // case std::regex_constants::error_badbrace:
1241 // std::cerr << "the expression contains an invalid range in a {} expression";
1242 // break;
1243 // case std::regex_constants::error_range:
1244 // std::cerr << "the expression contains an invalid character range (e.g. [b-a])";
1245 // break;
1246 // case std::regex_constants::error_space:
1247 // std::cerr << "there was not enough memory to convert the expression into a finite state machine";
1248 // break;
1249 // case std::regex_constants::error_badrepeat:
1250 // std::cerr << "one of *?+{ was not preceded by a valid regular expression";
1251 // break;
1252 // case std::regex_constants::error_complexity:
1253 // std::cerr << "the complexity of an attempted match exceeded a predefined level";
1254 // break;
1255 // case std::regex_constants::error_stack:
1256 // std::cerr << "there was not enough memory to perform a match";
1257 // break;
1258 // default:
1259 // std::cerr << "unknown error";
1260 // }
1261 // std::cerr << std::endl;
1262 // throw;
1263 // } // catch
1264
1265 const std::regex re(mark);
1266 return std::regex_replace(form, re, tag);
1267 }
1268
1270 std::string replace(
1271 const std::string& form,
1272 const std::string& mark,
1273 const size_t tag
1274 ) const
1275 {
1276 std::ostringstream stag; stag << tag;
1277 return replace(form, mark, stag.str());
1278 }
1279
1281 std::string format(
1282 std::string row,
1283 const std::string& what,
1285 const std::string& name,
1286#endif
1287 const level& stage,
1288 const std::string& file,
1289 const std::string& func,
1290 const size_t line
1292 ,
1293 const size_t depth
1294#endif
1295 ) const
1296 {
1297 row = replace(row, "\\{msg\\}", what);
1298
1299 const std::filesystem::path filepath(file);
1300 std::string filename;
1301 std::filesystem::path::iterator ip = filepath.end();
1302 std::advance(ip, -2);
1303 switch(_filename) {
1304 case filename::base:
1305 filename = filepath.filename().string();
1306 break;
1307 case filename::dir:
1308 filename = ip->string();
1309 break;
1310 case filename::dirbase:
1311 filename = (*ip / filepath.filename()).string();
1312 break;
1313 case filename::stem:
1314 filename = filepath.stem().string();
1315 break;
1316 case filename::dirstem:
1317 filename = (*ip / filepath.stem()).string();
1318 break;
1319 case filename::path:
1320 default:
1321 filename = file;
1322 break;
1323 }
1324 row = replace(row, "\\{file\\}", filename);
1325
1326
1327 row = replace(row, "\\{func\\}", func);
1328 row = replace(row, "\\{line\\}", line);
1329
1330 row = replace(row, "\\{level\\}", _level_word.at(stage));
1331 std::string letter(1, _level_word.at(stage).at(0)); // char -> string
1332 row = replace(row, "\\{level_letter\\}", letter);
1333 row = replace(row, "\\{level_short\\}", _level_short.at(stage));
1334
1335#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
1336 size_t actual_depth = depth - _strip_calls;
1337 row = replace(row, "\\{name\\}", name);
1338 row = replace(row, "\\{depth\\}", actual_depth);
1339
1340 if(_depth_fmts.size() == 0) {
1341 row = replace(row, "\\{depth_fmt\\}", fmt(actual_depth % 256).str() );
1342
1343 std::ostringstream chevrons;
1344 for(size_t i = 0; i < actual_depth; ++i) {
1345 chevrons << _depth_mark;
1346 }
1347 row = replace(row, "\\{depth_marks\\}", chevrons.str());
1348
1349 } else {
1350 row = replace(row, "\\{depth_fmt\\}",
1351 _depth_fmts[std::min(actual_depth,_depth_fmts.size()-1)].str() );
1352
1353 std::ostringstream chevrons;
1354 for(size_t i = 0; i < actual_depth; ++i) {
1355 chevrons << _depth_fmts[std::min(i+1,_depth_fmts.size()-1)].str()
1356 << _depth_mark;
1357 }
1358 row = replace(row, "\\{depth_marks\\}", chevrons.str());
1359 }
1360#endif
1361 row = replace(row, "\\{level_fmt\\}", _level_fmt.at(stage).str());
1362 row = replace(row, "\\{filehash_fmt\\}", fmt::hash(file, _filehash_fmts).str() );
1363 row = replace(row, "\\{funchash_fmt\\}", fmt::hash(func, _funchash_fmts).str() );
1364
1365#if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
1366 // hfill is replaced last to allow for correct line width estimation.
1367 const std::string raw_row = replace(row, "(\\x9B|\\x1B\\[)[0-?]*[ -\\/]*[@-~]", "");
1368 const std::string hfill_tag = "{hfill}";
1369 const size_t hfill_pos = row.find(hfill_tag);
1370 const size_t raw_hfill_pos = raw_row.find(hfill_tag);
1371 const size_t nb_columns = std::max(std::min((size_t)_nb_columns, _hfill_max), _hfill_min);
1372 if(hfill_pos != std::string::npos) {
1373 assert(raw_hfill_pos != std::string::npos);
1374 if(nb_columns > 0) {
1375 const size_t left_len = raw_hfill_pos;
1376 const size_t right_len = raw_row.size() - raw_hfill_pos - hfill_tag.size();
1377 if(right_len+left_len > nb_columns) {
1378 // The right part would go over the terminal width: add a new row.
1379 if(right_len < nb_columns) {
1380 // There is room for the right part on a new line.
1381 const std::string hfill(std::max((size_t)0, nb_columns-right_len), _hfill_char);
1382 const std::string hfill_styled = _hfill_fmt(hfill);
1383 row = replace(row, "\\{hfill\\}", "\n"+hfill_styled);
1384 } else {
1385 // Right part still goes over columns: let it go.
1386 const std::string hfill(1, _hfill_char);
1387 const std::string hfill_styled = _hfill_fmt(hfill);
1388 row = replace(row, "\\{hfill\\}", "\n"+hfill_styled);
1389 }
1390 } else {
1391 // There is some space in between left and right parts.
1392 const std::string hfill(std::max((size_t)0, nb_columns - (right_len+left_len)), _hfill_char);
1393 const std::string hfill_styled = _hfill_fmt(hfill);
1394 row = replace(row, "\\{hfill\\}", hfill_styled);
1395 }
1396 } else {
1397 // We don't know the terminal width.
1398 const std::string hfill(1, _hfill_char);
1399 const std::string hfill_styled = _hfill_fmt(hfill);
1400 row = replace(row, "\\{hfill\\}", hfill_styled);
1401 }
1402 }
1403#else
1404 // We cannot know the terminal width.
1405 const std::string hfill(1, _hfill_char);
1406 const std::string hfill_styled = _hfill_fmt(hfill);
1407 row = replace(row, "\\{hfill\\}", hfill_styled);
1408#endif
1409 return _level_fmt.at(stage)(row);
1410 }
1411
1413 void log(
1414 const level& stage,
1415 const std::string& what,
1416 const std::string& file, const std::string& func, const size_t line,
1417 const size_t depth_delta = 0
1418 ) const
1419 {
1420 scope_t scope = locate(stage, file, func, line);
1421
1422 if(scope.matches) {
1423#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
1424 *_out << format(_format_log, what, basename(getenv("_")),
1425 stage, file, func,
1426 line, scope.depth + depth_delta );
1427#else
1428 *_out << format(_format_log, what,
1429 stage, file, func,
1430 line );
1431#endif
1432 _out->flush();
1433 } // if scopes.matches
1434 }
1435
1437 template<class In>
1438 void dump(
1439 const level& stage,
1440 const In container_begin, const In container_end,
1441 const std::string& file, const std::string& func, const size_t line,
1442 const std::string& filename_template = "dump_{n}.dat",
1443 const std::string sep = dump_default_sep
1444 ) const
1445 {
1446 scope_t scope = locate(stage, file, func, line);
1447
1448 if(scope.matches) {
1449 const std::string tag = "\\{n\\}";
1450 const std::regex re(tag);
1451 std::string outfile = "";
1452
1453 // If the file name template has the {n} tag.
1454 if(std::regex_search(filename_template, re)) {
1455 // Increment n until a free one is found.
1456 size_t n = 0;
1457 do {
1458 outfile = replace(filename_template, tag, n);
1459 n++;
1460 } while( fs::exists( outfile ) );
1461
1462 } else {
1463 // Use the parameter as is.
1464 outfile = filename_template;
1465 }
1466
1467 std::ofstream fd(outfile);
1468
1469 if(_format_dump.size() > 0) {
1470#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
1471 fd << format(_format_dump, "", basename(getenv("_")),
1472 stage, file, func,
1473 line, scope.depth );
1474#else
1475 fd << format(_format_dump, "",
1476 stage, file, func,
1477 line );
1478#endif
1479 fd << sep; // sep after comment line.
1480 }
1481
1482 std::copy(container_begin, container_end,
1483 std::ostream_iterator<typename In::value_type>(fd, sep.c_str()));
1484
1485 fd.close();
1486 } // if scopes.matches
1487 }
1488
1490};
1491
1494#else // not WITH_CLUTCHLOG
1495
1496
1497/**********************************************************************
1498 * Fake implementation
1499 **********************************************************************/
1500
1501// Equivalent class with empty methods, will be optimized out
1502// while allowing to actually have calls implemented without WITH_CLUTCHLOG guards.
1503#pragma GCC diagnostic push
1504#pragma GCC diagnostic ignored "-Wreturn-type"
1505class clutchlog
1506{
1507 public:
1508 static clutchlog& logger() {
1509 static clutchlog instance;
1510 return instance;
1511 }
1512 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
1513 enum filename {path, base, dir, dirbase, stem, dirstem};
1514 class fmt {
1515 public:
1516 enum class ansi { colors_16, colors_256, colors_16M} mode;
1517 enum class typo { reset, bold, underline, inverse, none} style;
1518 enum class fg { black, red, green, yellow, blue, magenta, cyan, white, bright_black, bright_red, bright_green, bright_yellow, bright_blue, bright_magenta, bright_cyan, bright_white, none} fore;
1519 enum class bg { black, red, green, yellow, blue, magenta, cyan, white, bright_black, bright_red, bright_green, bright_yellow, bright_blue, bright_magenta, bright_cyan, bright_white, none } back;
1520 protected:
1521 friend std::ostream& operator<<(std::ostream&, const std::tuple<fg,bg,typo>&) {}
1522 friend std::ostream& operator<<(std::ostream&, const typo&) {}
1523 protected:
1524 struct color {
1525 ansi mode;
1526 enum class ground { fore, back } type;
1527 color(ansi a, ground g) : mode(a), type(g) {}
1528 virtual bool is_set() const = 0;
1529 virtual std::ostream& print_on( std::ostream&) const = 0;
1530 friend std::ostream& operator<<(std::ostream&, const color&) {}
1531 };
1532 struct color_256 : public color {
1533 short index;
1534 color_256(ground t) : color(ansi::colors_256, t), index(-1) {}
1535 color_256(ground t, const short i) : color(ansi::colors_256, t), index(i) {}
1536 bool is_set() const {}
1537 std::ostream& print_on( std::ostream&) const {}
1538 };
1539 struct fg_256 : public color_256 {
1540 fg_256() : color_256(ground::fore) {}
1541 fg_256(const short f) : color_256(ground::fore, f) {}
1542 fg_256(const fg&) : color_256(ground::fore, -1) {}
1543 } fore_256;
1544 struct bg_256 : public color_256 {
1545 bg_256() : color_256(ground::back) {}
1546 bg_256(const short b) : color_256(ground::back, b) {}
1547 bg_256(const bg&) : color_256(ground::back, -1) {}
1548 } back_256;
1549 struct color_16M : public color {
1550 short red, green, blue;
1551 color_16M(ground t) : color(ansi::colors_16M, t), red(-1), green(-1), blue(-1) {}
1552 color_16M(ground t, short r, short g, short b) : color(ansi::colors_16M, t), red(r), green(g), blue(b) {}
1553 color_16M(ground t, const std::string&) : color(ansi::colors_16M, t) {}
1554 bool is_set() const {return red > -1 and green > -1 and blue > -1;}
1555 std::ostream& print_on( std::ostream&) const {}
1556 };
1557 struct fg_16M : public color_16M {
1558 fg_16M() : color_16M(ground::fore) {}
1559 fg_16M(short r, short g, short b) : color_16M(ground::fore, r,g,b) {}
1560 fg_16M(const std::string& srgb) : color_16M(ground::fore, srgb) {}
1561 fg_16M(const fg&) : color_16M(ground::fore, -1,-1,-1) {}
1562 } fore_16M;
1563 struct bg_16M : public color_16M {
1564 bg_16M() : color_16M(ground::back) {}
1565 bg_16M(short r, short g, short b) : color_16M(ground::back, r,g,b) {}
1566 bg_16M(const std::string& srgb) : color_16M(ground::back, srgb) {}
1567 bg_16M(const bg&) : color_16M(ground::back, -1,-1,-1) {}
1568 } back_16M;
1569 public:
1570 fmt() : mode(ansi::colors_16), style(typo::none), fore(fg::none), back(bg::none) {}
1571 fmt( fg f, bg b = bg::none, typo s = typo::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
1572 fmt( fg f, typo s , bg b = bg::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
1573 fmt( bg b, fg f = fg::none, typo s = typo::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
1574 fmt( bg b, typo s , fg f = fg::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
1575 fmt(typo s, fg f = fg::none, bg b = bg::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
1576 fmt(typo s, bg b , fg f = fg::none) : mode(ansi::colors_16), style(s), fore(f), back(b) {}
1577 fmt(fg_256 f, bg_256 b, typo s = typo::none) : mode(ansi::colors_256), style(s), fore_256(f), back_256(b) {}
1578 fmt(fg_256 f, typo s = typo::none) : mode(ansi::colors_256), style(s), fore_256(f), back_256(bg::none) {}
1579 fmt(fg, bg_256 b, typo s = typo::none) : mode(ansi::colors_256), style(s), fore_256(fg::none), back_256(b) {}
1580 fmt(const short fr, const short fg, const short fb,
1581 const short gr, const short gg, const short gb,
1582 typo s = typo::none)
1583 : mode(ansi::colors_16M), style(s), fore_16M(fr,fg,fb), back_16M(gr,gg,gb) {}
1584 fmt(fg,
1585 const short gr, const short gg, const short gb,
1586 typo s = typo::none)
1587 : mode(ansi::colors_16M), style(s), fore_16M(fg::none), back_16M(gr,gg,gb) {}
1588 fmt(const short fr, const short fg, const short fb,
1589 bg, typo s = typo::none)
1590 : mode(ansi::colors_16M), style(s), fore_16M(fr,fg,fb), back_16M(bg::none) {}
1591 fmt(const short fr, const short fg, const short fb,
1592 typo s = typo::none)
1593 : mode(ansi::colors_16M), style(s), fore_16M(fr,fg,fb), back_16M(bg::none) {}
1594
1595 fmt(const std::string& f, const std::string& b, typo s = typo::none)
1596 : mode(ansi::colors_16M), style(s), fore_16M(f), back_16M(b) {}
1597 fmt(fg, const std::string& b, typo s = typo::none)
1598 : mode(ansi::colors_16M), style(s), fore_16M(fg::none), back_16M(b) {}
1599 fmt(const std::string& f, bg, typo s = typo::none)
1600 : mode(ansi::colors_16M), style(s), fore_16M(f), back_16M(bg::none) {}
1601 fmt(const std::string& f, typo s = typo::none)
1602 : mode(ansi::colors_16M), style(s), fore_16M(f), back_16M(bg::none) {}
1603 protected:
1604 std::ostream& print_on( std::ostream&) const {}
1605 public:
1606 friend std::ostream& operator<<(std::ostream& os, const fmt&) { return os; }
1607 std::string operator()( const std::string& msg) const { return msg; }
1608 std::string str() const { return ""; }
1609 static fmt hash( const std::string&, const std::vector<fmt>) {}
1610 };
1611 public:
1612 clutchlog(clutchlog const&) = delete;
1613 void operator=(clutchlog const&) = delete;
1614 private:
1615 clutchlog() {}
1616 protected:
1617 struct scope_t {};
1618 scope_t locate(
1619 const level&,
1620 const std::string&,
1621 const std::string&,
1622 const size_t
1623 ) const
1624 {}
1625 public:
1626 void format(const std::string&) {}
1627 std::string format() const { return ""; }
1628
1629 void format_comment(const std::string&) {}
1630 std::string format_comment() const { return ""; }
1631
1632 void out(std::ostream&) {}
1633 std::ostream& out() {}
1634
1635#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
1636 void depth(size_t) {}
1637 size_t depth() const { return 0; }
1638
1639 void depth_mark(const std::string) {}
1640 std::string depth_mark() const { return ""; }
1641 void strip_calls(const size_t) {}
1642 size_t strip_calls() const { return 0; }
1643#endif
1644#if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
1645 void hfill_mark(const char) {}
1646 char hfill_mark() const { return '\0'; }
1647 void hfill_fmt(fmt) {}
1648 fmt hfill_fmt() const { return fmt(); }
1649 void hfill_min(const size_t) {}
1650 size_t hfill_min() { return 0; }
1651 void hfill_max(const size_t) {}
1652 size_t hfill_max() { return 0; }
1653#endif
1654 void filehash_styles(std::vector<fmt> ) {}
1655 void funchash_styles(std::vector<fmt> ) {}
1656 void depth_styles(std::vector<fmt>) {}
1657
1658 void threshold(level) {}
1659 void threshold(const std::string&) {}
1660 level threshold() const { return level::error; }
1661 const std::map<std::string,level> levels() const {}
1662 level level_of(const std::string) { return level::error; }
1663
1664 void file(std::string) {}
1665 void func(std::string) {}
1666 void line(std::string) {}
1667
1668#pragma GCC diagnostic push
1669#pragma GCC diagnostic ignored "-Wunused-parameter"
1670 void location(
1671 const std::string&,
1672 const std::string& in_function=".*",
1673 const std::string& in_line=".*"
1674 )
1675 {}
1676#pragma GCC diagnostic pop
1677 template<class ... FMT>
1678 void style(level, FMT...) {}
1679 void style(level, fmt) {}
1680 fmt style(level) const { return fmt(); }
1681 void filename(filename) {}
1682 public:
1683 std::string replace(
1684 const std::string& form,
1685 const std::string&,
1686 const std::string&
1687 ) const
1688 {
1689 return form;
1690 }
1691
1692 std::string replace(
1693 const std::string& form,
1694 const std::string&,
1695 const size_t
1696 ) const
1697 {
1698 return form;
1699 }
1700
1701 std::string format(
1702 std::string row,
1703 const std::string&,
1705 const std::string&,
1706#endif
1707 const level&,
1708 const std::string&,
1709 const std::string&,
1710 const size_t
1712 ,
1713 const size_t
1714#endif
1715 ) const
1716 {
1717 return row;
1718 }
1719
1720 void log(
1721 const level&,
1722 const std::string&,
1723 const std::string&, const std::string&, size_t
1724 ) const
1725 {}
1726
1727 template<class In>
1728 void dump(
1729 const level&,
1730 const In, const In,
1731 const std::string&, const std::string&, size_t,
1732 const std::string&,
1733 const std::string
1734 ) const
1735 {}
1736};
1737#pragma GCC diagnostic pop
1738#endif // WITH_CLUTCHLOG
1739
1740#endif // CLUTCHLOG_H
Color and style formatter for ANSI terminal escape sequences.
Definition: clutchlog.h:380
enum clutchlog::fmt::ansi mode
Current ANSI color mode.
friend std::ostream & operator<<(std::ostream &os, const fmt &fmt)
Output stream overload.
Definition: clutchlog.h:795
enum clutchlog::fmt::typo style
Typographic style.
fmt()
Empty constructor, only useful for a no-op formatter.
Definition: clutchlog.h:711
ansi
ANSI code configuring the available number of colors.
Definition: clutchlog.h:383
@ colors_16M
16 millions ("true") colors mode.
@ colors_16
16 colors mode.
@ colors_256
256 colors mode.
typo
Typographic style codes.
Definition: clutchlog.h:393
std::string str() const
Return the formatting code as a string.
Definition: clutchlog.h:822
std::ostream & print_on(std::ostream &os) const
Print the currently encoded format escape code on the given output stream.
Definition: clutchlog.h:761
std::string operator()(const std::string &msg) const
Format the given string with the currently encoded format.
Definition: clutchlog.h:810
The single class which holds everything.
Definition: clutchlog.h:189
filename _filename
Filename rendering method.
Definition: clutchlog.h:977
void depth_styles(std::vector< fmt > styles)
Set the styles for value-dependant depth formatting.
Definition: clutchlog.h:1064
std::map< level, std::string > _level_short
dictionary of level identifier to their 4-letters representation.
Definition: clutchlog.h:925
std::vector< fmt > _funchash_fmts
List of candidate format objects for value-dependant function name styling.
Definition: clutchlog.h:962
static std::string default_format
Default format of the messages.
Definition: clutchlog.h:224
void file(std::string file)
Set the regular expression filtering the file location.
Definition: clutchlog.h:1090
level
Available log levels.
Definition: clutchlog.h:314
std::regex _in_func
Current function location filter.
Definition: clutchlog.h:955
void log(const level &stage, const std::string &what, const std::string &file, const std::string &func, const size_t line, const size_t depth_delta=0) const
Print a log message IF the location matches the given one.
Definition: clutchlog.h:1413
std::ostream * _out
Standard output.
Definition: clutchlog.h:943
static unsigned int default_strip_calls
Number of call stack levels to remove from depth display by default.
Definition: clutchlog.h:267
void format_comment(const std::string &format)
Set the template string for dumps.
Definition: clutchlog.h:991
static std::string default_depth_mark
Default mark for stack depth.
Definition: clutchlog.h:260
std::vector< fmt > _filehash_fmts
List of candidate format objects for value-dependant file name styling.
Definition: clutchlog.h:960
size_t _strip_calls
Current number of call stack levels to remove from depth display.
Definition: clutchlog.h:919
void threshold(level l)
Set the log level (below which logs are not printed) with an identifier.
Definition: clutchlog.h:1067
std::regex _in_line
Current line location filter.
Definition: clutchlog.h:957
fmt style(level stage) const
Get the configured fmt instance of the given log level.
Definition: clutchlog.h:1117
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:1154
static size_t default_hfill_min
Default minimum width (number of characters) at which to fill for right-aligning the right part of me...
Definition: clutchlog.h:288
std::string _format_dump
Current format of the file output.
Definition: clutchlog.h:931
void format(const std::string &format)
Set the template string.
Definition: clutchlog.h:986
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:1097
static clutchlog & logger()
Get the logger instance.
Definition: clutchlog.h:307
static char default_hfill_char
Default character used as a filling for right-align the right part of messages with "{hfill}".
Definition: clutchlog.h:274
void threshold(const std::string &l)
Set the log level (below which logs are not printed) with a string.
Definition: clutchlog.h:1069
std::string _format_log
Current format of the standard output.
Definition: clutchlog.h:929
void out(std::ostream &out)
Set the output stream on which to print.
Definition: clutchlog.h:996
filename
Available filename rendering methods.
Definition: clutchlog.h:317
void filename(filename f)
Sets the file naming scheme. *‍/.
Definition: clutchlog.h:1120
const std::map< std::string, level > & levels() const
Get the map of available log levels string representations toward their identifier....
Definition: clutchlog.h:1073
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:1205
void line(std::string line)
Set the regular expression filtering the line location.
Definition: clutchlog.h:1094
std::string format_comment() const
Get the template string for dumps.
Definition: clutchlog.h:993
const std::map< level, std::string > _level_word
Dictionary of level identifier to their string representation.
Definition: clutchlog.h:921
level threshold() const
Get the log level below which logs are not printed.
Definition: clutchlog.h:1071
void dump(const level &stage, const In container_begin, const In container_end, const std::string &file, const std::string &func, const 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:1438
std::ostream & out()
Get the output stream on which to print.
Definition: clutchlog.h:998
std::map< level, fmt > _level_fmt
Dictionary of level identifier to their format.
Definition: clutchlog.h:927
std::map< std::string, level > _word_level
Dictionary of level string to their identifier.
Definition: clutchlog.h:923
std::string format(std::string row, 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:1281
void style(level stage, FMT... styles)
Set the style (color and typo) of the given log level.
Definition: clutchlog.h:1113
static size_t default_hfill_max
Default maximum width (number of characters) for which to fill for right-aligning the right part of m...
Definition: clutchlog.h:286
void funchash_styles(std::vector< fmt > styles)
Set the candidate styles for value-dependant function name formatting.
Definition: clutchlog.h:1055
static std::string dump_default_format
Default format of the comment line in file dump.
Definition: clutchlog.h:246
level level_of(const std::string name)
Return the log level tag corresponding to the given pre-configured name.
Definition: clutchlog.h:1079
void style(level stage, fmt style)
Set the style (color and typo) of the given log level, passing a fmt instance.
Definition: clutchlog.h:1115
void func(std::string func)
Set the regular expression filtering the function location.
Definition: clutchlog.h:1092
std::regex _in_file
Current file location filter.
Definition: clutchlog.h:953
void filehash_styles(std::vector< fmt > styles)
Set the candidate styles for value-dependant file name formatting.
Definition: clutchlog.h:1047
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:1270
level _stage
Current log level.
Definition: clutchlog.h:951
static std::string dump_default_sep
Default item separator for dump.
Definition: clutchlog.h:253
std::string format() const
Get the template string.
Definition: clutchlog.h:988
#define CLUTCHLOG_HAVE_UNIX_SYSINFO
True if POSIX headers necessary for stack depth management are available.
Definition: clutchlog.h:34
#define CLUTCHDUMP_DEFAULT_FORMAT
Compile-time default format of the comment line in file dump.
Definition: clutchlog.h:232
#define CLUTCHLOG_DEFAULT_FORMAT
Definition: clutchlog.h:209
bg
Background color codes.
Definition: clutchlog.h:425
fg
Foreground color codes.
Definition: clutchlog.h:404
enum clutchlog::fmt::fg fore
Foreground color.
enum clutchlog::fmt::bg back
Background color.
friend std::ostream & operator<<(std::ostream &os, const typo &s)
Output stream operator for a typo tag alone, in 16-colors mode.
Definition: clutchlog.h:469
friend std::ostream & operator<<(std::ostream &os, const std::tuple< fg, bg, typo > &fbs)
Output stream operator for a 3-tuple of 16-colors mode tags.
Definition: clutchlog.h:447
clutchlog::fmt::bg_256 back_256
Current background in 256-colors mode.
clutchlog::fmt::fg_16M fore_16M
Current foreground in 16M-colors mode.
clutchlog::fmt::bg_16M back_16M
Current background in 16M-colors mode.
clutchlog::fmt::fg_256 fore_256
Current foreground in 256-colors mode.
background in 256-colors mode.
Definition: clutchlog.h:678
bg_16M()
Empty constructor: no color.
Definition: clutchlog.h:680
bg_16M(const bg &)
Conversion constructor from 16-colors mode.
Definition: clutchlog.h:703
bg_16M(short r, short g, short b)
Numeric triplet constructor.
Definition: clutchlog.h:690
bg_16M(const std::string &srgb)
Hex triplet string constructor.
Definition: clutchlog.h:698
Background in 256-colors mode.
Definition: clutchlog.h:570
bg_256(const bg &)
Conversion constructor from 16-colors mode.
Definition: clutchlog.h:582
bg_256()
Empty constructor: no color.
Definition: clutchlog.h:572
bg_256(const short b)
Constructor.
Definition: clutchlog.h:577
Abstract base class for 16M colors objects (24-bits ANSI).
Definition: clutchlog.h:587
short red
The encoded RGB indices.
Definition: clutchlog.h:591
color_16M(ground t, short r, short g, short b)
Numeric triplet constructor.
Definition: clutchlog.h:605
color_16M(ground t, const std::string &srgb)
Hex triplet string constructor.
Definition: clutchlog.h:615
bool is_set() const
Returns true if the underying representation encodes an existing color.
Definition: clutchlog.h:637
std::ostream & print_on(std::ostream &os) const
Print the color RGB triplet on the given stream.
Definition: clutchlog.h:640
color_16M(ground t)
Constructor.
Definition: clutchlog.h:596
Abstract base class for 256 colors objects (8-bits ANSI).
Definition: clutchlog.h:523
color_256(ground t)
Constructor.
Definition: clutchlog.h:532
color_256(ground t, const short i)
Constructor.
Definition: clutchlog.h:539
short index
The encoded color index in 4-bits ANSI.
Definition: clutchlog.h:527
std::ostream & print_on(std::ostream &os) const
Print the color index on the given stream.
Definition: clutchlog.h:545
bool is_set() const
Returns true if the underying representation encodes an existing color.
Definition: clutchlog.h:542
Interface class for colors representation.
Definition: clutchlog.h:484
virtual std::ostream & print_on(std::ostream &os) const =0
Should print the underlying representation on the given stream.
enum clutchlog::fmt::color::ground type
Type of color (foreground or background).
friend std::ostream & operator<<(std::ostream &os, const color &c)
Print the actually encoded escaped color sequence on the given stream.
Definition: clutchlog.h:507
color(ansi a, ground g)
Constructor.
Definition: clutchlog.h:498
virtual bool is_set() const =0
Should return true if the underying representation encodes an existing color.
ground
Codes for representing foreground or background.
Definition: clutchlog.h:488
Foreground in 256-colors mode.
Definition: clutchlog.h:648
fg_16M(short r, short g, short b)
Numeric triplet constructor.
Definition: clutchlog.h:660
fg_16M()
Empty constructor: no color.
Definition: clutchlog.h:650
fg_16M(const fg &)
Conversion constructor from 16-colors mode.
Definition: clutchlog.h:673
fg_16M(const std::string &srgb)
Hex triplet string constructor.
Definition: clutchlog.h:668
Foreground in 256-colors mode.
Definition: clutchlog.h:553
fg_256(const fg &)
Conversion constructor from 16-colors mode.
Definition: clutchlog.h:565
fg_256(const short f)
Constructor.
Definition: clutchlog.h:560
fg_256()
Empty constructor: no color.
Definition: clutchlog.h:555
Structure holding a location matching.
Definition: clutchlog.h:1130
scope_t()
Constructor.
Definition: clutchlog.h:1142
bool there
Location is compatible.
Definition: clutchlog.h:1140
level stage
Current log level.
Definition: clutchlog.h:1134
bool matches
Everything is compatible.
Definition: clutchlog.h:1132