diff --git a/README.md b/README.md index 10c399c..d8a866d 100644 --- a/README.md +++ b/README.md @@ -200,12 +200,15 @@ Available tags are: - `{file}`: the current file (absolute path), - `{func}`: the current function, - `{line}`: the current line number, -- `{level_fmt}`: the format of the current level (i.e. configured with `clutchlog::style`). +- `{level_fmt}`: the style of the current level (i.e. configured with `clutchlog::style`), +- `{filehash_fmt}`: a style for file names, which is value-dependant (see `clutchlog::filehash_styles`), +- `{funchash_fmt}`: a style for function names, which is value-dependant (see `clutchlog::funchash_styles`). Some tags are only available on POSIX operating systems as of now: - `{name}`: the name of the current binary, - `{depth}`: the current depth of the call stack, - `{depth_marks}`: as many chevrons `>` as there is calls in the stack, +- `{depth_fmt}`: a style depending on the current depth value (see `clutchlog::depth_styles`), - `{hfill}`: Inserts a sequence of characters that will stretch to fill the space available in the current terminal, between the rightmost and leftmost part of the log message. @@ -220,10 +223,12 @@ clutchlog will not put the location-related tags in the message formats (i.e. `{name}`, `{func}`, and `{line}`) when not in Debug builds. -Output style ------------- +Output Styling +-------------- -Output lines can be colored differently depending on the log level. +Output lines can be styled differently depending on their content. + +For example, output lines can be colored differently depending on the log level. ```cpp // Print error messages in bold red: log.style(level::error, // First, the log level. @@ -251,7 +256,8 @@ depending on the types of arguments passed to styling functions: - `clutchlog::fg::none` and `clutchlog::bg::none` can be passed in all modes. For example, all the following lines encode -a bright red foreground for the critical level: +a bright red foreground for the critical level +(see the "Colors" section below): ```cpp log.style(level:critical, fmt::fg::red); // 16-colors mode. @@ -371,6 +377,42 @@ log.style(level::info, fg::none, 100,0,0, typo::bold); // No color over bold dar ``` +### Value-dependant Format Tags + +Some tags can be used to change the style of (part of) the output line, + +*depending on its content*. +The `{filehash_fmt}` and `{funchash_fmt}` will introduce a styling sequence +which depends on the current file name, and function name respectively. +The chosen style is chosen at random among the candidate ones, +but will always be the same for each value. + +The set of candidate styles can be configured with `clutchlog::filehash_styles` +and `clutchlog::funchash_styles`, which both take a vector of `clutchlog::fmt` +objects as argument: +```cpp +// Either one or the other color for filenames: +log.filehash_styles( { fmt(fg::red), fmt(fg::yellow) } ); +// This would fix the function name style to a single one: +log.funchash_styles( { fmt(typo::bold) } ); +// Works with any `fmt` constructor +// (here, shades of blues in 256-colors mode): +log.funchash_styles( { fmt(33), fmt(27), fmt(39), fmt(45) } ); +``` + +The same idea applies to `{depth_fmt}`. +However, if `clutchlog::depth_styles` is configured, +then the styles are chosen *in order*. +That is, a depth of 1 would lead to the first style being chosen. +If the current depth of the stack is larger than the number of configured +styles, then the last one is used. +For example: +```cpp +// Increasingly darker depth level colors (using the 256-colors mode). +log.depth_styles({ fmt(255), fmt(250), fmt(245), fmt(240), fmt(235) }); +``` + + Advanced Usage ============== diff --git a/clutchlog/clutchlog.h b/clutchlog/clutchlog.h index cf98ad0..c79e0e9 100644 --- a/clutchlog/clutchlog.h +++ b/clutchlog/clutchlog.h @@ -810,6 +810,16 @@ class clutchlog this->print_on(os); return os.str(); } + + static fmt hash( const std::string& str, const std::vector domain = {}) + { + size_t h = std::hash{}(str); + if(domain.size() == 0) { + return fmt(static_cast(h % 256)); + } else { + return fmt(domain[h % domain.size()]); + } + } }; // fmt class /** @} */ @@ -872,6 +882,10 @@ class clutchlog _in_file(".*"), _in_func(".*"), _in_line(".*") + // Empty vectors by default: + // _filehash_fmts + // _funchash_fmts + // _depth_fmts { // Reverse the level->word map into a word->level map. for(auto& lw : _level_word) { @@ -926,9 +940,16 @@ class clutchlog /** Current line location filter. */ std::regex _in_line; + /** List of candidate format objects for value-dependant file name styling. */ + std::vector _filehash_fmts; + /** List of candidate format objects for value-dependant function name styling. */ + std::vector _funchash_fmts; + #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1 /** Maximum buffer size for backtrace message. */ static const size_t _max_buffer = 4096; + /** Ordered list of format objects for value-dependant depth styling. */ + std::vector _depth_fmts; #endif #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL @@ -974,11 +995,11 @@ class clutchlog size_t strip_calls() const {return _strip_calls;} #endif #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL == 1 - //! Set the character for the stretching hfill marker. + //! Set the character for the stretching `{hfill}` template tag marker. void hfill_mark(const char mark) {_hfill_char = mark;} - //! Get the character for the stretching hfill marker. + //! Get the character for the stretching `{hfill}` template tag marker. char hfill_mark() const {return _hfill_char;} - //! Set the style for the stretching hfill marker, with a `fmt` object. + //! Set the style for the stretching `{hfill}` template tag marker, with a `fmt` object. void hfill_style(fmt style) {_hfill_fmt = style;} /** Set the style for the stretching hfill marker. * @@ -986,17 +1007,42 @@ class clutchlog */ template void hfill_style(FMT... styles) { this->hfill_style(fmt(styles...)); } - //! Get the character for the stretching hfill marker. + //! Get the character for the stretching `{hfill}` template tag marker. fmt hfill_style() const {return _hfill_fmt;} - //! Set the maximum width for which to hfill. */ + //! Set the maximum width for which to `{hfill}`. void hfill_max(const size_t nmax) {_hfill_max = nmax;} - //! Get the maximum width for which to hfill. */ + //! Get the maximum width for which to `{hfill}`. size_t hfill_max() {return _hfill_max;} - //! Set the minimum width at which to hfill. */ + //! Set the minimum width at which to `{hfill}`. void hfill_min(const size_t nmin) {_hfill_min = nmin;} - //! Get the minimum width at which to hfill. */ + //! Get the minimum width at which to `{hfill}`. size_t hfill_min() {return _hfill_min;} #endif + /** Set the candidate styles for value-dependant file name formatting. + * + * Style will be chosen based on the hash value of the filename + * among the candidate ones. + * + * See the `{filehash_fmt}` template tag. + */ + void filehash_styles(std::vector styles) {_filehash_fmts = styles;} + /** Set the candidate styles for value-dependant function name formatting. + * + * Style will be chosen based on the hash value of the filename + * among the candidate ones. + * + * See the `{funchash_fmt}` template tag. + */ + void funchash_styles(std::vector styles) {_funchash_fmts = styles;} + /** Set the styles for value-dependant depth formatting. + * + * The given list should be ordered, styles will be applied + * for the corresponding depth level. If the actual depth is + * larger than the number of styles, the last one is used. + * + * See the `{depth_fmt}` template tag. + */ + void depth_styles(std::vector styles) {_depth_fmts = styles;} //! Set the log level (below which logs are not printed) with an identifier. void threshold(level l) {_stage = l;} @@ -1238,17 +1284,26 @@ class clutchlog row = replace(row, "\\{level_short\\}", _level_short.at(stage)); #if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1 + size_t actual_depth = depth - _strip_calls; row = replace(row, "\\{name\\}", name); - row = replace(row, "\\{depth\\}", depth - _strip_calls); + row = replace(row, "\\{depth\\}", actual_depth); std::ostringstream chevrons; for(size_t i = _strip_calls; i < depth; ++i) { chevrons << _depth_mark; } row = replace(row, "\\{depth_marks\\}", chevrons.str()); -#endif + if(_depth_fmts.size() == 0) { + row = replace(row, "\\{depth_fmt\\}", fmt(actual_depth % 256).str() ); + } else { + row = replace(row, "\\{depth_fmt\\}", + _depth_fmts[std::min(actual_depth,_depth_fmts.size()-1)].str() ); + } +#endif row = replace(row, "\\{level_fmt\\}", _level_fmt.at(stage).str()); + row = replace(row, "\\{filehash_fmt\\}", fmt::hash(file, _filehash_fmts).str() ); + row = replace(row, "\\{funchash_fmt\\}", fmt::hash(func, _funchash_fmts).str() ); #if CLUTCHLOG_HAVE_UNIX_SYSIOCTL // hfill is replaced last to allow for correct line width estimation. @@ -1490,6 +1545,7 @@ class clutchlog friend std::ostream& operator<<(std::ostream&, const fmt&) {} std::string operator()( const std::string&) const {} std::string str() const {} + static fmt hash( const std::string&, const std::vector) {} }; public: clutchlog(clutchlog const&) = delete; @@ -1534,6 +1590,9 @@ class clutchlog void hfill_max(const size_t) {} size_t hfill_max() {} #endif + void filehash_styles(std::vector ) {} + void funchash_styles(std::vector ) {} + void depth_styles(std::vector) {} void threshold(level) {} void threshold(const std::string&) {} diff --git a/tests/t-hash-color.cpp b/tests/t-hash-color.cpp new file mode 100644 index 0000000..1c6882f --- /dev/null +++ b/tests/t-hash-color.cpp @@ -0,0 +1,49 @@ +#include +#include + +#include "../clutchlog/clutchlog.h" + +void deepcall() +{ + CLUTCHLOG(warning,"at depth 4"); +} + +void subsubsubcall() +{ + CLUTCHLOG(warning,"at depth 3"); + deepcall(); +} + +void subsubcall() +{ + CLUTCHLOG(warning,"at depth 2"); + subsubsubcall(); +} + +void subcall() +{ + CLUTCHLOG(warning,"at depth 1"); + subsubcall(); +} + +int main(/*const int argc, char* argv[]*/) +{ + auto& log = clutchlog::logger(); + using fmt = clutchlog::fmt; + using typo = clutchlog::fmt::typo; + + fmt reset(typo::reset); + std::ostringstream tpl; + tpl << "{level_fmt}Having a {level} {filehash_fmt}within {file} {funchash_fmt}calling {func} {depth_fmt}at level {depth}" + << reset << " : {msg}\n"; + log.format(tpl.str()); + log.threshold(clutchlog::level::xdebug); + + log.filehash_styles( {fmt(fmt::fg::red), fmt(fmt::fg::yellow)} ); + log.funchash_styles( {fmt(fmt::fg::green), fmt(fmt::fg::blue), + fmt(fmt::fg::bright_green), fmt(fmt::fg::bright_blue), fmt(fmt::fg::magenta)} ); + log.depth_styles( {fmt(255),fmt(250),fmt(245),fmt(240)} ); + + CLUTCHLOG(warning,"in main"); + subcall(); +}