nestlog: + terminal colors for entry/exit/code location

This commit is contained in:
Roland Conybeare 2023-09-15 13:24:02 -04:00
commit fd2be2a4ae
6 changed files with 265 additions and 12 deletions

View file

@ -22,9 +22,13 @@ fib(int n) {
int int
main(int argc, char ** argv) { main(int argc, char ** argv) {
log_config::style = FS_Pretty; log_config::style = FS_Streamlined;
log_config::indent_width = 4; log_config::indent_width = 4;
log_config::location_tab = 40; 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; int n = 4;

View file

@ -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 <typename Tag>
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<class code_location_impl_tag>;
inline std::ostream &
operator<<(std::ostream & os,
code_location const & x)
{
x.print_code_location(os);
return os;
}
} /*namespace xo*/
/* end code_location.hpp */

123
include/nestlog/color.hpp Normal file
View file

@ -0,0 +1,123 @@
/* color.hpp */
#pragma once
#include <ostream>
//#include <utility> // for std::move
#include <cstdint>
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 <typename Contents>
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 <typename Contents>
color_impl<Contents> with_ansi_color(std::uint32_t color, Contents && contents) {
return color_impl<Contents>(CF_All, CE_Ansi, color, std::move(contents));
} /*with_ansi_color*/
template <typename Contents>
color_impl<Contents> with_xterm_color(std::uint32_t color, Contents && contents) {
return color_impl<Contents>(CF_All, CE_Xterm, color, std::move(contents));
} /*with_ansi_color*/
template <typename Contents>
color_impl<Contents> with_color(color_encoding encoding, std::uint32_t color, Contents && contents) {
return color_impl<Contents>(CF_All, encoding, color, std::move(contents));
} /*with_color*/
inline color_impl<int>
color_on_ansi(std::uint32_t color) {
return color_impl<int>(CF_ColorOn, CE_Ansi, color, 0);
} /*color_on_ansi*/
inline color_impl<int>
color_on_xterm(std::uint32_t color) {
return color_impl<int>(CF_ColorOn, CE_Xterm, color, 0);
} /*color_on_xterm*/
inline color_impl<int>
color_on(color_encoding encoding, std::uint32_t color) {
return color_impl<int>(CF_ColorOn, encoding, color, 0);
} /*color_on*/
inline color_impl<int>
color_off() {
/* any non-zero value works here for color */
return color_impl<int>(CF_ColorOff, CE_None, 1 /*color*/, 0);
} /*color_off*/
template <typename Contents>
inline std::ostream &
operator<<(std::ostream & os, color_impl<Contents> const & x) {
x.print(os);
return os;
} /*operator<<*/
} /*namespace xo*/
/* end color.hpp */

View file

@ -1,5 +1,7 @@
/* @file function.hpp */ /* @file function.hpp */
#include "color.hpp"
#include <iostream> #include <iostream>
#include <cstdint> #include <cstdint>
@ -19,11 +21,19 @@ namespace xo {
template <typename Tag> template <typename Tag>
class function_name_impl { class function_name_impl {
public: public:
/* color: ANSI escape color (lookup Select Graphic Rendition subset)
* 0 = none
* 31 = red
*/
function_name_impl(function_style style, function_name_impl(function_style style,
std::string_view pretty) color_encoding encoding,
: style_{style}, pretty_{pretty} {} std::uint32_t color,
std::string_view pretty)
: style_{style}, encoding_{encoding}, color_{color}, pretty_{pretty} {}
function_style style() const { return style_; } 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_; } std::string_view const & pretty() const { return pretty_; }
/* e.g. /* e.g.
@ -152,11 +162,16 @@ namespace xo {
if (ch == '>' || ch == ')') if (ch == '>' || ch == ')')
--nesting_level; --nesting_level;
} }
} /*print_aux*/ } /*print_aux*/
private: private:
/* FS_Simple | FS_Pretty (= FS_Literal) | FS_Streamlined */ /* FS_Simple | FS_Pretty (= FS_Literal) | FS_Streamlined */
function_style style_; function_style style_;
/* CE_Ansi | CE_Xterm */
color_encoding encoding_;
/* color, if non-zero */
std::uint32_t color_;
/* e.g. __PRETTY_FUNCTION__ */ /* e.g. __PRETTY_FUNCTION__ */
std::string_view pretty_; std::string_view pretty_;
}; /*function_name_impl*/ }; /*function_name_impl*/
@ -167,19 +182,25 @@ namespace xo {
operator<<(std::ostream & os, operator<<(std::ostream & os,
function_name const & fn) function_name const & fn)
{ {
/* set text color */
switch(fn.style()) { switch(fn.style()) {
case FS_Literal: case FS_Literal:
os << fn.pretty(); os << with_color(fn.encoding(), fn.color(), fn.pretty());
break; break;
case FS_Pretty: case FS_Pretty:
os << "[" << fn.pretty() << "]"; os << "[" << with_color(fn.encoding(), fn.color(), fn.pretty()) << "]";
break; break;
case FS_Simple: case FS_Simple:
os << color_on(fn.encoding(), fn.color());
function_name::print_simple(os, fn.pretty()); function_name::print_simple(os, fn.pretty());
os << color_off();
break; break;
case FS_Streamlined: case FS_Streamlined:
/* omit namespace qualifiers and template arguments */ /* omit namespace qualifiers and template arguments */
os << color_on(fn.encoding(), fn.color());
function_name::print_streamlined(os, fn.pretty()); function_name::print_streamlined(os, fn.pretty());
os << color_off();
break; break;
} }

View file

@ -3,6 +3,7 @@
#pragma once #pragma once
#include "function.hpp" #include "function.hpp"
#include "nestlog/color.hpp"
#include <cstdint> #include <cstdint>
namespace xo { namespace xo {
@ -13,10 +14,19 @@ namespace xo {
static std::uint32_t indent_width; static std::uint32_t indent_width;
/* display style for function names. FS_Simple|FS_Pretty|FS_Streamlined */ /* display style for function names. FS_Simple|FS_Pretty|FS_Streamlined */
static function_style style; 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 */ /* if true, append [file:line] to output */
static bool location_enabled; static bool location_enabled;
/* when .location_enabled, write [file:line] starting this many chars from left margin */ /* when .location_enabled, write [file:line] starting this many chars from left margin */
static std::uint32_t location_tab; static std::uint32_t location_tab;
/* color to use for code location */
static std::uint32_t code_location_color;
}; /*log_config_impl*/ }; /*log_config_impl*/
template <typename Tag> template <typename Tag>
@ -27,6 +37,18 @@ namespace xo {
function_style function_style
log_config_impl<Tag>::style = FS_Streamlined; log_config_impl<Tag>::style = FS_Streamlined;
template <typename Tag>
color_encoding
log_config_impl<Tag>::encoding = CE_Ansi;
template <typename Tag>
std::uint32_t
log_config_impl<Tag>::function_entry_color = 34;
template <typename Tag>
std::uint32_t
log_config_impl<Tag>::function_exit_color = 32;
template <typename Tag> template <typename Tag>
bool bool
log_config_impl<Tag>::location_enabled = true; log_config_impl<Tag>::location_enabled = true;
@ -35,6 +57,10 @@ namespace xo {
std::uint32_t std::uint32_t
log_config_impl<Tag>::location_tab = 80; log_config_impl<Tag>::location_tab = 80;
template <typename Tag>
std::uint32_t
log_config_impl<Tag>::code_location_color = 31;
using log_config = log_config_impl<class log_config_tag>; using log_config = log_config_impl<class log_config_tag>;
} /*namespace xo*/ } /*namespace xo*/

View file

@ -6,11 +6,17 @@
#include "log_streambuf.hpp" #include "log_streambuf.hpp"
#include "pad.hpp" #include "pad.hpp"
#include "filename.hpp" #include "filename.hpp"
#include "code_location.hpp"
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
#include <memory> // for std::unique_ptr #include <memory> // for std::unique_ptr
namespace xo { namespace xo {
enum EntryExit {
EE_Entry,
EE_Exit
};
// track per-thread state associated with nesting logger // track per-thread state associated with nesting logger
// //
template <typename CharT, typename Traits> template <typename CharT, typename Traits>
@ -55,7 +61,7 @@ namespace xo {
void entryexit_aux(function_style style, void entryexit_aux(function_style style,
std::string_view name1, std::string_view name1,
std::string_view name2, std::string_view name2,
char label_char); EntryExit entryexit);
private: private:
/* current nesting level for this thread */ /* current nesting level for this thread */
@ -129,21 +135,37 @@ namespace xo {
state_impl<CharT, Traits>::entryexit_aux(function_style style, state_impl<CharT, Traits>::entryexit_aux(function_style style,
std::string_view name1, std::string_view name1,
std::string_view name2, std::string_view name2,
char label_char) EntryExit entryexit)
{ {
log_streambuf_type * sbuf = this->p_sbuf_phase1_.get(); log_streambuf_type * sbuf = this->p_sbuf_phase1_.get();
sbuf->reset_stream(); sbuf->reset_stream();
this->indent(' '); this->indent(' ');
char ee_label = '\0';
std::uint32_t fn_color = 0;
color_encoding encoding = log_config::encoding;
/* mnemonic for scope entry/exit */ /* 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) if (log_config::indent_width > 1)
this->ss_ << ' '; this->ss_ << ' ';
/* scope name - note no trailing newline; expect .preamble()/.postamble() caller to supply */ /* 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*/ } /*entryexit_aux*/
template <typename CharT, typename Traits> template <typename CharT, typename Traits>
@ -152,7 +174,7 @@ namespace xo {
std::string_view name1, std::string_view name1,
std::string_view name2) std::string_view name2)
{ {
this->entryexit_aux(style, name1, name2, '+' /*label_char*/); this->entryexit_aux(style, name1, name2, EE_Entry);
} /*preamble*/ } /*preamble*/
template <typename CharT, typename Traits> template <typename CharT, typename Traits>
@ -161,7 +183,7 @@ namespace xo {
std::string_view name1, std::string_view name1,
std::string_view name2) std::string_view name2)
{ {
this->entryexit_aux(style, name1, name2, '-' /*label_char*/); this->entryexit_aux(style, name1, name2, EE_Exit);
} /*postamble*/ } /*postamble*/
template <typename CharT, typename Traits> template <typename CharT, typename Traits>
@ -241,7 +263,8 @@ namespace xo {
sbuf2->sputc(' '); sbuf2->sputc(' ');
std::stringstream ss; 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*/ std::string ss_str = std::move(ss.str()); /*c++20*/
sbuf2->sputn(ss_str.c_str(), ss_str.size()); sbuf2->sputn(ss_str.c_str(), ss_str.size());