227 lines
6.9 KiB
C++
227 lines
6.9 KiB
C++
/* @file log_state.hpp */
|
|
|
|
#pragma once
|
|
|
|
#include "log_streambuf.hpp"
|
|
#include <ostream>
|
|
#include <memory> // for std::unique_ptr
|
|
|
|
namespace xo {
|
|
// track per-thread state associated with nesting logger
|
|
//
|
|
template <typename CharT, typename Traits>
|
|
class state_impl {
|
|
public:
|
|
using log_streambuf_type = log_streambuf<char, std::char_traits<char>>;
|
|
|
|
public:
|
|
state_impl();
|
|
|
|
std::uint32_t nesting_level() const { return nesting_level_; }
|
|
|
|
void incr_nesting() { ++nesting_level_; }
|
|
void decr_nesting() { --nesting_level_; }
|
|
|
|
std::ostream & ss() { return ss_; }
|
|
|
|
/* call on entry to new scope */
|
|
void preamble(std::string_view name1, std::string_view name2);
|
|
/* call before each new log entry */
|
|
void indent(char pad_char);
|
|
/* call on exit from scope */
|
|
void postamble(std::string_view name1, std::string_view name2);
|
|
|
|
/* write collected output to *p_sbuf */
|
|
void flush2sbuf(std::streambuf * p_sbuf);
|
|
|
|
/* discard output, reset write pointer to beginning of buffer */
|
|
void reset_stream() {
|
|
p_sbuf_phase1_->reset_stream();
|
|
p_sbuf_phase2_->reset_stream();
|
|
}
|
|
|
|
private:
|
|
/* common implementation for .preamble(), .postamble() */
|
|
void entryexit_aux(std::string_view name1,
|
|
std::string_view name2,
|
|
char label_char);
|
|
|
|
private:
|
|
/* current nesting level for this thread */
|
|
uint32_t nesting_level_ = 0;
|
|
|
|
/* buffer space for logging
|
|
* (before pretty-printing for scope::log() calls that span multiple lines)
|
|
* reused across tos() and scope::log() calls
|
|
*/
|
|
std::unique_ptr<log_streambuf_type> p_sbuf_phase1_;
|
|
|
|
/* buffer space for handling scope::log() calls that span multiple lines;
|
|
* inserts extra characters in effort to indent gracefully
|
|
*/
|
|
std::unique_ptr<log_streambuf_type> p_sbuf_phase2_;
|
|
|
|
/* output stream -- always attached to .p_sbuf_phase1
|
|
* stream inserters for application datatypes will target this stream
|
|
*/
|
|
std::ostream ss_;
|
|
}; /*state_impl*/
|
|
|
|
constexpr uint32_t c_default_buf_size = 1024;
|
|
|
|
template <typename CharT, typename Traits>
|
|
state_impl<CharT, Traits>::state_impl()
|
|
: p_sbuf_phase1_(new log_streambuf_type(c_default_buf_size)),
|
|
p_sbuf_phase2_(new log_streambuf_type(c_default_buf_size)),
|
|
ss_(p_sbuf_phase1_.get())
|
|
{
|
|
assert(p_sbuf_phase1_.get() == ss_.rdbuf());
|
|
} /*ctor*/
|
|
|
|
template <typename CharT, typename Traits>
|
|
void
|
|
state_impl<CharT, Traits>::indent(char pad_char)
|
|
{
|
|
//log_streambuf * sbuf = this->p_sbuf_phase1_.get();
|
|
|
|
#ifdef NOT_IN_USE
|
|
{
|
|
char buf[80];
|
|
::snprintf(buf, sizeof(buf), "[%02d] ", this->nesting_level_);
|
|
|
|
this->ss_ << buf;
|
|
//this->p_sbuf_->sputn(buf, strlen(buf));
|
|
}
|
|
#endif
|
|
|
|
/* indent to nesting level */
|
|
for(uint32_t i = 0, n = this->nesting_level_; i<n; ++i) {
|
|
this->ss_ << pad_char;
|
|
}
|
|
} /*indent*/
|
|
|
|
template <typename CharT, typename Traits>
|
|
void
|
|
state_impl<CharT, Traits>::entryexit_aux(std::string_view name1,
|
|
std::string_view name2,
|
|
char label_char)
|
|
{
|
|
log_streambuf_type * sbuf = this->p_sbuf_phase1_.get();
|
|
|
|
sbuf->reset_stream();
|
|
this->indent(' ');
|
|
|
|
/* mnemonic for scope entry/exit */
|
|
this->ss_ << label_char;
|
|
|
|
/* scope name */
|
|
this->ss_ << name1 << name2 << "\n";
|
|
} /*entryexit_aux*/
|
|
|
|
template <typename CharT, typename Traits>
|
|
void
|
|
state_impl<CharT, Traits>::preamble(std::string_view name1,
|
|
std::string_view name2)
|
|
{
|
|
this->entryexit_aux(name1, name2, '+' /*label_char*/);
|
|
} /*preamble*/
|
|
|
|
template <typename CharT, typename Traits>
|
|
void
|
|
state_impl<CharT, Traits>::postamble(std::string_view name1,
|
|
std::string_view name2)
|
|
{
|
|
this->entryexit_aux(name1, name2, '-' /*label_char*/);
|
|
} /*postamble*/
|
|
|
|
template <typename CharT, typename Traits>
|
|
void
|
|
state_impl<CharT, Traits>::flush2sbuf(std::streambuf * p_sbuf)
|
|
{
|
|
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.
|
|
* 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
|
|
*/
|
|
char const * s = sbuf1->lo();
|
|
char const * e = s + sbuf1->pos();
|
|
|
|
char const * p = s;
|
|
|
|
/* point to first space following a non-space character.
|
|
* will indent to just after this space
|
|
*/
|
|
char const * space_after_nonspace = nullptr;
|
|
|
|
while(true) {
|
|
bool have_nonspace = false;
|
|
|
|
/* invariant: s<=p<=e */
|
|
|
|
/* for indenting, looking for first 'space following non-space, on first line', if any */
|
|
|
|
while(p < e) {
|
|
if(space_after_nonspace) {
|
|
;
|
|
} else {
|
|
if(*p != ' ')
|
|
have_nonspace = true;
|
|
|
|
if(have_nonspace && (*p == ' ')) {
|
|
space_after_nonspace = p;
|
|
}
|
|
}
|
|
|
|
if(*p == '\n') {
|
|
++p;
|
|
break;
|
|
} else {
|
|
++p;
|
|
}
|
|
}
|
|
|
|
/* p=e or *p=\n */
|
|
|
|
/* charseq [s,p) does not contain any newlines, print it */
|
|
sbuf2->sputn(s, p - s);
|
|
|
|
if(p == e) {
|
|
break;
|
|
}
|
|
|
|
// {
|
|
// char buf[80];
|
|
// snprintf(buf, sizeof(buf), "*** indent=[%d] next=[%c]", this->nesting_level_, *(p+1));
|
|
//
|
|
// std::clog.rdbuf()->sputn(buf, strlen(buf));
|
|
//}
|
|
|
|
/* at least 1 char following newline, need to indent for it
|
|
* - minimum indent = nesting level;
|
|
* - however if space_after_nonspace defined, indent to that
|
|
*/
|
|
uint32_t n_indent = this->nesting_level_;
|
|
|
|
if(space_after_nonspace)
|
|
n_indent += (space_after_nonspace - s);
|
|
|
|
for(uint32_t i = 0; i < n_indent; ++i)
|
|
sbuf2->sputc(' ');
|
|
|
|
s = p;
|
|
}
|
|
|
|
/* now write entire contents of *sbuf2 to clog */
|
|
p_sbuf->sputn(sbuf2->lo(), sbuf2->pos());
|
|
|
|
/* reset streams for next message */
|
|
this->reset_stream();
|
|
} /*flush2sbuf*/
|
|
} /*namespace xo*/
|
|
|
|
/* end log_state.hpp */
|