 |
clutchlog
0.12
|
Go to the documentation of this file.
9 #include <experimental/filesystem>
10 namespace fs = std::experimental::filesystem;
13 namespace fs = std::filesystem;
27 #if __has_include(<execinfo.h>) && __has_include(<stdlib.h>) && __has_include(<libgen.h>)
31 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 1
33 #define CLUTCHLOG_HAVE_UNIX_SYSINFO 0
37 #if __has_include(<sys/ioctl.h>) && __has_include(<stdio.h>) && __has_include(<unistd.h>)
38 #include <sys/ioctl.h>
41 #define CLUTCHLOG_HAVE_UNIX_SYSIOCTL 1
43 #define CLUTCHLOG_HAVE_UNIX_SYSIOCTL 0
50 #ifndef WITH_CLUTCHLOG
52 #define WITH_CLUTCHLOG
65 #ifndef CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG
66 #define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress
68 #endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT
76 #define CLUTCHLOC __FILE__, __FUNCTION__, __LINE__
81 #define CLUTCHLOG( LEVEL, WHAT ) do { \
82 auto& clutchlog__logger = clutchlog::logger(); \
83 std::ostringstream clutchlog__msg ; clutchlog__msg << WHAT; \
84 clutchlog__logger.log(clutchlog::level::LEVEL, clutchlog__msg.str(), CLUTCHLOC); \
86 #else // not Debug build.
87 #define CLUTCHLOG( LEVEL, WHAT ) do { \
88 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
89 auto& clutchlog__logger = clutchlog::logger(); \
90 std::ostringstream clutchlog__msg ; clutchlog__msg << WHAT; \
91 clutchlog__logger.log(clutchlog::level::LEVEL, clutchlog__msg.str(), CLUTCHLOC); \
98 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do { \
99 auto& clutchlog__logger = clutchlog::logger(); \
100 clutchlog__logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
101 CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
103 #else // not Debug build.
104 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do { \
105 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
106 auto& clutchlog__logger = clutchlog::logger(); \
107 clutchlog__logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), \
108 CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
115 #define CLUTCHFUNC( LEVEL, FUNC, ... ) do { \
116 auto& clutchlog__logger = clutchlog::logger(); \
117 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
118 if(clutchlog__scope.matches) { \
122 #else // not Debug build.
123 #define CLUTCHFUNC( LEVEL, FUNC, ... ) do { \
124 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
125 auto& clutchlog__logger = clutchlog::logger(); \
126 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
127 if(clutchlog__scope.matches) { \
136 #define CLUTCHCODE( LEVEL, ... ) do { \
137 auto& clutchlog__logger = clutchlog::logger(); \
138 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
139 if(clutchlog__scope.matches) { \
143 #else // not Debug build.
144 #define CLUTCHCODE( LEVEL, CODE ) do { \
145 if(clutchlog::level::LEVEL <= CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG) { \
146 auto& clutchlog__logger = clutchlog::logger(); \
147 clutchlog::scope_t clutchlog__scope = clutchlog__logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
148 if(clutchlog__scope.matches) { \
157 #else // not WITH_CLUTCHLOG
159 #define CLUTCHLOG( LEVEL, WHAT ) do {} while(0)
160 #define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) do {} while(0)
161 #define CLUTCHFUNC( LEVEL, FUNC, ... ) do {} while(0)
162 #define CLUTCHCODE( LEVEL, CODE ) do {} while(0)
163 #endif // WITH_CLUTCHLOG
169 #ifdef WITH_CLUTCHLOG
186 #ifndef CLUTCHLOG_DEFAULT_FORMAT
187 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1 // Enables: name, depth and depth_marks
189 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1 // Enables: hfill
190 #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg} {hfill} {func} @ {file}:{line}\n"
192 #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
195 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
196 #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg} {hfill} {func} @ {file}:{line}\n"
198 #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
203 #ifndef CLUTCHLOG_DEFAULT_FORMAT
204 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
206 #define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg} {hfill} {func}\n"
208 #define CLUTCHLOG_DEFAULT_FORMAT "{level_letter} {msg}\t\t\t\t\t{func}\n"
216 #ifndef CLUTCHDUMP_DEFAULT_FORMAT
217 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
219 #define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth}) @ {file}:{line}"
221 #define CLUTCHDUMP_DEFAULT_FORMAT "# {level} in {func} @ {file}:{line}"
223 #endif // CLUTCHDUMP_DEFAULT_FORMAT
225 #ifndef CLUTCHDUMP_DEFAULT_FORMAT
226 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
228 #define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth})"
230 #define CLUTCHDUMP_DEFAULT_FORMAT "# {level} in {func}"
232 #endif // CLUTCHDUMP_DEFAULT_FORMAT
237 #ifndef CLUTCHDUMP_DEFAULT_SEP
238 #define CLUTCHDUMP_DEFAULT_SEP "\n"
240 #endif // CLUTCHDUMP_DEFAULT_SEP
244 #ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
245 #define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
247 #endif // CLUTCHLOG_DEFAULT_DEPTH_MARK
251 #ifndef CLUTCHLOG_STRIP_CALLS
252 #define CLUTCHLOG_STRIP_CALLS 5
254 #endif // CLUTCHLOG_STRIP_CALLS
258 #ifndef CLUTCHLOG_DEFAULT_HFILL_MARK
259 #define CLUTCHLOG_DEFAULT_HFILL_MARK '.'
261 #endif // CLUTCHLOG_DEFAULT_HFILL_MARK
266 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
267 #ifndef CLUTCHLOG_DEFAULT_HFILL_MAX
268 #define CLUTCHLOG_DEFAULT_HFILL_MAX 300
270 #ifndef CLUTCHLOG_DEFAULT_HFILL_MIN
271 #define CLUTCHLOG_DEFAULT_HFILL_MIN 150
303 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
368 std::vector<int> codes; codes.reserve(3);
369 if(this->
fore != fg::none) { codes.push_back(
static_cast<int>(this->
fore ));}
370 if(this->
back != bg::none) { codes.push_back(
static_cast<int>(this->
back ));}
371 if(this->
style != typo::none) { codes.push_back(
static_cast<int>(this->
style));}
372 if(codes.size() == 0) {
return os;}
375 assert(codes.size() > 0);
377 for(
size_t i=1; i < codes.size(); ++i) {
378 os <<
";" << codes[i];
413 std::ostringstream os;
415 fmt reset(fmt::typo::reset);
425 std::ostringstream os;
438 void operator=(
clutchlog const&) =
delete;
445 {level::critical,
"Critical"},
446 {level::error ,
"Error"},
447 {level::warning ,
"Warning"},
448 {level::progress,
"Progress"},
449 {level::note ,
"Note"},
450 {level::info ,
"Info"},
451 {level::debug ,
"Debug"},
452 {level::xdebug ,
"XDebug"}
455 {level::critical,fmt(fmt::fg::red, fmt::typo::underline)},
456 {level::error ,fmt(fmt::fg::red, fmt::typo::bold)},
457 {level::warning ,fmt(fmt::fg::magenta, fmt::typo::bold)},
458 {level::progress,fmt()},
459 {level::note ,fmt()},
460 {level::info ,fmt()},
461 {level::debug ,fmt()},
462 {level::xdebug ,fmt()}
466 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
468 _hfill_fmt(fmt::fg::none),
473 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
474 _depth(std::numeric_limits<size_t>::max() -
_strip_calls),
486 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
488 ioctl(STDERR_FILENO, TIOCGWINSZ, &w);
506 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
518 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
522 std::string _depth_mark;
533 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
535 static const size_t _max_buffer = 4096;
538 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
564 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
565 void depth(
size_t d) {_depth = d;}
568 size_t depth()
const {
return _depth;}
571 void depth_mark(
const std::string mark) {_depth_mark = mark;}
573 std::string depth_mark()
const {
return _depth_mark;}
580 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
581 void hfill_mark(
const char mark) {_hfill_char = mark;}
584 char hfill_mark()
const {
return _hfill_char;}
586 void hfill_style(fmt
style) {_hfill_fmt =
style;}
591 template<
class ... FMT>
592 void hfill_style(FMT... styles) { this->hfill_style(fmt(styles...)); }
594 fmt hfill_style()
const {
return _hfill_fmt;}
596 void hfill_max(
const size_t nmax) {_hfill_max = nmax;}
598 size_t hfill_max() {
return _hfill_max;}
600 void hfill_min(
const size_t nmin) {_hfill_min = nmin;}
602 size_t hfill_min() {
return _hfill_min;}
622 return ilevel->second;
624 throw std::out_of_range(
"'" + name +
"' is not a valid log level name");
637 const std::string& in_file,
638 const std::string& in_function=
".*",
639 const std::string& in_line=
".*"
651 template<
class ... FMT>
671 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
692 const std::string&
file,
693 const std::string&
func,
702 if(not (scope.stage <=
_stage)) {
707 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
711 void *buffer[_max_buffer];
712 stack_depth = backtrace(buffer, _max_buffer);
713 scope.depth = stack_depth;
722 std::ostringstream sline; sline <<
line;
726 and std::regex_search(sline.str(),
_in_line);
729 scope.matches = scope.there;
742 const std::string& form,
743 const std::string& mark,
744 const std::string& tag
801 const std::regex re(mark);
802 return std::regex_replace(form, re, tag);
807 const std::string& form,
808 const std::string& mark,
812 std::ostringstream stag; stag << tag;
813 return replace(form, mark, stag.str());
819 const std::string& what,
821 const std::string& name,
824 const std::string&
file,
825 const std::string&
func,
833 row =
replace(row,
"\\{msg\\}", what);
839 std::string letter(1,
_level_word.at(stage).at(0));
840 row =
replace(row,
"\\{level_letter\\}", letter);
842 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
843 row =
replace(row,
"\\{name\\}", name);
846 std::ostringstream chevrons;
848 chevrons << _depth_mark;
850 row =
replace(row,
"\\{depth_marks\\}", chevrons.str());
855 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
857 const std::string raw_row =
replace(row,
"\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]",
"");
858 const std::string hfill_tag =
"{hfill}";
859 const size_t hfill_pos = row.find(hfill_tag);
860 const size_t raw_hfill_pos = raw_row.find(hfill_tag);
861 const size_t nb_columns = std::max(std::min((
size_t)_nb_columns, _hfill_max), _hfill_min);
862 if(hfill_pos != std::string::npos) {
863 assert(raw_hfill_pos != std::string::npos);
865 const size_t left_len = raw_hfill_pos;
866 const size_t right_len = raw_row.size() - raw_hfill_pos - hfill_tag.size();
867 if(right_len+left_len > nb_columns) {
869 if(right_len < nb_columns) {
871 const std::string hfill(std::max((
size_t)0, nb_columns-right_len), _hfill_char);
872 const std::string hfill_styled = _hfill_fmt(hfill);
873 row =
replace(row,
"\\{hfill\\}",
"\n"+hfill_styled);
876 const std::string hfill(1, _hfill_char);
877 const std::string hfill_styled = _hfill_fmt(hfill);
878 row =
replace(row,
"\\{hfill\\}",
"\n"+hfill_styled);
882 const std::string hfill(std::max((
size_t)0, nb_columns - (right_len+left_len)), _hfill_char);
883 const std::string hfill_styled = _hfill_fmt(hfill);
884 row =
replace(row,
"\\{hfill\\}", hfill_styled);
888 const std::string hfill(1, _hfill_char);
889 const std::string hfill_styled = _hfill_fmt(hfill);
890 row =
replace(row,
"\\{hfill\\}", hfill_styled);
895 const std::string hfill(1, _hfill_char);
896 const std::string hfill_styled = _hfill_fmt(hfill);
897 row =
replace(row,
"\\{hfill\\}", hfill_styled);
905 const std::string& what,
906 const std::string&
file,
const std::string&
func,
size_t line
912 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
930 const In container_begin,
const In container_end,
931 const std::string&
file,
const std::string&
func,
size_t line,
932 const std::string& filename_template =
"dump_{n}.dat",
939 const std::string tag =
"\\{n\\}";
940 const std::regex re(tag);
941 std::string outfile =
"";
944 if(std::regex_search(filename_template, re)) {
948 outfile =
replace(filename_template, tag, n);
950 }
while( fs::exists( outfile ) );
954 outfile = filename_template;
957 std::ofstream fd(outfile);
960 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
972 std::copy(container_begin, container_end,
973 std::ostream_iterator<typename In::value_type>(fd, sep.c_str()));
984 #else // not WITH_CLUTCHLOG
993 #pragma GCC diagnostic push
994 #pragma GCC diagnostic ignored "-Wreturn-type"
999 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
1002 enum class fg { black, red, green, yellow, blue, magenta, cyan, white, none }
fore;
1003 enum class bg { black, red, green, yellow, blue, magenta, cyan, white, none }
back;
1004 enum class typo { reset, bold, underline, inverse, none }
style;
1013 std::ostream&
print_on(std::ostream&)
const {}
1015 friend std::ostream&
operator<<(std::ostream&,
const fmt&) {}
1016 std::string
operator()(
const std::string&)
const {}
1020 void operator=(
clutchlog const&) =
delete;
1033 void format(
const std::string&) {}
1034 std::string
format()
const {}
1039 void out(std::ostream&) {}
1040 std::ostream&
out() {}
1042 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
1043 void depth(
size_t) {}
1044 size_t depth()
const {}
1046 void depth_mark(
const std::string) {}
1047 std::string depth_mark()
const {}
1048 void strip_calls(
const size_t) {}
1049 size_t strip_calls()
const {}
1051 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
1052 void hfill_mark(
const char) {}
1053 char hfill_mark()
const {}
1054 void hfill_fmt(fmt) {}
1055 fmt hfill_fmt()
const {}
1056 void hfill_min(
const size_t) {}
1057 size_t hfill_min() {}
1058 void hfill_max(
const size_t) {}
1059 size_t hfill_max() {}
1065 const std::map<std::string,level>
levels()
const {}
1068 void file(std::string) {}
1069 void func(std::string) {}
1070 void line(std::string) {}
1072 #pragma GCC diagnostic push
1073 #pragma GCC diagnostic ignored "-Wunused-parameter"
1076 const std::string& in_function=
".*",
1077 const std::string& in_line=
".*"
1080 #pragma GCC diagnostic pop
1081 template<
class ... FMT>
1120 const std::string&,
const std::string&,
size_t
1128 const std::string&,
const std::string&,
size_t,
1134 #pragma GCC diagnostic pop
1135 #endif // WITH_CLUTCHLOG
1137 #endif // CLUTCHLOG_H
static std::string default_depth_mark
Default mark for stack depth.
std::string _format_log
Current format of the standard output.
std::map< level, fmt > _level_fmt
Dictionary of level identifier to their format.
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.
std::string str() const
Return the formatting code as a string.
void line(std::string line)
Set the regular expression filtering the line location.
static std::string dump_default_format
Default format of the comment line in file dump.
void out(std::ostream &out)
Set the output stream on which to print.
static std::string dump_default_sep
Default item separator for dump.
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...
#define CLUTCHLOG_DEFAULT_DEPTH_MARK
Compile-time default mark for stack depth.
static unsigned int default_strip_calls
Number of call stack levels to remove from depth display by default.
static size_t default_hfill_min
Default minimum width (number of characters) at which to fill for right-aligning the right part of me...
std::string replace(const std::string &form, const std::string &mark, const std::string &tag) const
Replace mark by tag in form.
enum clutchlog::fmt::bg back
Background color.
fg
Foreground color codes.
enum clutchlog::fmt::fg fore
Foreground color.
static char default_hfill_char
Default character used as a filling for right-align the right part of messages with "{hfill}".
bool matches
Everything is compatible.
enum clutchlog::fmt::typo style
Typographic style.
void format_comment(const std::string &format)
Set the template string for dumps.
void file(std::string file)
Set the regular expression filtering the file location.
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.
fmt()
 Empty constructor, only useful for a no-op formatter.
void style(level stage, fmt style)
Set the style (color and typo) of the given log level, passing a fmt instance.
void threshold(level l)
Set the log level (below which logs are not printed) with an identifier.
level threshold() const
Get the log level below which logs are not printed.
level
Available log levels.
static size_t default_hfill_max
Default maximum width (number of characters) for which to fill for right-aligning the right part of m...
std::regex _in_func
Current function location filter.
bg
Background color codes.
static std::string default_format
Default format of the messages.
static clutchlog & logger()
Get the logger instance.
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=dump_default_sep) const
Dump a serializable container after a comment line with log information.
Color and style formatter for ANSI terminal escape sequences.
void func(std::string func)
Set the regular expression filtering the function location.
std::string format() const
Get the template string.
std::regex _in_file
Current file location filter.
void style(level stage, FMT... styles)
Set the style (color and typo) of the given log level.
level level_of(const std::string name)
Return the log level tag corresponding to the given pre-configured name.
const std::map< level, std::string > _level_word
Dictionary of level identifier to their string representation.
std::string operator()(const std::string &msg) const
Format the given string with the currently encoded format.
#define CLUTCHLOG_DEFAULT_FORMAT
Compile-time default format of the messages (debug mode: with absolute location).
std::regex _in_line
Current line location filter.
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.
std::string format_comment() const
Get the template string for dumps.
std::string _format_dump
Current format of the file output.
#define CLUTCHDUMP_DEFAULT_SEP
Compile-time default item separator for dump.
Structure holding a location matching.
#define CLUTCHLOG_DEFAULT_HFILL_MARK
Character used as a filling for right-align the right part of messages with "{hfill}".
std::ostream & print_on(std::ostream &os) const
Print the currently encoded format escape code on the given output stream.
std::ostream * _out
Standard output.
std::ostream & out()
Get the output stream on which to print.
void threshold(const std::string &l)
Set the log level (below which logs are not printed) with a string.
const std::map< std::string, level > & levels() const
Get the map of available log levels string representations toward their identifier....
size_t _strip_calls
Current number of call stack levels to remove from depth display.
level stage
Current log level.
bool there
Location is compatible.
#define CLUTCHLOG_STRIP_CALLS
Compile-time number of call stack levels to remove from depth display by default.
friend std::ostream & operator<<(std::ostream &os, const fmt &fmt)
Output stream overload.
std::map< std::string, level > _word_level
Dictionary of level string to their identifier.
level _stage
Current log level.
fmt style(level stage) const
Get the configured fmt instance of the given log level.
typo
Typographic style codes.
void location(const std::string &in_file, const std::string &in_function=".*", const std::string &in_line=".*")
Set the regular expressions filtering the location.
#define CLUTCHLOG_HAVE_UNIX_SYSINFO
True if POSIX headers necessary for stack depth management are available.
The single class which holds everything.
#define CLUTCHDUMP_DEFAULT_FORMAT
Compile-time default format of the comment line in file dump.