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_FORMAT
54 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
56 #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
58 #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
60 #endif // CLUTCHLOG_DEFAULT_FORMAT
62 #ifndef CLUTCHDUMP_DEFAULT_FORMAT
63 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
65 #define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth}) @ {file}:{line}"
67 #define CLUTCHDUMP_DEFAULT_FORMAT "# {level} in {func} @ {file}:{line}"
69 #endif // CLUTCHDUMP_DEFAULT_FORMAT
71 #ifndef CLUTCHDUMP_DEFAULT_SEP
72 #define CLUTCHDUMP_DEFAULT_SEP "\n"
74 #endif // CLUTCHDUMP_DEFAULT_SEP
76 #ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
77 #define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
79 #endif // CLUTCHLOG_DEFAULT_DEPTH_MARK
81 #ifndef CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG
82 #define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress
84 #endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT
86 #ifndef CLUTCHLOG_STRIP_CALLS
87 #define CLUTCHLOG_STRIP_CALLS 5
89 #endif // CLUTCHLOG_STRIP_CALLS
95 #define CLUTCHLOC __FILE__, __FUNCTION__, __LINE__
100 #define CLUTCHLOG( LEVEL, WHAT ) { \
101 auto& clutchlog__logger = clutchlog::logger(); \
102 std::ostringstream clutchlog__msg ; clutchlog__msg << WHAT; \
103 clutchlog__logger.log(clutchlog::level::LEVEL, clutchlog__msg.str(), CLUTCHLOC); \
105 #else // not Debug build.
106 #define CLUTCHLOG( LEVEL, WHAT ) { \
107 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
108 auto& clutchlog__logger = clutchlog::logger(); \
109 std::ostringstream clutchlog__msg ; clutchlog__msg << WHAT; \
110 clutchlog__logger.log(clutchlog::level::LEVEL, clutchlog__msg.str(), CLUTCHLOC); \
117 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { \
118 auto& clutchlog__logger = clutchlog::logger(); \
119 clutchlog__logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
120 CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
122 #else // not Debug build.
123 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { \
124 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
125 auto& clutchlog__logger = clutchlog::logger(); \
126 clutchlog__logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
127 CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
134 #define CLUTCHFUNC( LEVEL, FUNC, ... ) { \
135 auto& clutchlog__logger = clutchlog::logger(); \
136 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
137 if(clutchlog__scope.matches) { \
141 #else // not Debug build.
142 #define CLUTCHFUNC( LEVEL, FUNC, ... ) { \
143 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
144 auto& clutchlog__logger = clutchlog::logger(); \
145 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
146 if(clutchlog__scope.matches) { \
155 #define CLUTCHCODE( LEVEL, ... ) { \
156 auto& clutchlog__logger = clutchlog::logger(); \
157 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
158 if(clutchlog__scope.matches) { \
162 #else // not Debug build.
163 #define CLUTCHCODE( LEVEL, CODE ) { \
164 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
165 auto& clutchlog__logger = clutchlog::logger(); \
166 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
167 if(clutchlog__scope.matches) { \
176 #else // not WITH_CLUTCHLOG
178 #define CLUTCHLOG( LEVEL, WHAT ) { do {} while(false); }
179 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { do {} while(false); }
180 #define CLUTCHFUNC( LEVEL, FUNC, ... ) { do {} while(false); }
181 #define CLUTCHCODE( LEVEL, CODE ) { do {} while(false); }
182 #endif // WITH_CLUTCHLOG
188 #ifdef WITH_CLUTCHLOG
215 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
271 fmt(
fg f,
bg b = bg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
272 fmt(
fg f,
typo s ,
bg b = bg::none) : fore(f), back(b), style(s) { }
273 fmt(
bg b,
fg f = fg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
274 fmt(
bg b,
typo s ,
fg f = fg::none) : fore(f), back(b), style(s) { }
275 fmt(
typo s,
fg f = fg::none,
bg b = bg::none) : fore(f), back(b), style(s) { }
276 fmt(
typo s,
bg b ,
fg f = fg::none) : fore(f), back(b), style(s) { }
283 std::vector<int> codes; codes.reserve(3);
284 if(this->fore != fg::none) { codes.push_back(
static_cast<int>(this->fore ));}
285 if(this->back != bg::none) { codes.push_back(
static_cast<int>(this->back ));}
286 if(this->style != typo::none) { codes.push_back(
static_cast<int>(this->style));}
287 if(codes.size() == 0) {
return os;}
290 assert(codes.size() > 0);
292 for(
size_t i=1; i < codes.size(); ++i) {
293 os <<
";" << codes[i];
328 std::ostringstream os;
330 fmt reset(fmt::typo::reset);
345 void operator=(
clutchlog const&) =
delete;
352 {level::critical,
"Critical"},
353 {level::error ,
"Error"},
354 {level::warning ,
"Warning"},
355 {level::progress,
"Progress"},
356 {level::note ,
"Note"},
357 {level::info ,
"Info"},
358 {level::debug ,
"Debug"},
359 {level::xdebug ,
"XDebug"}
362 {level::critical,fmt(fmt::fg::red, fmt::typo::underline)},
363 {level::error ,fmt(fmt::fg::red, fmt::typo::bold)},
364 {level::warning ,fmt(fmt::fg::magenta, fmt::typo::bold)},
365 {level::progress,fmt()},
366 {level::note ,fmt()},
367 {level::info ,fmt()},
368 {level::debug ,fmt()},
369 {level::xdebug ,fmt()}
374 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
375 _depth(std::numeric_limits<size_t>::max() - _strip_calls),
378 _stage(level::error),
384 for(
auto& lw : _level_word) {
385 _word_level[lw.second] = lw.first;
390 const size_t _strip_calls;
391 const std::map<level,std::string> _level_word;
392 std::map<std::string,level> _word_level;
393 std::map<level,fmt> _level_fmt;
394 std::string _format_log;
395 std::string _format_dump;
397 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
399 std::string _depth_mark;
406 static const size_t max_buffer = 4096;
417 std::string
format()
const {
return _format_log;}
427 std::ostream&
out() {
return *_out;}
429 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
430 void depth(
size_t d) {_depth = d;}
433 size_t depth()
const {
return _depth;}
436 void depth_mark(std::string mark) {_depth_mark = mark;}
438 std::string depth_mark()
const {
return _depth_mark;}
455 const std::string& in_file,
456 const std::string& in_function=
".*",
457 const std::string& in_line=
".*"
469 template<
class ... FMT>
482 const auto ilevel = _word_level.find(name);
483 if( ilevel != std::end(_word_level)) {
484 return ilevel->second;
486 throw std::out_of_range(
"'" + name +
"' is not a valid log level name");
501 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
507 stage(
level::xdebug),
508 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
519 const std::string&
file,
520 const std::string&
func,
529 if(not (scope.stage <= _stage)) {
534 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
538 void *buffer[max_buffer];
539 stack_depth = backtrace(buffer, max_buffer);
540 scope.depth = stack_depth;
541 if(not (scope.depth <= _depth + _strip_calls)) {
549 std::ostringstream sline; sline <<
line;
551 std::regex_search(
file, _in_file)
552 and std::regex_search(
func, _in_func)
553 and std::regex_search(sline.str(), _in_line);
556 scope.matches = scope.there;
569 const std::string& form,
570 const std::string& mark,
571 const std::string& tag
628 const std::regex re(mark);
629 return std::regex_replace(form, re, tag);
634 const std::string& form,
635 const std::string& mark,
639 std::ostringstream stag; stag << tag;
640 return replace(form, mark, stag.str());
646 const std::string& what,
647 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
648 const std::string& name,
651 const std::string&
file,
652 const std::string&
func,
654 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
666 std::string letter(1, _level_word.at(stage).at(0));
669 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
673 std::ostringstream chevrons;
674 for(
size_t i = _strip_calls; i < depth; ++i) {
675 chevrons << _depth_mark;
680 return _level_fmt.at(stage)(
format);
686 const std::string& what,
687 const std::string&
file,
const std::string&
func,
size_t line
693 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
694 *_out <<
format(_format_log, what, basename(getenv(
"_")),
698 *_out <<
format(_format_log, what,
711 const In container_begin,
const In container_end,
712 const std::string&
file,
const std::string&
func,
size_t line,
713 const std::string& filename_template=
"dump_{n}.dat",
720 const std::string tag =
"\\{n\\}";
721 const std::regex re(tag);
722 std::string outfile =
"";
725 if(std::regex_search(filename_template, re)) {
729 outfile =
replace(filename_template, tag, n);
731 }
while( fs::exists( outfile ) );
735 outfile = filename_template;
738 std::ofstream fd(outfile);
740 if(_format_dump.size() > 0) {
741 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
742 fd <<
format(_format_dump,
"", basename(getenv(
"_")),
746 fd <<
format(_format_dump,
"",
753 std::copy(container_begin, container_end,
754 std::ostream_iterator<typename In::value_type>(fd, sep.c_str()));
765 #else // not WITH_CLUTCHLOG
774 #pragma GCC diagnostic push
775 #pragma GCC diagnostic ignored "-Wreturn-type"
780 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
783 enum class fg { black, red, green, yellow, blue, magenta, cyan, white, none } fore;
784 enum class bg { black, red, green, yellow, blue, magenta, cyan, white, none } back;
785 enum class typo { reset, bold, underline, inverse, none } style;
786 fmt() : fore(
fg::none), back(
bg::none), style(
typo::none) { }
787 fmt(
fg f,
bg b = bg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
788 fmt(
fg f,
typo s ,
bg b = bg::none) : fore(f), back(b), style(s) { }
789 fmt(
bg b,
fg f = fg::none,
typo s = typo::none) : fore(f), back(b), style(s) { }
790 fmt(
bg b,
typo s ,
fg f = fg::none) : fore(f), back(b), style(s) { }
791 fmt(
typo s,
fg f = fg::none,
bg b = bg::none) : fore(f), back(b), style(s) { }
792 fmt(
typo s,
bg b ,
fg f = fg::none) : fore(f), back(b), style(s) { }
794 std::ostream&
print_on(std::ostream&)
const { }
796 friend std::ostream&
operator<<(std::ostream&,
const fmt&) { }
797 std::string
operator()(
const std::string&)
const { }
801 void operator=(
clutchlog const&) =
delete;
814 void format(
const std::string&) {}
815 std::string
format()
const {}
820 void out(std::ostream&) {}
821 std::ostream&
out() {}
823 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
824 void depth(
size_t) {}
825 size_t depth()
const {}
827 void depth_mark(std::string) {}
828 std::string depth_mark()
const {}
834 void file(std::string) {}
835 void func(std::string) {}
836 void line(std::string) {}
838 #pragma GCC diagnostic push
839 #pragma GCC diagnostic ignored "-Wunused-parameter"
842 const std::string& in_function=
".*",
843 const std::string& in_line=
".*"
846 #pragma GCC diagnostic pop
868 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
875 #
if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
885 const std::string&,
const std::string&,
size_t
893 const std::string&,
const std::string&,
size_t,
899 #pragma GCC diagnostic pop
900 #endif // WITH_CLUTCHLOG
902 #endif // __CLUTCHLOG_H__