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

@ -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 */
#include "color.hpp"
#include <iostream>
#include <cstdint>
@ -19,11 +21,19 @@ namespace xo {
template <typename Tag>
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;
}

View file

@ -3,6 +3,7 @@
#pragma once
#include "function.hpp"
#include "nestlog/color.hpp"
#include <cstdint>
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 <typename Tag>
@ -27,6 +37,18 @@ namespace xo {
function_style
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>
bool
log_config_impl<Tag>::location_enabled = true;
@ -35,6 +57,10 @@ namespace xo {
std::uint32_t
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>;
} /*namespace xo*/

View file

@ -6,11 +6,17 @@
#include "log_streambuf.hpp"
#include "pad.hpp"
#include "filename.hpp"
#include "code_location.hpp"
#include <ostream>
#include <sstream>
#include <memory> // for std::unique_ptr
namespace xo {
enum EntryExit {
EE_Entry,
EE_Exit
};
// track per-thread state associated with nesting logger
//
template <typename CharT, typename Traits>
@ -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<CharT, Traits>::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 <typename CharT, typename Traits>
@ -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 <typename CharT, typename Traits>
@ -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 <typename CharT, typename Traits>
@ -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());