From fd2be2a4aec400a83aa689b655e82c5ef072e153 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 15 Sep 2023 13:24:02 -0400 Subject: [PATCH] nestlog: + terminal colors for entry/exit/code location --- example/ex3/ex3.cpp | 6 +- include/nestlog/code_location.hpp | 56 ++++++++++++++ include/nestlog/color.hpp | 123 ++++++++++++++++++++++++++++++ include/nestlog/function.hpp | 29 ++++++- include/nestlog/log_config.hpp | 26 +++++++ include/nestlog/log_state.hpp | 37 +++++++-- 6 files changed, 265 insertions(+), 12 deletions(-) create mode 100644 include/nestlog/code_location.hpp create mode 100644 include/nestlog/color.hpp diff --git a/example/ex3/ex3.cpp b/example/ex3/ex3.cpp index b4d1e9e..64682a8 100644 --- a/example/ex3/ex3.cpp +++ b/example/ex3/ex3.cpp @@ -22,9 +22,13 @@ fib(int n) { int main(int argc, char ** argv) { - log_config::style = FS_Pretty; + log_config::style = FS_Streamlined; log_config::indent_width = 4; log_config::location_tab = 40; + log_config::encoding = CE_Xterm; + log_config::function_entry_color = 69; + log_config::function_exit_color = 70; + log_config::code_location_color = 166; int n = 4; diff --git a/include/nestlog/code_location.hpp b/include/nestlog/code_location.hpp new file mode 100644 index 0000000..e343fb5 --- /dev/null +++ b/include/nestlog/code_location.hpp @@ -0,0 +1,56 @@ +/* @file code_location.hpp */ + +#pragma once + +#include "filename.hpp" +#include "color.hpp" + +namespace xo { + /* Example: + * os << code_location("/path/to/foo.cpp", 123) + * writes + * foo.cpp:123 + * on stream os + */ + + /* Tag to drive header-only expression */ + template + class code_location_impl { + public: + code_location_impl(std::string_view file, + std::uint32_t line, + color_encoding encoding = CE_Ansi, + std::uint32_t color = 31 /*red*/) + : file_{file}, line_{line}, encoding_{encoding}, color_{color} {} + + void print_code_location(std::ostream & os) const { + os << "[" + << with_color(encoding_, color_, basename(file_)) + << ":" + << line_ + << "]"; + } /*print_code_location*/ + + private: + /* __FILE__ */ + std::string_view file_; + /* __LINE__ */ + std::uint32_t line_ = 0; + /* color encoding for file,line */ + color_encoding encoding_ = CE_Ansi; + /* color for file,line */ + std::uint32_t color_ = 0; + }; /*code_location_impl*/ + + using code_location = code_location_impl; + + inline std::ostream & + operator<<(std::ostream & os, + code_location const & x) + { + x.print_code_location(os); + return os; + } +} /*namespace xo*/ + +/* end code_location.hpp */ diff --git a/include/nestlog/color.hpp b/include/nestlog/color.hpp new file mode 100644 index 0000000..8f5a4d7 --- /dev/null +++ b/include/nestlog/color.hpp @@ -0,0 +1,123 @@ +/* color.hpp */ + +#pragma once + +#include +//#include // for std::move +#include + +namespace xo { + enum color_encoding { + CE_None, + CE_Ansi, + CE_Xterm, + }; + + enum color_flags { + CF_None = 0x0, + CF_ColorOn = 0x01, + CF_Contents = 0x02, + CF_ColorOff = 0x04, + CF_All = 0x07 + }; + + template + class color_impl { + public: + color_impl(color_flags flags, color_encoding encoding, std::uint32_t color, Contents && contents) + : flags_{flags}, encoding_{encoding}, color_{color}, contents_{std::move(contents)} {} + + std::uint32_t color() const { return color_; } + Contents const & contents() const { return contents_; } + + void print(std::ostream & os) const { + if ((flags_ & CF_ColorOn) && (color_ > 0)) { + switch(encoding_) { + case CE_Ansi: + os << "\033[" << color_ << "m"; + break; + case CE_Xterm: + os << "\033[38;5;" << color_ << "m"; + break; + } + } + + if (flags_ & CF_Contents) + os << contents_; + + if ((flags_ & CF_ColorOff) && (color_ > 0)) + os << "\033[0m"; + } /*print*/ + + private: + color_flags flags_ = CF_None; + + color_encoding encoding_ = CE_Ansi; + /* .encoding = CE_Ansi: + * 0 = no color + * 30 = black + * 31 = red + * 32 = green + * 33 = yellow + * 34 = blue + * 35 = magenta + * 36 = cyan + * + * .encoding = CE_Xterm: + * see [[https://i.stack.imgur.com/KTSQa.png]] + * 0..7 standard colors (muted: grey, red, green, yellow, blue, pink, cyan, white) + * 8..15 high-intensity colors (grey, red, green, yellow, blue, pink, cyan, white) + * 16..51 chooses hue + * 16..51 + (0..5)x36 increases whiteness + */ + std::uint32_t color_ = 0; + + Contents contents_; + }; /*color_impl*/ + + template + color_impl with_ansi_color(std::uint32_t color, Contents && contents) { + return color_impl(CF_All, CE_Ansi, color, std::move(contents)); + } /*with_ansi_color*/ + + template + color_impl with_xterm_color(std::uint32_t color, Contents && contents) { + return color_impl(CF_All, CE_Xterm, color, std::move(contents)); + } /*with_ansi_color*/ + + template + color_impl with_color(color_encoding encoding, std::uint32_t color, Contents && contents) { + return color_impl(CF_All, encoding, color, std::move(contents)); + } /*with_color*/ + + inline color_impl + color_on_ansi(std::uint32_t color) { + return color_impl(CF_ColorOn, CE_Ansi, color, 0); + } /*color_on_ansi*/ + + inline color_impl + color_on_xterm(std::uint32_t color) { + return color_impl(CF_ColorOn, CE_Xterm, color, 0); + } /*color_on_xterm*/ + + inline color_impl + color_on(color_encoding encoding, std::uint32_t color) { + return color_impl(CF_ColorOn, encoding, color, 0); + } /*color_on*/ + + inline color_impl + color_off() { + /* any non-zero value works here for color */ + return color_impl(CF_ColorOff, CE_None, 1 /*color*/, 0); + } /*color_off*/ + + template + inline std::ostream & + operator<<(std::ostream & os, color_impl const & x) { + x.print(os); + return os; + } /*operator<<*/ + +} /*namespace xo*/ + +/* end color.hpp */ diff --git a/include/nestlog/function.hpp b/include/nestlog/function.hpp index da6a3f6..42c5752 100644 --- a/include/nestlog/function.hpp +++ b/include/nestlog/function.hpp @@ -1,5 +1,7 @@ /* @file function.hpp */ +#include "color.hpp" + #include #include @@ -19,11 +21,19 @@ namespace xo { template class function_name_impl { public: + /* color: ANSI escape color (lookup Select Graphic Rendition subset) + * 0 = none + * 31 = red + */ function_name_impl(function_style style, - std::string_view pretty) - : style_{style}, pretty_{pretty} {} + color_encoding encoding, + std::uint32_t color, + std::string_view pretty) + : style_{style}, encoding_{encoding}, color_{color}, pretty_{pretty} {} function_style style() const { return style_; } + color_encoding encoding() const { return encoding_; } + std::uint32_t color() const { return color_; } std::string_view const & pretty() const { return pretty_; } /* e.g. @@ -152,11 +162,16 @@ namespace xo { if (ch == '>' || ch == ')') --nesting_level; } + } /*print_aux*/ private: /* FS_Simple | FS_Pretty (= FS_Literal) | FS_Streamlined */ function_style style_; + /* CE_Ansi | CE_Xterm */ + color_encoding encoding_; + /* color, if non-zero */ + std::uint32_t color_; /* e.g. __PRETTY_FUNCTION__ */ std::string_view pretty_; }; /*function_name_impl*/ @@ -167,19 +182,25 @@ namespace xo { operator<<(std::ostream & os, function_name const & fn) { + /* set text color */ + switch(fn.style()) { case FS_Literal: - os << fn.pretty(); + os << with_color(fn.encoding(), fn.color(), fn.pretty()); break; case FS_Pretty: - os << "[" << fn.pretty() << "]"; + os << "[" << with_color(fn.encoding(), fn.color(), fn.pretty()) << "]"; break; case FS_Simple: + os << color_on(fn.encoding(), fn.color()); function_name::print_simple(os, fn.pretty()); + os << color_off(); break; case FS_Streamlined: /* omit namespace qualifiers and template arguments */ + os << color_on(fn.encoding(), fn.color()); function_name::print_streamlined(os, fn.pretty()); + os << color_off(); break; } diff --git a/include/nestlog/log_config.hpp b/include/nestlog/log_config.hpp index d6da65d..3046ef4 100644 --- a/include/nestlog/log_config.hpp +++ b/include/nestlog/log_config.hpp @@ -3,6 +3,7 @@ #pragma once #include "function.hpp" +#include "nestlog/color.hpp" #include namespace xo { @@ -13,10 +14,19 @@ namespace xo { static std::uint32_t indent_width; /* display style for function names. FS_Simple|FS_Pretty|FS_Streamlined */ static function_style style; + /* color encoding */ + static color_encoding encoding; + /* color to use for function name, on entry/exit (xo::scope creation/destruction) + * (ansi color codes, see Select Graphics Rendition subset) + */ + static std::uint32_t function_entry_color; + static std::uint32_t function_exit_color; /* if true, append [file:line] to output */ static bool location_enabled; /* when .location_enabled, write [file:line] starting this many chars from left margin */ static std::uint32_t location_tab; + /* color to use for code location */ + static std::uint32_t code_location_color; }; /*log_config_impl*/ template @@ -27,6 +37,18 @@ namespace xo { function_style log_config_impl::style = FS_Streamlined; + template + color_encoding + log_config_impl::encoding = CE_Ansi; + + template + std::uint32_t + log_config_impl::function_entry_color = 34; + + template + std::uint32_t + log_config_impl::function_exit_color = 32; + template bool log_config_impl::location_enabled = true; @@ -35,6 +57,10 @@ namespace xo { std::uint32_t log_config_impl::location_tab = 80; + template + std::uint32_t + log_config_impl::code_location_color = 31; + using log_config = log_config_impl; } /*namespace xo*/ diff --git a/include/nestlog/log_state.hpp b/include/nestlog/log_state.hpp index 73b7796..fa48adf 100644 --- a/include/nestlog/log_state.hpp +++ b/include/nestlog/log_state.hpp @@ -6,11 +6,17 @@ #include "log_streambuf.hpp" #include "pad.hpp" #include "filename.hpp" +#include "code_location.hpp" #include #include #include // for std::unique_ptr namespace xo { + enum EntryExit { + EE_Entry, + EE_Exit + }; + // track per-thread state associated with nesting logger // template @@ -55,7 +61,7 @@ namespace xo { void entryexit_aux(function_style style, std::string_view name1, std::string_view name2, - char label_char); + EntryExit entryexit); private: /* current nesting level for this thread */ @@ -129,21 +135,37 @@ namespace xo { state_impl::entryexit_aux(function_style style, std::string_view name1, std::string_view name2, - char label_char) + EntryExit entryexit) { log_streambuf_type * sbuf = this->p_sbuf_phase1_.get(); sbuf->reset_stream(); this->indent(' '); + char ee_label = '\0'; + std::uint32_t fn_color = 0; + + color_encoding encoding = log_config::encoding; + /* mnemonic for scope entry/exit */ - this->ss_ << label_char; + switch(entryexit) { + case EE_Entry: + ee_label = '+'; + fn_color = log_config::function_entry_color; + break; + case EE_Exit: + ee_label = '-'; + fn_color = log_config::function_exit_color; + break; + } + + this->ss_ << ee_label; if (log_config::indent_width > 1) this->ss_ << ' '; /* scope name - note no trailing newline; expect .preamble()/.postamble() caller to supply */ - this->ss_ << function_name(style, name1) << name2; + this->ss_ << function_name(style, encoding, fn_color, name1) << name2; } /*entryexit_aux*/ template @@ -152,7 +174,7 @@ namespace xo { std::string_view name1, std::string_view name2) { - this->entryexit_aux(style, name1, name2, '+' /*label_char*/); + this->entryexit_aux(style, name1, name2, EE_Entry); } /*preamble*/ template @@ -161,7 +183,7 @@ namespace xo { std::string_view name1, std::string_view name2) { - this->entryexit_aux(style, name1, name2, '-' /*label_char*/); + this->entryexit_aux(style, name1, name2, EE_Exit); } /*postamble*/ template @@ -241,7 +263,8 @@ namespace xo { sbuf2->sputc(' '); std::stringstream ss; - ss << "[" << basename(this->file_) << ":" << this->line_ << "]"; + ss << code_location(this->file_, this->line_, + log_config::encoding, log_config::code_location_color); std::string ss_str = std::move(ss.str()); /*c++20*/ sbuf2->sputn(ss_str.c_str(), ss_str.size());