nestlog: print [file:line] on rhs of log line

This commit is contained in:
Roland Conybeare 2023-09-14 17:02:50 -04:00
commit a150913045
6 changed files with 206 additions and 46 deletions

View file

@ -24,6 +24,7 @@ int
main(int argc, char ** argv) {
log_config::style = FS_Pretty;
log_config::indent_width = 4;
log_config::location_tab = 40;
int n = 4;
@ -34,3 +35,5 @@ main(int argc, char ** argv) {
log(xtag("n", n));
log("<-", xtag("fib(n)", fn));
}
/* ex3/ex3.cpp */

View file

@ -0,0 +1,70 @@
/* @file filename.hpp */
#pragma once
#include <iostream>
#include <cstdint>
namespace xo {
/* Example:
* os << basename("/path/to/basename.cpp")
* prints
* basename.cpp
* on os
*/
/* Tag to drive header-only expression */
template <typename Tag>
class basename_impl {
public:
basename_impl(std::string_view path)
: path_{path} {}
std::string_view const & path() const { return path_; }
/* /home/roland/proj/nestlog/include/nestlog/filename.hpp
* <-basename->
*/
static void print_basename(std::ostream & os, std::string_view const & s) {
std::size_t p = exclude_dirname(s);
os << s.substr(p);
} /*print_basename*/
private:
static std::size_t exclude_dirname(std::string_view const & s) {
std::size_t z = s.size();
if (z == 0)
return 0;
if (s[z-1] == '/') {
/* ignore trailing '/' */
return exclude_dirname(s.substr(0, z-1));
}
std::size_t p = s.find_last_of('/');
if (p == std::string_view::npos)
return 0;
else
return p + 1;
} /*exclude_dirname*/
private:
/* some unix pathname, e.g. [/home/roland/proj/nestlog/include/nestlog/filename.hpp] */
std::string_view path_;
}; /*basename_impl*/
using basename = basename_impl<class basename_impl_tag>;
inline std::ostream &
operator<<(std::ostream & os,
basename const & bn)
{
basename::print_basename(os, bn.path());
return os;
}
} /*xo*/
/* end filename.hpp */

View file

@ -13,6 +13,10 @@ namespace xo {
static std::uint32_t indent_width;
/* display style for function names. FS_Simple|FS_Pretty|FS_Streamlined */
static function_style style;
/* 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;
}; /*log_config_impl*/
template <typename Tag>
@ -23,6 +27,14 @@ namespace xo {
function_style
log_config_impl<Tag>::style = FS_Streamlined;
template <typename Tag>
bool
log_config_impl<Tag>::location_enabled = true;
template <typename Tag>
std::uint32_t
log_config_impl<Tag>::location_tab = 80;
using log_config = log_config_impl<class log_config_tag>;
} /*namespace xo*/

View file

@ -5,7 +5,9 @@
#include "log_config.hpp"
#include "log_streambuf.hpp"
#include "pad.hpp"
#include "filename.hpp"
#include <ostream>
#include <sstream>
#include <memory> // for std::unique_ptr
namespace xo {
@ -42,6 +44,12 @@ namespace xo {
p_sbuf_phase2_->reset_stream();
}
void set_location(std::string_view file, std::uint32_t line) {
this->location_flag_ = true;
this->file_ = std::move(file);
this->line_ = line;
} /*set_location*/
private:
/* common implementation for .preamble(), .postamble() */
void entryexit_aux(function_style style,
@ -59,6 +67,21 @@ namespace xo {
*/
std::unique_ptr<log_streambuf_type> p_sbuf_phase1_;
/* #of characters found in .p_sbuf_phase1 since last \n.
* this value is established+updated in .flush2sbuf().
* (in particular ignored by stream .ss())
*/
std::size_t lpos_ = 0;
/* whenever .set_location() is called:
* - capture (file, line)
* - print them near right margin with next output line
* - ..and reset .location_flag
*/
bool location_flag_ = false;
std::string_view file_;
std::uint32_t line_ = 0;
/* buffer space for handling scope::log() calls that span multiple lines;
* inserts extra characters in effort to indent gracefully
*/
@ -99,11 +122,6 @@ namespace xo {
/* indent to nesting level */
this->ss_ << pad(this->nesting_level_ * log_config::indent_width, pad_char);
#ifdef OBSOLETE
for(uint32_t i = 0, n = this->nesting_level_; i<n; ++i) {
this->ss_ << pad_char;
}
#endif
} /*indent*/
template <typename CharT, typename Traits>
@ -124,7 +142,7 @@ namespace xo {
if (log_config::indent_width > 1)
this->ss_ << ' ';
/* scope name */
/* scope name - note no trailing newline; expect .preamble()/.postamble() caller to supply */
this->ss_ << function_name(style, name1) << name2;
} /*entryexit_aux*/
@ -153,12 +171,15 @@ namespace xo {
log_streambuf_type * sbuf1 = this->p_sbuf_phase1_.get();
log_streambuf_type * sbuf2 = this->p_sbuf_phase2_.get();
/* expecting sbuf to contain one line of output.
/* generally expecting sbuf to contain one line of output.
* if it contains multiple newlines, need to indent
* after each one.
*
* will scan output in *sbuf1, post-process to *sbuf2,
* then write *sbuf2 to clog
* then write *sbuf2 to output stream
*
* note: we inherit .lpos from prec call to .flush2sbuf(),
* in the unlikely event that it's non-zero
*/
char const * s = sbuf1->lo();
char const * e = s + sbuf1->pos();
@ -177,6 +198,8 @@ namespace xo {
/* for indenting, looking for first 'space following non-space, on first line', if any */
std::size_t lpos_on_newline = 0;
while(p < e) {
if(space_after_nonspace) {
;
@ -191,18 +214,50 @@ namespace xo {
if(*p == '\n') {
++p;
/* reset .pos on newline */
lpos_on_newline = this->lpos_;
this->lpos_ = 0;
break;
} else {
++p;
/* increment .lpos on non-newline */
++(this->lpos_);
}
}
/* p=e or *p=\n */
/* charseq [s,p) does not contain any newlines, print it */
sbuf2->sputn(s, p - s);
if (lpos_on_newline > 0) {
/* charseq [s,p) does not contain any newlines, print it */
sbuf2->sputn(s, p - s - 1);
if(p == e) {
if (this->location_flag_) {
/* 'tab' to position 80 */
sbuf2->sputc(' ');
for (std::uint32_t i = lpos_on_newline + 1; i < log_config::location_tab; ++i)
sbuf2->sputc(' ');
std::stringstream ss;
ss << "[" << basename(this->file_) << ":" << this->line_ << "]";
std::string ss_str = std::move(ss.str()); /*c++20*/
sbuf2->sputn(ss_str.c_str(), ss_str.size());
this->location_flag_ = false;
this->file_ = "";
this->line_ = 0;
}
sbuf2->sputc('\n');
} else {
/* control here if .flush2sbuf() called without trailing newline in .p_sbuf_phase1 */
sbuf2->sputn(s, p - s);
}
if (p == e) {
break;
}

View file

@ -36,30 +36,30 @@ namespace xo {
protected:
virtual std::streamsize
xsputn(char const * s, std::streamsize n) override {
/* s must be an address in [this->lo() .. this->lo() + capacity()] */
/* s must be an address in [this->lo() .. this->lo() + capacity()] */
assert(this->hi() >= this->pptr());
assert(this->hi() >= this->pptr());
#ifdef NOT_USING_DEBUG
std::cout << "xsputn: pbase=" << (void *)(this->pbase())
<< ", pptr=" << (void*)(this->pptr())
<< "(+" << (this->pptr() - this->lo()) << ")"
<< ", n=" << n << " -> (+" << (this->pptr() + n - this->lo()) << ")"
<< ", buf_v.size=" << this->buf_v_.size()
<< std::endl;
std::cout << "xsputn: pbase=" << (void *)(this->pbase())
<< ", pptr=" << (void*)(this->pptr())
<< "(+" << (this->pptr() - this->lo()) << ")"
<< ", n=" << n << " -> (+" << (this->pptr() + n - this->lo()) << ")"
<< ", buf_v.size=" << this->buf_v_.size()
<< std::endl;
#endif
//std::cout << "xsputn: s=" << quoted(string_view(s, n)) << ", n=" << n << std::endl;
//std::cout << "xsputn: s=" << quoted(string_view(s, n)) << ", n=" << n << std::endl;
if (this->pptr() + n > this->hi()) {
n = this->hi() - this->pptr();
std::memcpy(this->pptr(), s, n);
} else {
std::memcpy(this->pptr(), s, n);
}
this->pbump(n);
if (this->pptr() + n > this->hi()) {
n = this->hi() - this->pptr();
std::memcpy(this->pptr(), s, n);
} else {
std::memcpy(this->pptr(), s, n);
}
this->pbump(n);
return n;
} /*xsputn*/
return n;
} /*xsputn*/
virtual int_type
overflow(int_type new_ch) override

View file

@ -1,8 +1,9 @@
/* @file scophpp */
/* @file scope.hpp */
#pragma once
#include "log_state.hpp"
#include "filename.hpp"
#include "tostr.hpp"
#include "tag.hpp"
@ -15,38 +16,44 @@ namespace xo {
template <typename ChartT, typename Traits>
class state_impl;
# define XO_ENTER0() xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__)
# define XO_ENTER1(debug_flag) xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__, debug_flag)
# define XO_ENTER0() xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__, __FILE__, __LINE__)
# define XO_ENTER1(debug_flag) xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__, __FILE__, __LINE__, debug_flag)
//# define XO_SSETUP0() xo::scope_setup(__FUNCTION__)
# define XO_SSETUP0() xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__)
# define XO_SSETUP0() xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__, __FILE__, __LINE__)
/* throw exception if condition not met*/
# define XO_EXPECT(f,msg) if(!(f)) { throw std::runtime_error(msg); }
/* establish scope using current function name */
# define XO_SCOPE(name) xo::scope name(xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__))
# define XO_SCOPE(name) xo::scope name(xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__, __FILE__, __LINE__))
/* like XO_SCOPE(name), but also set enabled flag */
# define XO_SCOPE2(name, debug_flag) xo::scope name(xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__, debug_flag))
# define XO_SCOPE_DISABLED(name) xo::scope name(xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__, false))
# define XO_SCOPE2(name, debug_flag) xo::scope name(xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__, __FILE__, __LINE__, debug_flag))
# define XO_SCOPE_DISABLED(name) xo::scope name(xo::scope_setup(xo::log_config::style, __PRETTY_FUNCTION__, __FILE__, __LINE__, false))
# define XO_STUB() { XO_SCOPE(logr); logr.log("STUB"); }
/* convenience class for basic_scope<..> construction (see below).
* use to disambiguate setup from other arguments
*/
struct scope_setup {
scope_setup(function_style style, std::string_view name1, std::string_view name2, bool enabled_flag)
: style_{style}, name1_{name1}, name2_{name2}, enabled_flag_{enabled_flag} {}
scope_setup(function_style style, std::string_view name1, bool enabled_flag)
: scope_setup(style, name1, "", enabled_flag) {}
scope_setup(function_style style, std::string_view name1)
: scope_setup(style, name1, true /*enabled_flag*/) {}
scope_setup(function_style style, std::string_view name1, std::string_view name2,
std::string_view file, std::uint32_t line, bool enabled_flag)
: style_{style}, name1_{name1}, name2_{name2}, file_{file}, line_{line}, enabled_flag_{enabled_flag} {}
scope_setup(function_style style, std::string_view name1, std::string_view file, std::uint32_t line, bool enabled_flag)
: scope_setup(style, name1, "", file, line, enabled_flag) {}
scope_setup(function_style style, std::string_view name1, std::string_view file, std::uint32_t line)
: scope_setup(style, name1, file, line, true /*enabled_flag*/) {}
static scope_setup literal(std::string_view name1, bool enabled_flag = true) { return scope_setup(FS_Literal, name1, enabled_flag); }
static scope_setup literal(std::string_view name1, std::string_view name2, bool enabled_flag = true) { return scope_setup(FS_Literal, name1, name2, enabled_flag); }
//static scope_setup literal(std::string_view name1, bool enabled_flag = true) { return scope_setup(FS_Literal, name1, enabled_flag); }
//static scope_setup literal(std::string_view name1, std::string_view name2, bool enabled_flag = true) { return scope_setup(FS_Literal, name1, name2, enabled_flag); }
function_style style_ = FS_Pretty;
std::string_view name1_ = "<.name1>";
std::string_view name2_ = "<.name2>";
/* __FILE__ */
std::string_view file_ = "<.file>";
/* __LINE__ */
std::uint32_t line_ = 0;
/* true iff output enabled for this scope */
bool enabled_flag_ = false;
}; /*scope_setup*/
@ -155,6 +162,10 @@ namespace xo {
std::string_view name1_ = "<name1>";
/* name of this scope (part 2) */
std::string_view name2_ = "::<name2>";
/* captured value of __FILE__ */
std::string_view file_ = "<file>";
/* captured value of __LINE__ */
std::uint32_t line_ = 0;
/* set once per scope .finalized=true <-> logging disabled */
bool finalized_ = false;
}; /*basic_scope*/
@ -166,14 +177,23 @@ namespace xo {
: style_{setup.style_},
name1_{std::move(setup.name1_)},
name2_{std::move(setup.name2_)},
file_{std::move(setup.file_)},
line_{setup.line_},
finalized_{!setup.enabled_flag_}
{
if(setup.enabled_flag_) {
state_impl_type * logstate = basic_scope::require_thread_local_state();
std::ostream & os = logstate2stream(logstate);
logstate->preamble(this->style_, this->name1_, this->name2_);
tosn(logstate2stream(logstate), " ", std::forward<Tn>(args)...);
tosn(os, " ", std::forward<Tn>(args)...);
if (log_config::location_enabled) {
/* prints on next call to flush2sbuf */
logstate->set_location(this->file_, this->line_);
//tosn(os, " [", basename(this->file_), ":", this->line_, "]");
}
logstate->flush2sbuf(std::clog.rdbuf());