 |
clutchlog
0.11.1
|
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_HFILL_MARK
259 #define CLUTCHLOG_HFILL_MARK '.'
261 #endif // CLUTCHLOG_HFILL_MARK
266 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
267 #ifndef CLUTCHLOG_HFILL_MAX
268 #define CLUTCHLOG_HFILL_MAX 300
298 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
363 std::vector<int> codes; codes.reserve(3);
364 if(this->
fore != fg::none) { codes.push_back(
static_cast<int>(this->
fore ));}
365 if(this->
back != bg::none) { codes.push_back(
static_cast<int>(this->
back ));}
366 if(this->
style != typo::none) { codes.push_back(
static_cast<int>(this->
style));}
367 if(codes.size() == 0) {
return os;}
370 assert(codes.size() > 0);
372 for(
size_t i=1; i < codes.size(); ++i) {
373 os <<
";" << codes[i];
408 std::ostringstream os;
410 fmt reset(fmt::typo::reset);
420 std::ostringstream os;
433 void operator=(
clutchlog const&) =
delete;
440 {level::critical,
"Critical"},
441 {level::error ,
"Error"},
442 {level::warning ,
"Warning"},
443 {level::progress,
"Progress"},
444 {level::note ,
"Note"},
445 {level::info ,
"Info"},
446 {level::debug ,
"Debug"},
447 {level::xdebug ,
"XDebug"}
450 {level::critical,fmt(fmt::fg::red, fmt::typo::underline)},
451 {level::error ,fmt(fmt::fg::red, fmt::typo::bold)},
452 {level::warning ,fmt(fmt::fg::magenta, fmt::typo::bold)},
453 {level::progress,fmt()},
454 {level::note ,fmt()},
455 {level::info ,fmt()},
456 {level::debug ,fmt()},
457 {level::xdebug ,fmt()}
461 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
463 _hfill_fmt(fmt::fg::none),
467 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
468 _depth(std::numeric_limits<size_t>::max() -
_strip_calls),
480 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
482 ioctl(STDERR_FILENO, TIOCGWINSZ, &w);
500 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
510 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
514 std::string _depth_mark;
525 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
527 static const size_t _max_buffer = 4096;
530 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
556 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
557 void depth(
size_t d) {_depth = d;}
560 size_t depth()
const {
return _depth;}
563 void depth_mark(
const std::string mark) {_depth_mark = mark;}
565 std::string depth_mark()
const {
return _depth_mark;}
572 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
573 void hfill_mark(
const char mark) {_hfill_char = mark;}
576 char hfill_mark()
const {
return _hfill_char;}
578 void hfill_style(fmt
style) {_hfill_fmt =
style;}
583 template<
class ... FMT>
584 void hfill_style(FMT... styles) { this->hfill_style(fmt(styles...)); }
586 fmt hfill_style()
const {
return _hfill_fmt;}
588 void hfill_max(
const size_t nmax) {_hfill_max = nmax;}
590 size_t hfill_max() {
return _hfill_max;}
610 return ilevel->second;
612 throw std::out_of_range(
"'" + name +
"' is not a valid log level name");
625 const std::string& in_file,
626 const std::string& in_function=
".*",
627 const std::string& in_line=
".*"
639 template<
class ... FMT>
659 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
680 const std::string&
file,
681 const std::string&
func,
690 if(not (scope.stage <=
_stage)) {
695 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
699 void *buffer[_max_buffer];
700 stack_depth = backtrace(buffer, _max_buffer);
701 scope.depth = stack_depth;
710 std::ostringstream sline; sline <<
line;
714 and std::regex_search(sline.str(),
_in_line);
717 scope.matches = scope.there;
730 const std::string& form,
731 const std::string& mark,
732 const std::string& tag
789 const std::regex re(mark);
790 return std::regex_replace(form, re, tag);
795 const std::string& form,
796 const std::string& mark,
800 std::ostringstream stag; stag << tag;
801 return replace(form, mark, stag.str());
807 const std::string& what,
809 const std::string& name,
812 const std::string&
file,
813 const std::string&
func,
821 row =
replace(row,
"\\{msg\\}", what);
827 std::string letter(1,
_level_word.at(stage).at(0));
828 row =
replace(row,
"\\{level_letter\\}", letter);
830 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
831 row =
replace(row,
"\\{name\\}", name);
834 std::ostringstream chevrons;
836 chevrons << _depth_mark;
838 row =
replace(row,
"\\{depth_marks\\}", chevrons.str());
843 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL
845 const std::string raw_row =
replace(row,
"\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]",
"");
846 const std::string hfill_tag =
"{hfill}";
847 const size_t hfill_pos = row.find(hfill_tag);
848 const size_t raw_hfill_pos = raw_row.find(hfill_tag);
849 const size_t nb_columns = std::min(_nb_columns, _hfill_max);
850 if(hfill_pos != std::string::npos) {
851 assert(raw_hfill_pos != std::string::npos);
853 const size_t left_len = raw_hfill_pos;
854 const size_t right_len = raw_row.size() - raw_hfill_pos - hfill_tag.size();
855 if(right_len+left_len > nb_columns) {
857 if(right_len < nb_columns) {
859 const std::string hfill(std::max((
size_t)0, nb_columns-right_len), _hfill_char);
860 const std::string hfill_styled = _hfill_fmt(hfill);
861 row =
replace(row,
"\\{hfill\\}",
"\n"+hfill_styled);
864 const std::string hfill(1, _hfill_char);
865 const std::string hfill_styled = _hfill_fmt(hfill);
866 row =
replace(row,
"\\{hfill\\}",
"\n"+hfill_styled);
870 const std::string hfill(std::max((
size_t)0, nb_columns - (right_len+left_len)), _hfill_char);
871 const std::string hfill_styled = _hfill_fmt(hfill);
872 row =
replace(row,
"\\{hfill\\}", hfill_styled);
876 const std::string hfill(1, _hfill_char);
877 const std::string hfill_styled = _hfill_fmt(hfill);
878 row =
replace(row,
"\\{hfill\\}", hfill_styled);
883 const std::string hfill(1, _hfill_char);
884 const std::string hfill_styled = _hfill_fmt(hfill);
885 row =
replace(row,
"\\{hfill\\}", hfill_styled);
893 const std::string& what,
894 const std::string&
file,
const std::string&
func,
size_t line
900 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
918 const In container_begin,
const In container_end,
919 const std::string&
file,
const std::string&
func,
size_t line,
920 const std::string& filename_template =
"dump_{n}.dat",
927 const std::string tag =
"\\{n\\}";
928 const std::regex re(tag);
929 std::string outfile =
"";
932 if(std::regex_search(filename_template, re)) {
936 outfile =
replace(filename_template, tag, n);
938 }
while( fs::exists( outfile ) );
942 outfile = filename_template;
945 std::ofstream fd(outfile);
948 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
960 std::copy(container_begin, container_end,
961 std::ostream_iterator<typename In::value_type>(fd, sep.c_str()));
972 #else // not WITH_CLUTCHLOG
981 #pragma GCC diagnostic push
982 #pragma GCC diagnostic ignored "-Wreturn-type"
987 enum level {critical=0, error=1, warning=2, progress=3, note=4, info=5, debug=6, xdebug=7};
990 enum class fg { black, red, green, yellow, blue, magenta, cyan, white, none }
fore;
991 enum class bg { black, red, green, yellow, blue, magenta, cyan, white, none }
back;
992 enum class typo { reset, bold, underline, inverse, none }
style;
1001 std::ostream&
print_on(std::ostream&)
const {}
1003 friend std::ostream&
operator<<(std::ostream&,
const fmt&) {}
1004 std::string
operator()(
const std::string&)
const {}
1008 void operator=(
clutchlog const&) =
delete;
1021 void format(
const std::string&) {}
1022 std::string
format()
const {}
1027 void out(std::ostream&) {}
1028 std::ostream&
out() {}
1030 #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
1031 void depth(
size_t) {}
1032 size_t depth()
const {}
1034 void depth_mark(
const std::string) {}
1035 std::string depth_mark()
const {}
1036 void strip_calls(
const size_t) {}
1037 size_t strip_calls()
const {}
1039 #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1
1040 void hfill_mark(
const char) {}
1041 char hfill_mark()
const {}
1042 void hfill_fmt(fmt) {}
1043 fmt hfill_fmt()
const {}
1044 void hfill_max(
const size_t) {}
1045 size_t hfill_max() {}
1051 const std::map<std::string,level>
levels()
const {}
1054 void file(std::string) {}
1055 void func(std::string) {}
1056 void line(std::string) {}
1058 #pragma GCC diagnostic push
1059 #pragma GCC diagnostic ignored "-Wunused-parameter"
1062 const std::string& in_function=
".*",
1063 const std::string& in_line=
".*"
1066 #pragma GCC diagnostic pop
1067 template<
class ... FMT>
1106 const std::string&,
const std::string&,
size_t
1114 const std::string&,
const std::string&,
size_t,
1120 #pragma GCC diagnostic pop
1121 #endif // WITH_CLUTCHLOG
1123 #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.
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.
#define CLUTCHLOG_HFILL_MARK
Character used as a filling for right-align the right part of messages with "{hfill}".
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 number of character used as a filling for right-align the right part of messages with...
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.
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.