add colored console log
This commit is contained in:
parent
c19b5e224f
commit
52f46b5c51
|
|
@ -27,6 +27,9 @@
|
|||
namespace kiwano
|
||||
{
|
||||
|
||||
//
|
||||
// LogFormater
|
||||
//
|
||||
String LogFormater::GetLevelLabel(LogLevel level) const
|
||||
{
|
||||
switch (level)
|
||||
|
|
@ -68,8 +71,107 @@ public:
|
|||
};
|
||||
|
||||
|
||||
//
|
||||
// LogProvider
|
||||
//
|
||||
LogProvider::~LogProvider() {}
|
||||
|
||||
#if defined(KGE_PLATFORM_WINDOWS)
|
||||
void SetWindowConsoleColor(std::ostream& os, int foreground, int background)
|
||||
{
|
||||
static WORD defaultAttributes = 0;
|
||||
|
||||
// get terminal handle
|
||||
HANDLE hTerminal = INVALID_HANDLE_VALUE;
|
||||
if (&os == &std::cout)
|
||||
hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
else if (&os == &std::cerr)
|
||||
hTerminal = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
if (hTerminal == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
// save default terminal attributes if it unsaved
|
||||
if (!defaultAttributes)
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
|
||||
return;
|
||||
defaultAttributes = info.wAttributes;
|
||||
}
|
||||
|
||||
// restore all default settings
|
||||
if (foreground == -1 && background == -1)
|
||||
{
|
||||
SetConsoleTextAttribute(hTerminal, defaultAttributes);
|
||||
return;
|
||||
}
|
||||
|
||||
// get current settings
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
|
||||
return;
|
||||
|
||||
if (foreground != -1)
|
||||
{
|
||||
info.wAttributes &= ~(info.wAttributes & 0x0F);
|
||||
info.wAttributes |= static_cast<WORD>(foreground);
|
||||
}
|
||||
|
||||
if (background != -1)
|
||||
{
|
||||
info.wAttributes &= ~(info.wAttributes & 0xF0);
|
||||
info.wAttributes |= static_cast<WORD>(background);
|
||||
}
|
||||
|
||||
SetConsoleTextAttribute(hTerminal, info.wAttributes);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <int color>
|
||||
std::ostream& ConsoleColorBrush(std::ostream& os)
|
||||
{
|
||||
#if defined(KGE_PLATFORM_WINDOWS)
|
||||
if (color > 0)
|
||||
{
|
||||
switch (color)
|
||||
{
|
||||
case 31: // red
|
||||
SetWindowConsoleColor(os, FOREGROUND_RED | FOREGROUND_INTENSITY, -1);
|
||||
break;
|
||||
case 32: // green
|
||||
SetWindowConsoleColor(os, FOREGROUND_GREEN | FOREGROUND_INTENSITY, -1);
|
||||
break;
|
||||
case 33: // yellow
|
||||
SetWindowConsoleColor(os, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, -1);
|
||||
break;
|
||||
case 34: // blue
|
||||
SetWindowConsoleColor(os, FOREGROUND_BLUE | FOREGROUND_INTENSITY, -1);
|
||||
break;
|
||||
case 36: // cyan
|
||||
SetWindowConsoleColor(os, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY, -1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (color < 0)
|
||||
{
|
||||
SetWindowConsoleColor(os, -1, -1);
|
||||
}
|
||||
#else
|
||||
if (color > 0)
|
||||
{
|
||||
os << "\033[1;" << color << "m";
|
||||
}
|
||||
else if (color < 0)
|
||||
{
|
||||
os << "\033[0m";
|
||||
}
|
||||
#endif
|
||||
return os;
|
||||
}
|
||||
|
||||
LogProviderPtr ConsoleLogProvider::Create()
|
||||
{
|
||||
LogProviderPtr ptr = new ConsoleLogProvider;
|
||||
|
|
@ -88,9 +190,9 @@ void ConsoleLogProvider::Init()
|
|||
void ConsoleLogProvider::WriteMessage(LogLevel level, LogBuffer* msg)
|
||||
{
|
||||
if (level != LogLevel::Error)
|
||||
std::cout << msg << std::flush;
|
||||
std::cout << GetColor(level) << msg << std::flush << ConsoleColorBrush<-1>;
|
||||
else
|
||||
std::cerr << msg;
|
||||
std::cerr << GetColor(level) << msg << ConsoleColorBrush<-1>;
|
||||
|
||||
#if defined(KGE_PLATFORM_WINDOWS)
|
||||
::OutputDebugStringA(msg->GetRaw());
|
||||
|
|
@ -100,6 +202,22 @@ void ConsoleLogProvider::WriteMessage(LogLevel level, LogBuffer* msg)
|
|||
void ConsoleLogProvider::Flush()
|
||||
{
|
||||
std::cout.flush();
|
||||
std::cout.clear();
|
||||
}
|
||||
|
||||
ConsoleLogProvider::ConsoleColor ConsoleLogProvider::GetColor(LogLevel level)
|
||||
{
|
||||
std::initializer_list<ConsoleColor> colors = {
|
||||
ConsoleColorBrush<34>, // Debug Blue
|
||||
ConsoleColorBrush<0>, // Info Default
|
||||
ConsoleColorBrush<32>, // Notice Green
|
||||
ConsoleColorBrush<33>, // Warn Yellow
|
||||
ConsoleColorBrush<31>, // Error Red
|
||||
};
|
||||
|
||||
if (size_t(level) < colors.size())
|
||||
return *std::next(colors.begin(), ptrdiff_t(level));
|
||||
return ConsoleColorBrush<0>;
|
||||
}
|
||||
|
||||
LogProviderPtr FileLogProvider::Create(const String& filepath, std::ios_base::openmode mode)
|
||||
|
|
@ -123,22 +241,242 @@ void FileLogProvider::Init() {}
|
|||
void FileLogProvider::WriteMessage(LogLevel level, LogBuffer* msg)
|
||||
{
|
||||
if (ofs_)
|
||||
{
|
||||
ofs_ << msg << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
void FileLogProvider::Flush()
|
||||
{
|
||||
if (ofs_)
|
||||
{
|
||||
ofs_.flush();
|
||||
ofs_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// LogBuffer
|
||||
//
|
||||
LogBuffer::LogBuffer(size_t buffer_size)
|
||||
: buf_(buffer_size)
|
||||
, seek_high_(nullptr)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void LogBuffer::Resize(size_t size)
|
||||
{
|
||||
if (buf_.size() < size)
|
||||
{
|
||||
buf_.resize(size);
|
||||
}
|
||||
}
|
||||
|
||||
void LogBuffer::Reset()
|
||||
{
|
||||
const auto begin = buf_.data();
|
||||
const auto size = buf_.size();
|
||||
this->setp(begin, begin + size);
|
||||
this->setg(begin, begin, begin);
|
||||
seek_high_ = nullptr;
|
||||
}
|
||||
|
||||
const char* LogBuffer::GetRaw() const
|
||||
{
|
||||
const auto pptr = this->pptr();
|
||||
if (!pptr)
|
||||
return "";
|
||||
|
||||
const auto data = buf_.data();
|
||||
const auto size = buf_.size();
|
||||
if (pptr == data)
|
||||
return "";
|
||||
|
||||
if (pptr > data && pptr < data + size)
|
||||
{
|
||||
*pptr = '\0';
|
||||
return data;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
LogBuffer::int_type LogBuffer::overflow(int_type ch)
|
||||
{
|
||||
if (traits_type::eq_int_type(ch, traits_type::eof()))
|
||||
return traits_type::not_eof(ch); // EOF, return success
|
||||
|
||||
const auto pptr = this->pptr();
|
||||
if (pptr)
|
||||
return traits_type::eof();
|
||||
|
||||
const auto epptr = this->epptr();
|
||||
if (pptr < epptr)
|
||||
{
|
||||
seek_high_ = pptr + 1;
|
||||
return ch;
|
||||
}
|
||||
|
||||
const auto old_ptr = buf_.data();
|
||||
const auto old_size = pptr - old_ptr;
|
||||
|
||||
size_t new_size = 0;
|
||||
if (old_size < INT_MAX / 2)
|
||||
new_size = old_size << 1;
|
||||
else if (old_size < INT_MAX)
|
||||
new_size = INT_MAX;
|
||||
else
|
||||
return traits_type::eof(); // buffer can't grow, fail
|
||||
|
||||
// grow
|
||||
buf_.resize(new_size);
|
||||
|
||||
const auto new_ptr = buf_.data();
|
||||
const auto new_pnext = new_ptr + old_size;
|
||||
|
||||
seek_high_ = new_pnext + 1;
|
||||
|
||||
this->setp(new_ptr, new_pnext, new_ptr + new_size);
|
||||
this->setg(new_ptr, new_ptr + (this->gptr() - old_ptr), seek_high_);
|
||||
return ch;
|
||||
}
|
||||
|
||||
LogBuffer::int_type LogBuffer::underflow()
|
||||
{
|
||||
const auto gptr = this->gptr();
|
||||
if (!gptr)
|
||||
return traits_type::eof();
|
||||
|
||||
if (gptr < this->egptr())
|
||||
return traits_type::to_int_type(*gptr);
|
||||
|
||||
const auto pptr = this->pptr();
|
||||
if (!pptr)
|
||||
return traits_type::eof();
|
||||
|
||||
const auto high = std::max(seek_high_, pptr);
|
||||
if (high <= gptr)
|
||||
return traits_type::eof();
|
||||
|
||||
seek_high_ = high;
|
||||
this->setg(this->eback(), gptr, high);
|
||||
return traits_type::to_int_type(*gptr);
|
||||
}
|
||||
|
||||
LogBuffer::pos_type LogBuffer::seekpos(pos_type pos, std::ios_base::openmode mode)
|
||||
{
|
||||
const auto offset = static_cast<std::streamoff>(pos);
|
||||
const auto old_gptr = this->gptr();
|
||||
const auto olg_pptr = this->pptr();
|
||||
if (olg_pptr && seek_high_ < olg_pptr)
|
||||
{
|
||||
seek_high_ = olg_pptr;
|
||||
}
|
||||
|
||||
const auto seek_low = this->eback();
|
||||
const auto seek_dist = seek_high_ - seek_low;
|
||||
if (static_cast<unsigned long long>(offset) > static_cast<unsigned long long>(seek_dist))
|
||||
{
|
||||
return pos_type(off_type(-1));
|
||||
}
|
||||
|
||||
if (offset != 0 && (((mode & std::ios_base::in) && !old_gptr) || ((mode & std::ios_base::out) && !olg_pptr)))
|
||||
{
|
||||
return pos_type(off_type(-1));
|
||||
}
|
||||
|
||||
const auto new_ptr = seek_low + offset;
|
||||
if ((mode & std::ios_base::in) && old_gptr)
|
||||
{
|
||||
this->setg(seek_low, new_ptr, seek_high_);
|
||||
}
|
||||
|
||||
if ((mode & std::ios_base::out) && olg_pptr)
|
||||
{
|
||||
this->setp(seek_low, new_ptr, this->epptr());
|
||||
}
|
||||
return pos_type(offset);
|
||||
}
|
||||
|
||||
LogBuffer::pos_type LogBuffer::seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode)
|
||||
{
|
||||
const auto old_gptr = this->gptr();
|
||||
const auto old_pptr = this->pptr();
|
||||
if (old_pptr && seek_high_ < old_pptr)
|
||||
{
|
||||
seek_high_ = old_pptr;
|
||||
}
|
||||
|
||||
const auto seek_low = this->eback();
|
||||
const auto seek_dist = seek_high_ - seek_low;
|
||||
off_type new_offset;
|
||||
switch (way)
|
||||
{
|
||||
case std::ios_base::beg:
|
||||
new_offset = 0;
|
||||
break;
|
||||
case std::ios_base::end:
|
||||
new_offset = seek_dist;
|
||||
break;
|
||||
case std::ios_base::cur:
|
||||
{
|
||||
constexpr auto both = std::ios_base::in | std::ios_base::out;
|
||||
if ((mode & both) != both)
|
||||
{
|
||||
if (mode & std::ios_base::in)
|
||||
{
|
||||
if (old_gptr || !seek_low)
|
||||
{
|
||||
new_offset = old_gptr - seek_low;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((mode & std::ios_base::out) && (old_pptr || !seek_low))
|
||||
{
|
||||
new_offset = old_pptr - seek_low;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fallthrough
|
||||
default:
|
||||
return pos_type(off_type(-1));
|
||||
}
|
||||
|
||||
if (std::streamsize(offset) + new_offset > std::streamsize(seek_dist))
|
||||
{
|
||||
return pos_type(off_type(-1));
|
||||
}
|
||||
|
||||
offset += new_offset;
|
||||
if (offset != 0 && (((mode & std::ios_base::in) && !old_gptr) || ((mode & std::ios_base::out) && !old_pptr)))
|
||||
{
|
||||
return pos_type(off_type(-1));
|
||||
}
|
||||
|
||||
const auto new_ptr = seek_low + offset;
|
||||
if ((mode & std::ios_base::in) && old_gptr)
|
||||
{
|
||||
this->setg(seek_low, new_ptr, seek_high_);
|
||||
}
|
||||
|
||||
if ((mode & std::ios_base::out) && old_pptr)
|
||||
{
|
||||
this->setp(seek_low, new_ptr, this->epptr());
|
||||
}
|
||||
return pos_type(offset);
|
||||
}
|
||||
|
||||
//
|
||||
// Logger
|
||||
//
|
||||
Logger::Logger()
|
||||
: enabled_(true)
|
||||
, level_(LogLevel::Debug)
|
||||
, buffer_(1024)
|
||||
{
|
||||
ResizeBuffer(1024);
|
||||
|
||||
LogFormaterPtr formater = new TextFormater;
|
||||
SetFormater(formater);
|
||||
|
||||
|
|
@ -203,16 +541,18 @@ LogFormaterPtr Logger::GetFormater()
|
|||
|
||||
void Logger::ResizeBuffer(size_t buffer_size)
|
||||
{
|
||||
buffer_.resize(buffer_size);
|
||||
buffer_.Resize(buffer_size);
|
||||
}
|
||||
|
||||
void Logger::Write(LogLevel level, std::streambuf* raw_msg)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
LogBuffer buf(buffer_);
|
||||
std::iostream stream(&buf);
|
||||
// reset buffer
|
||||
buffer_.Reset();
|
||||
|
||||
// format message
|
||||
std::iostream stream(&buffer_);
|
||||
if (formater_)
|
||||
{
|
||||
formater_->Format(stream, level, Time::Now(), raw_msg);
|
||||
|
|
@ -222,10 +562,11 @@ void Logger::Write(LogLevel level, std::streambuf* raw_msg)
|
|||
stream << raw_msg << "\n";
|
||||
}
|
||||
|
||||
// write message
|
||||
for (auto provider : providers_)
|
||||
{
|
||||
buf.pubseekpos(0, std::ios_base::out);
|
||||
provider->WriteMessage(level, &buf);
|
||||
buffer_.pubseekpos(0, std::ios_base::in);
|
||||
provider->WriteMessage(level, &buffer_);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -333,8 +674,11 @@ HWND GetAllocatedConsole()
|
|||
}
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
||||
void Logger::ShowConsole(bool show)
|
||||
{
|
||||
#if defined(KGE_PLATFORM_WINDOWS)
|
||||
HWND current_console = ::GetConsoleWindow();
|
||||
if (show)
|
||||
{
|
||||
|
|
@ -372,119 +716,10 @@ void Logger::ShowConsole(bool show)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
// NOT IMPLEMENT
|
||||
#endif
|
||||
|
||||
LogBuffer::LogBuffer(Vector<char_type>& buf)
|
||||
: buf_(buf)
|
||||
, seek_high_(nullptr)
|
||||
{
|
||||
this->setbuf(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
const char* LogBuffer::GetRaw() const
|
||||
{
|
||||
const auto pptr = this->pptr();
|
||||
if (!pptr)
|
||||
return "";
|
||||
|
||||
const auto data = buf_.data();
|
||||
const auto size = buf_.size();
|
||||
if (pptr == data)
|
||||
return "";
|
||||
|
||||
if (pptr > data && pptr < data + size)
|
||||
{
|
||||
*pptr = '\0';
|
||||
return data;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
LogBuffer::int_type LogBuffer::overflow(int_type ch)
|
||||
{
|
||||
if (traits_type::eq_int_type(ch, traits_type::eof()))
|
||||
return traits_type::not_eof(ch); // EOF, return success
|
||||
|
||||
const auto pptr = this->pptr();
|
||||
if (pptr)
|
||||
return traits_type::eof();
|
||||
|
||||
const auto epptr = this->epptr();
|
||||
if (pptr < epptr)
|
||||
{
|
||||
seek_high_ = pptr + 1;
|
||||
return ch;
|
||||
}
|
||||
|
||||
const auto old_ptr = buf_.data();
|
||||
const auto old_size = pptr - old_ptr;
|
||||
|
||||
size_t new_size = 0;
|
||||
if (old_size < INT_MAX / 2)
|
||||
new_size = old_size << 1;
|
||||
else if (old_size < INT_MAX)
|
||||
new_size = INT_MAX;
|
||||
else
|
||||
return traits_type::eof(); // buffer can't grow, fail
|
||||
|
||||
// grow
|
||||
buf_.resize(new_size);
|
||||
|
||||
const auto new_ptr = buf_.data();
|
||||
const auto new_pnext = new_ptr + old_size;
|
||||
|
||||
seek_high_ = new_pnext + 1;
|
||||
|
||||
this->setp(new_ptr, new_pnext, new_ptr + new_size);
|
||||
this->setg(new_ptr, new_ptr + (this->gptr() - old_ptr), seek_high_);
|
||||
return ch;
|
||||
}
|
||||
|
||||
LogBuffer::int_type LogBuffer::underflow()
|
||||
{
|
||||
const auto gptr = this->gptr();
|
||||
if (!gptr)
|
||||
return traits_type::eof();
|
||||
|
||||
if (gptr < this->egptr())
|
||||
return traits_type::to_int_type(*gptr);
|
||||
|
||||
const auto pptr = this->pptr();
|
||||
if (!pptr)
|
||||
return traits_type::eof();
|
||||
|
||||
const auto high = std::max(seek_high_, pptr);
|
||||
if (high <= gptr)
|
||||
return traits_type::eof();
|
||||
|
||||
seek_high_ = high;
|
||||
this->setg(this->eback(), gptr, high);
|
||||
return traits_type::to_int_type(*gptr);
|
||||
}
|
||||
|
||||
LogBuffer::pos_type LogBuffer::seekpos(pos_type sp, std::ios_base::openmode which)
|
||||
{
|
||||
return this->seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
|
||||
}
|
||||
|
||||
LogBuffer::pos_type LogBuffer::seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which)
|
||||
{
|
||||
if (dir == std::ios_base::cur)
|
||||
gbump(int(off));
|
||||
else if (dir == std::ios_base::end)
|
||||
setg(eback(), egptr() + off, egptr());
|
||||
else if (dir == std::ios_base::beg)
|
||||
setg(eback(), eback() + off, egptr());
|
||||
return gptr() - eback();
|
||||
}
|
||||
|
||||
std::streambuf* LogBuffer::setbuf(char_type* s, std::streamsize n)
|
||||
{
|
||||
this->setp(s, s + n);
|
||||
this->setg(s, s, s);
|
||||
return this;
|
||||
}
|
||||
|
||||
} // namespace kiwano
|
||||
|
|
|
|||
|
|
@ -113,10 +113,18 @@ public:
|
|||
class LogBuffer : public std::streambuf
|
||||
{
|
||||
public:
|
||||
LogBuffer(Vector<char_type>& buf);
|
||||
LogBuffer(size_t buffer_size);
|
||||
|
||||
void Resize(size_t size);
|
||||
|
||||
void Reset();
|
||||
|
||||
const char* GetRaw() const;
|
||||
|
||||
LogBuffer(const LogBuffer&) = delete;
|
||||
|
||||
LogBuffer& operator=(const LogBuffer&) = delete;
|
||||
|
||||
protected:
|
||||
int_type overflow(int_type ch) override;
|
||||
|
||||
|
|
@ -127,11 +135,9 @@ protected:
|
|||
pos_type seekoff(off_type off, std::ios_base::seekdir dir,
|
||||
std::ios_base::openmode which = std::ios_base::in) override;
|
||||
|
||||
std::streambuf* setbuf(char_type* s, std::streamsize n) override;
|
||||
|
||||
private:
|
||||
Vector<char_type>& buf_;
|
||||
char_type* seek_high_;
|
||||
Vector<char_type> buf_;
|
||||
char_type* seek_high_;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -166,6 +172,11 @@ public:
|
|||
void WriteMessage(LogLevel level, LogBuffer* msg) override;
|
||||
|
||||
void Flush() override;
|
||||
|
||||
private:
|
||||
typedef std::ostream&(*ConsoleColor)(std::ostream&);
|
||||
|
||||
ConsoleColor GetColor(LogLevel level);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -253,11 +264,9 @@ public:
|
|||
/// @param raw_msg 日志内容
|
||||
void Write(LogLevel level, std::streambuf* raw_msg);
|
||||
|
||||
#if defined(KGE_PLATFORM_WINDOWS)
|
||||
/// \~chinese
|
||||
/// @brief 显示或关闭控制台
|
||||
void ShowConsole(bool show);
|
||||
#endif
|
||||
|
||||
virtual ~Logger();
|
||||
|
||||
|
|
@ -268,8 +277,8 @@ private:
|
|||
bool enabled_;
|
||||
LogLevel level_;
|
||||
LogFormaterPtr formater_;
|
||||
LogBuffer buffer_;
|
||||
Vector<LogProviderPtr> providers_;
|
||||
Vector<char> buffer_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue