From 819a2ebf15b2066bdf05d1f924c4c8f1c337c470 Mon Sep 17 00:00:00 2001 From: nojhan Date: Sun, 30 Aug 2020 23:30:00 +0200 Subject: [PATCH] refactor msg formatting, add CLUTCHDUMP - remove show_* methods. --- CMakeLists.txt | 3 + clutchlog/clutchlog.h | 241 +++++++++++++++++++++++--------- tests/t-dump.cpp | 18 +++ tests/{t-core.cpp => t-log.cpp} | 3 - 4 files changed, 196 insertions(+), 69 deletions(-) create mode 100644 tests/t-dump.cpp rename tests/{t-core.cpp => t-log.cpp} (93%) diff --git a/CMakeLists.txt b/CMakeLists.txt index e1d6843..dfda08c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,9 @@ set(VERSION_MINOR 1 CACHE STRING "Minor version number" ) set(VERSION_PATCH 0 CACHE STRING "Patch version number" ) mark_as_advanced(VERSION_MAJOR VERSION_MINOR VERSION_PATCH) +# There's no point building clutchlog tests in Release +set(CMAKE_BUILD_TYPE Debug) + ###################################################################################### # Configurable user settings diff --git a/clutchlog/clutchlog.h b/clutchlog/clutchlog.h index 29f424d..ad16cf1 100644 --- a/clutchlog/clutchlog.h +++ b/clutchlog/clutchlog.h @@ -29,6 +29,25 @@ #endif #endif +#ifdef WITH_CLUTCHLOG +#define CLUTCHLOC __FILE__, __FUNCTION__, __LINE__ + +#define CLUTCHLOG( LEVEL, WHAT ) { \ + auto& logger = clutchlog::logger(); \ + std::ostringstream msg ; msg << WHAT; \ + logger.log(msg.str(), clutchlog::level::LEVEL, CLUTCHLOC); \ +} + +#define CLUTCHDUMP( LEVEL, CONTAINER ) { \ + auto& logger = clutchlog::logger(); \ + logger.dump(std::begin(CONTAINER), std::end(CONTAINER), clutchlog::level::LEVEL, CLUTCHLOC, "\n"); \ +} + +#else +#define CLUTCHLOG ( LEVEL, WHAT ) { do {/*nothing*/} while(false); } +#define CLUTCHDUMP ( LEVEL, WHAT ) { do {/*nothing*/} while(false); } +#endif + class clutchlog { public: @@ -52,16 +71,8 @@ class clutchlog private: clutchlog() : - _out(&std::clog), - _depth(std::numeric_limits::max()), - _stage(level::error), - _in_file(".*"), - _in_func(".*"), - _in_line(".*"), - _show_name(true), - _show_depth(true), - _show_location(true), - _show_level(true), + // system, main, log + _strip_calls(3), _level_letters({ {level::quiet,"Q"}, {level::error,"E"}, @@ -69,12 +80,24 @@ class clutchlog {level::info,"I"}, {level::debug,"D"}, {level::xdebug,"X"} - }) + }), + _format("[{name}] {level}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"), + _out(&std::clog), + _depth(std::numeric_limits::max() - _strip_calls), + _stage(level::error), + _in_file(".*"), + _in_func(".*"), + _in_line(".*"), + _show_name(true), + _show_depth(true), + _show_location(true), + _show_level(true) {} protected: - // system, main, log - const size_t _strip_calls = 3; + const size_t _strip_calls; + const std::map _level_letters; + std::string _format; std::ostream* _out; size_t _depth; level _stage; @@ -85,16 +108,20 @@ class clutchlog bool _show_depth; bool _show_location; bool _show_level; - const std::map _level_letters; struct scope_t { - bool matches; - level stage; - size_t depth; - bool there; + bool matches; // everything is compatible + level stage; // current log level + size_t depth; // current depth + bool there; // location is compatible }; - scope_t locate(level stage, std::string file, std::string func, size_t line) + scope_t locate( + const level& stage, + const std::string& file, + const std::string& func, + const size_t line + ) const { const size_t max_buffer = 4096; size_t stack_depth; @@ -114,6 +141,7 @@ class clutchlog scope.matches = scope.stage <= _stage and scope.depth <= _depth + _strip_calls and scope.there; + return scope; } @@ -123,6 +151,9 @@ class clutchlog /** Configuration accessors @{ */ + void format(const std::string& format) {_format = format;} + std::string format() const {return _format;} + void out(std::ostream& out) {_out = &out;} std::ostream& out() {return *_out;} @@ -136,74 +167,152 @@ class clutchlog void func(std::string func) {_in_func = func;} void line(std::string line) {_in_line = line;} - void location(std::string in_file, std::string in_function=".*", std::string in_line=".*") + void location( + const std::string& in_file, + const std::string& in_function=".*", + const std::string& in_line=".*" + ) { file(in_file); func(in_function); line(in_line); } - void show_name(bool n) {_show_name = n;} - bool show_name() const {return _show_name;} - - void show_depth(bool d) {_show_depth = d;} - bool show_depth() const {return _show_depth;} - - void show_location(bool l) {_show_location = l;} - bool show_location() const {return _show_location;} - - void show_level(bool l) {_show_level = l;} - bool show_level() const {return _show_level;} - /** }@ Configuration */ public: /** Low-level API @{ */ - void log(std::string what, level stage, std::string file, std::string func, size_t line, bool newline) + std::string replace( + const std::string& form, + const std::string& mark, + const std::string& tag + ) const + { + std::regex re; + try { + re = std::regex(mark); + + } catch(const std::regex_error& e) { + std::cerr << "ERROR with a regular expression \"" << mark << "\": "; + switch(e.code()) { + case std::regex_constants::error_collate: + std::cerr << "the expression contains an invalid collating element name"; + break; + case std::regex_constants::error_ctype: + std::cerr << "the expression contains an invalid character class name"; + break; + case std::regex_constants::error_escape: + std::cerr << "the expression contains an invalid escaped character or a trailing escape"; + break; + case std::regex_constants::error_backref: + std::cerr << "the expression contains an invalid back reference"; + break; + case std::regex_constants::error_brack: + std::cerr << "the expression contains mismatched square brackets ('[' and ']')"; + break; + case std::regex_constants::error_paren: + std::cerr << "the expression contains mismatched parentheses ('(' and ')')"; + break; + case std::regex_constants::error_brace: + std::cerr << "the expression contains mismatched curly braces ('{' and '}')"; + break; + case std::regex_constants::error_badbrace: + std::cerr << "the expression contains an invalid range in a {} expression"; + break; + case std::regex_constants::error_range: + std::cerr << "the expression contains an invalid character range (e.g. [b-a])"; + break; + case std::regex_constants::error_space: + std::cerr << "there was not enough memory to convert the expression into a finite state machine"; + break; + case std::regex_constants::error_badrepeat: + std::cerr << "one of *?+{ was not preceded by a valid regular expression"; + break; + case std::regex_constants::error_complexity: + std::cerr << "the complexity of an attempted match exceeded a predefined level"; + break; + case std::regex_constants::error_stack: + std::cerr << "there was not enough memory to perform a match"; + break; + default: + std::cerr << "unknown error"; + } + std::cerr << std::endl; + throw; + } // catch + + return std::regex_replace(form, re, tag); + } + + std::string format( + std::string format, + const std::string& what, + const std::string& name, + const std::string& stage, + const std::string& file, + const std::string& func, + const std::string& line, + const size_t depth + ) const + { + format = replace(format, "\\{msg\\}", what); + format = replace(format, "\\{name\\}", name); + format = replace(format, "\\{level\\}", stage); + format = replace(format, "\\{file\\}", file); + format = replace(format, "\\{func\\}", func); + format = replace(format, "\\{line\\}", line); + + std::ostringstream sdepth; sdepth << depth; + format = replace(format, "\\{depth\\}", sdepth.str()); + + std::ostringstream chevrons; + for(size_t i = _strip_calls; i < depth; ++i) { + chevrons << ">"; } + format = replace(format, "\\{depth_marks\\}", chevrons.str()); + + return format; + } + + void log( + const std::string& what, + const level& stage, const std::string& file, const std::string& func, size_t line + ) const { scope_t scope = locate(stage, file, func, line); if(scope.matches) { - if(_show_name) { - *_out << "[" << basename(getenv("_")) << "]"; - } - if(_show_level) { - *_out << " " << _level_letters.at(stage) << ":"; - } - if(_show_depth) { - for(size_t i = _strip_calls; i < scope.depth; ++i) { - *_out << ">"; - } - if(scope.depth > _strip_calls) { - *_out << " "; - } - } + std::ostringstream sline; sline << line; + *_out << format(_format, what, basename(getenv("_")), + _level_letters.at(stage), file, func, + sline.str(), scope.depth ); + } // if scopes.matches + } - *_out << what; + template + void dump( + const In container_begin, const In container_end, + const level& stage, const std::string& file, const std::string& func, size_t line, + const std::string sep="\n" + ) const + // FIXME use a file name as input + { + scope_t scope = locate(stage, file, func, line); - if(_show_location) { - *_out << "\t\t\t\t\t" << file << ":" << line << " (" << func << ")"; - } - if(newline) { - *_out << std::endl; - } - } + if(scope.matches) { + std::ostringstream sline; sline << line; + *_out << "#" + << format(_format, "", basename(getenv("_")), + _level_letters.at(stage), file, func, + sline.str(), scope.depth ); + + std::copy(container_begin, container_end, + std::ostream_iterator(*_out, sep.c_str())); + } // if scopes.matches } /** }@ Low-level API */ }; -#ifdef WITH_CLUTCHLOG -#define CLUTCHLOG( LEVEL, WHAT ) { \ - auto& logger = clutchlog::logger(); \ - std::ostringstream msg ; msg << WHAT; \ - logger.log(msg.str(), clutchlog::level::LEVEL, __FILE__, __FUNCTION__, __LINE__, true); \ -} - -#else -#define CLUTCHLOG ( LEVEL, WHAT ) { do {/*nothing*/} while(false); } -#endif - #endif // __CLUTCHLOG_H__ diff --git a/tests/t-dump.cpp b/tests/t-dump.cpp new file mode 100644 index 0000000..3df4b86 --- /dev/null +++ b/tests/t-dump.cpp @@ -0,0 +1,18 @@ +#include + +#include "../clutchlog/clutchlog.h" + +int main(/*const int argc, char* argv[]*/) +{ +#ifdef WITH_CLUTCHLOG + + auto& log = clutchlog::logger(); + + log.out(std::clog); + log.threshold(clutchlog::level::xdebug); + + std::vector msg = {"hello", "world", "!"}; + + CLUTCHDUMP(xdebug, msg); +#endif +} diff --git a/tests/t-core.cpp b/tests/t-log.cpp similarity index 93% rename from tests/t-core.cpp rename to tests/t-log.cpp index 9128e76..5c75029 100644 --- a/tests/t-core.cpp +++ b/tests/t-log.cpp @@ -27,9 +27,6 @@ int main(/*const int argc, char* argv[]*/) auto& log = clutchlog::logger(); log.out(std::clog); - log.threshold(clutchlog::level::warning); - log.file("core"); - log.func("(main|f)"); std::clog << "depth: 99; threshold: xdebug; location: .*" << std::endl; log.depth(99);