1 #ifndef __CLUTCHLOG_H__
2 #define __CLUTCHLOG_H__
8 #include <experimental/filesystem>
9 namespace fs = std::experimental::filesystem;
12 namespace fs = std::filesystem;
25 #if __has_include(<execinfo.h>) && __has_include(<stdlib.h>) && __has_include(<libgen.h>)
29 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 1
31 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 0
37 #ifndef WITH_CLUTCHLOG
39 #define WITH_CLUTCHLOG
51 #ifndef CLUTCHLOG_DEFAULT_FORMAT
52 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
54 #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
56 #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
58 #endif // CLUTCHLOG_DEFAULT_FORMAT
60 #ifndef CLUTCHDUMP_DEFAULT_FORMAT
61 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
63 #define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth}) @ {file}:{line}"
65 #define CLUTCHDUMP_DEFAULT_FORMAT "# {level} in {func} @ {file}:{line}"
67 #endif // CLUTCHDUMP_DEFAULT_FORMAT
69 #ifndef CLUTCHDUMP_DEFAULT_SEP
70 #define CLUTCHDUMP_DEFAULT_SEP "\n"
72 #endif // CLUTCHDUMP_DEFAULT_SEP
74 #ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
75 #define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
77 #endif // CLUTCHLOG_DEFAULT_DEPTH_MARK
79 #ifndef CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG
80 #define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress
82 #endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT
89 #define CLUTCHLOC __FILE__, __FUNCTION__, __LINE__
94 #define CLUTCHLOG( LEVEL, WHAT ) { \
95 auto& logger = clutchlog::logger(); \
96 std::ostringstream msg ; msg << WHAT; \
97 logger.log(clutchlog::level::LEVEL, msg.str(), CLUTCHLOC); \
99 #else // not Debug build.
100 #define CLUTCHLOG( LEVEL, WHAT ) { \
101 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
102 auto& logger = clutchlog::logger(); \
103 std::ostringstream msg ; msg << WHAT; \
104 logger.log(clutchlog::level::LEVEL, msg.str(), CLUTCHLOC); \
111 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { \
112 auto& logger = clutchlog::logger(); \
113 logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
114 CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
116 #else // not Debug build.
117 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { \
118 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
119 auto& logger = clutchlog::logger(); \
120 logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
121 CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
128 #define CLUTCHFUNC( LEVEL, FUNC, ... ) { \
129 auto& logger = clutchlog::logger(); \
130 clutchlog::scope_t scope = logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
131 if(scope.matches) { \
135 #else // not Debug build.
136 #define CLUTCHFUNC( LEVEL, FUNC, ... ) { \
137 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
138 auto& logger = clutchlog::logger(); \
139 clutchlog::scope_t scope = logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
140 if(scope.matches) { \
149 #define CLUTCHCODE( LEVEL, ... ) { \
150 auto& logger = clutchlog::logger(); \
151 clutchlog::scope_t scope = logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
152 if(scope.matches) { \
156 #else // not Debug build.
157 #define CLUTCHCODE( LEVEL, CODE ) { \
158 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
159 auto& logger = clutchlog::logger(); \
160 clutchlog::scope_t scope = logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
161 if(scope.matches) { \
170 #else // not WITH_CLUTCHLOG
172 #define CLUTCHLOG( LEVEL, WHAT ) { do {} while(false); }
173 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { do {} while(false); }
174 #define CLUTCHFUNC( LEVEL, FUNC, ... ) { do {} while(false); }
175 #define CLUTCHCODE( LEVEL, CODE ) { do {} while(false); }
176 #endif // WITH_CLUTCHLOG
182 #ifdef WITH_CLUTCHLOG
209 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
265 fmt(
fg f,
bg b = bg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
266 fmt(
fg f,
typo s ,
bg b = bg::none) : fore(f), back(b), style(s) { }
267 fmt(
bg b,
fg f = fg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
268 fmt(
bg b,
typo s ,
fg f = fg::none) : fore(f), back(b), style(s) { }
269 fmt(
typo s,
fg f = fg::none,
bg b = bg::none) : fore(f), back(b), style(s) { }
270 fmt(
typo s,
bg b ,
fg f = fg::none) : fore(f), back(b), style(s) { }
277 std::vector<int> codes; codes.reserve(3);
278 if(this->fore != fg::none) { codes.push_back(
static_cast<int>(this->fore ));}
279 if(this->back != bg::none) { codes.push_back(
static_cast<int>(this->back ));}
280 if(this->style != typo::none) { codes.push_back(
static_cast<int>(this->style));}
281 if(codes.size() == 0) {
return os;}
284 assert(codes.size() > 0);
286 for(
size_t i=1; i < codes.size(); ++i) {
287 os <<
";" << codes[i];
322 std::ostringstream os;
324 fmt reset(fmt::typo::reset);
339 void operator=(
clutchlog const&) =
delete;
346 {level::critical,
"Critical"},
347 {level::error ,
"Error"},
348 {level::warning ,
"Warning"},
349 {level::progress,
"Progress"},
350 {level::note ,
"Note"},
351 {level::info ,
"Info"},
352 {level::debug ,
"Debug"},
353 {level::xdebug ,
"XDebug"}
356 {level::critical,fmt(fmt::fg::red, fmt::typo::underline)},
357 {level::error ,fmt(fmt::fg::red, fmt::typo::bold)},
358 {level::warning ,fmt(fmt::fg::magenta, fmt::typo::bold)},
359 {level::progress,fmt()},
360 {level::note ,fmt()},
361 {level::info ,fmt()},
362 {level::debug ,fmt()},
363 {level::xdebug ,fmt()}
368 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
369 _depth(std::numeric_limits<size_t>::max() - _strip_calls),
372 _stage(level::error),
378 for(
auto& lw : _level_word) {
379 _word_level[lw.second] = lw.first;
384 const size_t _strip_calls;
385 const std::map<level,std::string> _level_word;
386 std::map<std::string,level> _word_level;
387 std::map<level,fmt> _level_fmt;
388 std::string _format_log;
389 std::string _format_dump;
391 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
393 std::string _depth_mark;
400 static const size_t max_buffer = 4096;
411 std::string
format()
const {
return _format_log;}
421 std::ostream&
out() {
return *_out;}
423 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
424 void depth(
size_t d) {_depth = d;}
427 size_t depth()
const {
return _depth;}
430 void depth_mark(std::string mark) {_depth_mark = mark;}
432 std::string depth_mark()
const {
return _depth_mark;}
449 const std::string& in_file,
450 const std::string& in_function=
".*",
451 const std::string& in_line=
".*"
463 template<
class ... FMT>
476 const auto ilevel = _word_level.find(name);
477 if( ilevel != std::end(_word_level)) {
478 return ilevel->second;
480 throw std::out_of_range(
"'" + name +
"' is not a valid log level name");
495 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
501 stage(
level::xdebug),
502 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
513 const std::string&
file,
514 const std::string&
func,
523 if(not (scope.stage <= _stage)) {
528 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
532 void *buffer[max_buffer];
533 stack_depth = backtrace(buffer, max_buffer);
534 scope.depth = stack_depth;
535 if(not (scope.depth <= _depth + _strip_calls)) {
543 std::ostringstream sline; sline <<
line;
545 std::regex_search(
file, _in_file)
546 and std::regex_search(
func, _in_func)
547 and std::regex_search(sline.str(), _in_line);
550 scope.matches = scope.there;
563 const std::string& form,
564 const std::string& mark,
565 const std::string& tag
622 const std::regex re(mark);
623 return std::regex_replace(form, re, tag);
628 const std::string& form,
629 const std::string& mark,
633 std::ostringstream stag; stag << tag;
634 return replace(form, mark, stag.str());
640 const std::string& what,
641 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
642 const std::string& name,
645 const std::string&
file,
646 const std::string&
func,
648 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
660 std::string letter(1, _level_word.at(stage).at(0));
663 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
667 std::ostringstream chevrons;
668 for(
size_t i = _strip_calls; i < depth; ++i) {
669 chevrons << _depth_mark;
674 return _level_fmt.at(stage)(
format);
680 const std::string& what,
681 const std::string&
file,
const std::string&
func,
size_t line
687 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
688 *_out <<
format(_format_log, what, basename(getenv(
"_")),
692 *_out <<
format(_format_log, what,
705 const In container_begin,
const In container_end,
706 const std::string&
file,
const std::string&
func,
size_t line,
707 const std::string& filename_template=
"dump_{n}.dat",
714 const std::string tag =
"\\{n\\}";
715 const std::regex re(tag);
716 std::string outfile =
"";
719 if(std::regex_search(filename_template, re)) {
723 outfile =
replace(filename_template, tag, n);
725 }
while( fs::exists( outfile ) );
729 outfile = filename_template;
732 std::ofstream fd(outfile);
734 if(_format_dump.size() > 0) {
735 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
736 fd <<
format(_format_dump,
"", basename(getenv(
"_")),
740 fd <<
format(_format_dump,
"",
747 std::copy(container_begin, container_end,
748 std::ostream_iterator<typename In::value_type>(fd, sep.c_str()));
759 #else // not WITH_CLUTCHLOG
768 #pragma GCC diagnostic push
769 #pragma GCC diagnostic ignored "-Wreturn-type"
774 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
777 enum class fg { black, red, green, yellow, blue, magenta, cyan, white, none } fore;
778 enum class bg { black, red, green, yellow, blue, magenta, cyan, white, none } back;
779 enum class typo { reset, bold, underline, inverse, none } style;
780 fmt() : fore(
fg::none), back(
bg::none), style(
typo::none) { }
781 fmt(
fg f,
bg b = bg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
782 fmt(
fg f,
typo s ,
bg b = bg::none) : fore(f), back(b), style(s) { }
783 fmt(
bg b,
fg f = fg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
784 fmt(
bg b,
typo s ,
fg f = fg::none) : fore(f), back(b), style(s) { }
785 fmt(
typo s,
fg f = fg::none,
bg b = bg::none) : fore(f), back(b), style(s) { }
786 fmt(
typo s,
bg b ,
fg f = fg::none) : fore(f), back(b), style(s) { }
788 std::ostream&
print_on(std::ostream&)
const { }
790 friend std::ostream&
operator<<(std::ostream&,
const fmt&) { }
791 std::string
operator()(
const std::string&)
const { }
795 void operator=(
clutchlog const&) =
delete;
808 void format(
const std::string&) {}
809 std::string
format()
const {}
814 void out(std::ostream&) {}
815 std::ostream&
out() {}
817 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
818 void depth(
size_t) {}
819 size_t depth()
const {}
821 void depth_mark(std::string) {}
822 std::string depth_mark()
const {}
828 void file(std::string) {}
829 void func(std::string) {}
830 void line(std::string) {}
832 #pragma GCC diagnostic push
833 #pragma GCC diagnostic ignored "-Wunused-parameter"
836 const std::string& in_function=
".*",
837 const std::string& in_line=
".*"
840 #pragma GCC diagnostic pop
862 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
869 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
879 const std::string&,
const std::string&,
size_t
887 const std::string&,
const std::string&,
size_t,
893 #pragma GCC diagnostic pop
894 #endif // WITH_CLUTCHLOG
896 #endif // __CLUTCHLOG_H__