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>)
30 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 1
32 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 0
38 #ifndef WITH_CLUTCHLOG
40 #define WITH_CLUTCHLOG
53 #ifndef CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG
54 #define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress
56 #endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT
64 #define CLUTCHLOC __FILE__, __FUNCTION__, __LINE__
69 #define CLUTCHLOG( LEVEL, WHAT ) do { \
70 auto& clutchlog__logger = clutchlog::logger(); \
71 std::ostringstream clutchlog__msg ; clutchlog__msg << WHAT; \
72 clutchlog__logger.log(clutchlog::level::LEVEL, clutchlog__msg.str(), CLUTCHLOC); \
74 #else // not Debug build.
75 #define CLUTCHLOG( LEVEL, WHAT ) do { \
76 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
77 auto& clutchlog__logger = clutchlog::logger(); \
78 std::ostringstream clutchlog__msg ; clutchlog__msg << WHAT; \
79 clutchlog__logger.log(clutchlog::level::LEVEL, clutchlog__msg.str(), CLUTCHLOC); \
86 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do { \
87 auto& clutchlog__logger = clutchlog::logger(); \
88 clutchlog__logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
89 CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
91 #else // not Debug build.
92 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do { \
93 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
94 auto& clutchlog__logger = clutchlog::logger(); \
95 clutchlog__logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
96 CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
103 #define CLUTCHFUNC( LEVEL, FUNC, ... ) do { \
104 auto& clutchlog__logger = clutchlog::logger(); \
105 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
106 if(clutchlog__scope.matches) { \
110 #else // not Debug build.
111 #define CLUTCHFUNC( LEVEL, FUNC, ... ) do { \
112 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
113 auto& clutchlog__logger = clutchlog::logger(); \
114 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
115 if(clutchlog__scope.matches) { \
123 #define CLUTCHCODE( LEVEL, ... ) do { \
124 auto& clutchlog__logger = clutchlog::logger(); \
125 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
126 if(clutchlog__scope.matches) { \
130 #else // not Debug build.
131 #define CLUTCHCODE( LEVEL, CODE ) do { \
132 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
133 auto& clutchlog__logger = clutchlog::logger(); \
134 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
135 if(clutchlog__scope.matches) { \
144 #else // not WITH_CLUTCHLOG
146 #define CLUTCHLOG( LEVEL, WHAT ) do {} while(0)
147 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do {} while(0)
148 #define CLUTCHFUNC( LEVEL, FUNC, ... ) do {} while(0)
149 #define CLUTCHCODE( LEVEL, CODE ) do {} while(0)
150 #endif // WITH_CLUTCHLOG
156 #ifdef WITH_CLUTCHLOG
170 #ifndef CLUTCHLOG_DEFAULT_FORMAT
171 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
173 #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
175 #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
177 #endif // CLUTCHLOG_DEFAULT_FORMAT
180 #ifndef CLUTCHDUMP_DEFAULT_FORMAT
181 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
183 #define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth}) @ {file}:{line}"
185 #define CLUTCHDUMP_DEFAULT_FORMAT "# {level} in {func} @ {file}:{line}"
187 #endif // CLUTCHDUMP_DEFAULT_FORMAT
190 #ifndef CLUTCHDUMP_DEFAULT_SEP
191 #define CLUTCHDUMP_DEFAULT_SEP "\n"
193 #endif // CLUTCHDUMP_DEFAULT_SEP
196 #ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
197 #define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
199 #endif // CLUTCHLOG_DEFAULT_DEPTH_MARK
202 #ifndef CLUTCHLOG_STRIP_CALLS
203 #define CLUTCHLOG_STRIP_CALLS 5
205 #endif // CLUTCHLOG_STRIP_CALLS
227 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
283 fmt(
fg f,
bg b = bg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
284 fmt(
fg f,
typo s ,
bg b = bg::none) : fore(f), back(b), style(s) { }
285 fmt(
bg b,
fg f = fg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
286 fmt(
bg b,
typo s ,
fg f = fg::none) : fore(f), back(b), style(s) { }
287 fmt(
typo s,
fg f = fg::none,
bg b = bg::none) : fore(f), back(b), style(s) { }
288 fmt(
typo s,
bg b ,
fg f = fg::none) : fore(f), back(b), style(s) { }
295 std::vector<int> codes; codes.reserve(3);
296 if(this->fore != fg::none) { codes.push_back(
static_cast<int>(this->fore ));}
297 if(this->back != bg::none) { codes.push_back(
static_cast<int>(this->back ));}
298 if(this->style != typo::none) { codes.push_back(
static_cast<int>(this->style));}
299 if(codes.size() == 0) {
return os;}
302 assert(codes.size() > 0);
304 for(
size_t i=1; i < codes.size(); ++i) {
305 os <<
";" << codes[i];
340 std::ostringstream os;
342 fmt reset(fmt::typo::reset);
357 void operator=(
clutchlog const&) =
delete;
364 {level::critical,
"Critical"},
365 {level::error ,
"Error"},
366 {level::warning ,
"Warning"},
367 {level::progress,
"Progress"},
368 {level::note ,
"Note"},
369 {level::info ,
"Info"},
370 {level::debug ,
"Debug"},
371 {level::xdebug ,
"XDebug"}
374 {level::critical,fmt(fmt::fg::red, fmt::typo::underline)},
375 {level::error ,fmt(fmt::fg::red, fmt::typo::bold)},
376 {level::warning ,fmt(fmt::fg::magenta, fmt::typo::bold)},
377 {level::progress,fmt()},
378 {level::note ,fmt()},
379 {level::info ,fmt()},
380 {level::debug ,fmt()},
381 {level::xdebug ,fmt()}
383 _format_log(clutchlog::default_format),
384 _format_dump(clutchlog::dump_default_format),
386 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
387 _depth(std::numeric_limits<size_t>::max() - _strip_calls),
388 _depth_mark(clutchlog::default_depth_mark),
390 _stage(level::error),
396 for(
auto& lw : _level_word) {
397 _word_level[lw.second] = lw.first;
402 const size_t _strip_calls;
403 const std::map<level,std::string> _level_word;
404 std::map<std::string,level> _word_level;
405 std::map<level,fmt> _level_fmt;
406 std::string _format_log;
407 std::string _format_dump;
409 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
411 std::string _depth_mark;
418 static const size_t max_buffer = 4096;
429 std::string
format()
const {
return _format_log;}
439 std::ostream&
out() {
return *_out;}
441 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
442 void depth(
size_t d) {_depth = d;}
445 size_t depth()
const {
return _depth;}
448 void depth_mark(std::string mark) {_depth_mark = mark;}
450 std::string depth_mark()
const {
return _depth_mark;}
467 const std::string& in_file,
468 const std::string& in_function=
".*",
469 const std::string& in_line=
".*"
481 template<
class ... FMT>
494 const auto ilevel = _word_level.find(name);
495 if( ilevel != std::end(_word_level)) {
496 return ilevel->second;
498 throw std::out_of_range(
"'" + name +
"' is not a valid log level name");
513 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
519 stage(
level::xdebug),
520 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
531 const std::string&
file,
532 const std::string&
func,
541 if(not (scope.stage <= _stage)) {
546 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
550 void *buffer[max_buffer];
551 stack_depth = backtrace(buffer, max_buffer);
552 scope.depth = stack_depth;
553 if(not (scope.depth <= _depth + _strip_calls)) {
561 std::ostringstream sline; sline <<
line;
563 std::regex_search(
file, _in_file)
564 and std::regex_search(
func, _in_func)
565 and std::regex_search(sline.str(), _in_line);
568 scope.matches = scope.there;
581 const std::string& form,
582 const std::string& mark,
583 const std::string& tag
640 const std::regex re(mark);
641 return std::regex_replace(form, re, tag);
646 const std::string& form,
647 const std::string& mark,
651 std::ostringstream stag; stag << tag;
652 return replace(form, mark, stag.str());
658 const std::string& what,
659 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
660 const std::string& name,
663 const std::string&
file,
664 const std::string&
func,
666 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
678 std::string letter(1, _level_word.at(stage).at(0));
681 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
685 std::ostringstream chevrons;
686 for(
size_t i = _strip_calls; i < depth; ++i) {
687 chevrons << _depth_mark;
692 return _level_fmt.at(stage)(
format);
698 const std::string& what,
699 const std::string&
file,
const std::string&
func,
size_t line
705 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
706 *_out <<
format(_format_log, what, basename(getenv(
"_")),
710 *_out <<
format(_format_log, what,
723 const In container_begin,
const In container_end,
724 const std::string&
file,
const std::string&
func,
size_t line,
725 const std::string& filename_template =
"dump_{n}.dat",
726 const std::string sep = dump_default_sep
732 const std::string tag =
"\\{n\\}";
733 const std::regex re(tag);
734 std::string outfile =
"";
737 if(std::regex_search(filename_template, re)) {
741 outfile =
replace(filename_template, tag, n);
743 }
while( fs::exists( outfile ) );
747 outfile = filename_template;
750 std::ofstream fd(outfile);
752 if(_format_dump.size() > 0) {
753 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
754 fd <<
format(_format_dump,
"", basename(getenv(
"_")),
758 fd <<
format(_format_dump,
"",
765 std::copy(container_begin, container_end,
766 std::ostream_iterator<typename In::value_type>(fd, sep.c_str()));
777 #else // not WITH_CLUTCHLOG
786 #pragma GCC diagnostic push
787 #pragma GCC diagnostic ignored "-Wreturn-type"
792 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
795 enum class fg { black, red, green, yellow, blue, magenta, cyan, white, none } fore;
796 enum class bg { black, red, green, yellow, blue, magenta, cyan, white, none } back;
797 enum class typo { reset, bold, underline, inverse, none } style;
798 fmt() : fore(
fg::none), back(
bg::none), style(
typo::none) { }
799 fmt(
fg f,
bg b = bg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
800 fmt(
fg f,
typo s ,
bg b = bg::none) : fore(f), back(b), style(s) { }
801 fmt(
bg b,
fg f = fg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
802 fmt(
bg b,
typo s ,
fg f = fg::none) : fore(f), back(b), style(s) { }
803 fmt(
typo s,
fg f = fg::none,
bg b = bg::none) : fore(f), back(b), style(s) { }
804 fmt(
typo s,
bg b ,
fg f = fg::none) : fore(f), back(b), style(s) { }
806 std::ostream&
print_on(std::ostream&)
const { }
808 friend std::ostream&
operator<<(std::ostream&,
const fmt&) { }
809 std::string
operator()(
const std::string&)
const { }
813 void operator=(
clutchlog const&) =
delete;
826 void format(
const std::string&) {}
827 std::string
format()
const {}
832 void out(std::ostream&) {}
833 std::ostream&
out() {}
835 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
836 void depth(
size_t) {}
837 size_t depth()
const {}
839 void depth_mark(std::string) {}
840 std::string depth_mark()
const {}
846 void file(std::string) {}
847 void func(std::string) {}
848 void line(std::string) {}
850 #pragma GCC diagnostic push
851 #pragma GCC diagnostic ignored "-Wunused-parameter"
854 const std::string& in_function=
".*",
855 const std::string& in_line=
".*"
858 #pragma GCC diagnostic pop
880 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
887 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
897 const std::string&,
const std::string&,
size_t
905 const std::string&,
const std::string&,
size_t,
911 #pragma GCC diagnostic pop
912 #endif // WITH_CLUTCHLOG
914 #endif // __CLUTCHLOG_H__