145 lines
3.4 KiB
C++
145 lines
3.4 KiB
C++
|
//Logger
|
||
|
|
||
|
#include <time.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <assert.h>
|
||
|
#include <stdio.h>
|
||
|
#include <errno.h>
|
||
|
#include <string.h>
|
||
|
#include "Logger.hh"
|
||
|
|
||
|
__thread char t_time[64];
|
||
|
__thread time_t t_lastSecond;
|
||
|
__thread char t_errnobuf[512];
|
||
|
|
||
|
const char* strerror_tl(int savedErrno)
|
||
|
{
|
||
|
return strerror_r(savedErrno, t_errnobuf, sizeof(t_errnobuf));
|
||
|
}
|
||
|
|
||
|
Logger::LogLevel g_logLevel = Logger::INFO;
|
||
|
|
||
|
void Logger::setLogLevel(LogLevel level){
|
||
|
g_logLevel = level;
|
||
|
}
|
||
|
|
||
|
Logger::LogLevel Logger::logLevel(){
|
||
|
return g_logLevel;
|
||
|
}
|
||
|
|
||
|
const char* LogLevelName[Logger::NUM_LOG_LEVELS] =
|
||
|
{
|
||
|
"[TRACE]",
|
||
|
"[DEBUG]",
|
||
|
"[INFO ]",
|
||
|
"[WARN ]",
|
||
|
"[ERROR]",
|
||
|
"[FATAL]",
|
||
|
};
|
||
|
|
||
|
// helper class for known string length at compile time
|
||
|
class T
|
||
|
{
|
||
|
public:
|
||
|
T(const char* str, unsigned len)
|
||
|
:m_str(str),
|
||
|
m_len(len)
|
||
|
{
|
||
|
assert(strlen(str) == m_len);
|
||
|
}
|
||
|
|
||
|
const char* m_str;
|
||
|
const unsigned m_len;
|
||
|
};
|
||
|
|
||
|
void defaultOutput(const char *msg, int len){
|
||
|
size_t n = fwrite(msg, 1, len, stdout);
|
||
|
(void)n;
|
||
|
}
|
||
|
|
||
|
void defaultFlush(){
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
|
||
|
Logger::outputFunc g_output = defaultOutput;
|
||
|
Logger::flushFunc g_flush = defaultFlush;
|
||
|
|
||
|
void Logger::setOutput(outputFunc out){
|
||
|
g_output = out;
|
||
|
}
|
||
|
|
||
|
void Logger::setFlush(flushFunc flush){
|
||
|
g_flush = flush;
|
||
|
}
|
||
|
|
||
|
Logger::Logger(SourceFile file, int line)
|
||
|
: m_impl(INFO, 0, file, line){
|
||
|
}
|
||
|
|
||
|
Logger::Logger(SourceFile file, int line, LogLevel level)
|
||
|
: m_impl(level, 0, file, line){
|
||
|
}
|
||
|
|
||
|
Logger::Logger(SourceFile file, int line, bool toAbort)
|
||
|
: m_impl(toAbort? FATAL:ERROR, errno, file, line){
|
||
|
}
|
||
|
|
||
|
Logger::Logger(SourceFile file, int line, LogLevel level, const char* func)
|
||
|
: m_impl(level, 0, file, line){
|
||
|
m_impl.m_stream << '[' << func << "] ";
|
||
|
}
|
||
|
|
||
|
Logger::~Logger(){
|
||
|
m_impl.finish();
|
||
|
const LogStream::Buffer& buf(stream().buffer());
|
||
|
g_output(buf.data(), buf.length());
|
||
|
if (m_impl.m_level == FATAL)
|
||
|
{
|
||
|
g_flush();
|
||
|
abort();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)
|
||
|
: m_time(TimeStamp::now()),
|
||
|
m_stream(),
|
||
|
m_level(level),
|
||
|
m_fileBaseName(file),
|
||
|
m_line(line)
|
||
|
{
|
||
|
formatTime();
|
||
|
m_stream << LogLevelName[level] << ' ';
|
||
|
m_stream << '[' << m_fileBaseName.m_data << ':' << m_line << "] ";
|
||
|
if (savedErrno != 0)
|
||
|
{
|
||
|
m_stream << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Logger::Impl::finish()
|
||
|
{
|
||
|
m_stream<< '\n';
|
||
|
}
|
||
|
|
||
|
void Logger::Impl::formatTime()
|
||
|
{
|
||
|
int64_t microSecondsSinceEpoch = m_time.microSecondsSinceEpoch();
|
||
|
time_t seconds = static_cast<time_t>(microSecondsSinceEpoch / TimeStamp::kMicroSecondsPerSecond);
|
||
|
int microseconds = static_cast<int>(microSecondsSinceEpoch % TimeStamp::kMicroSecondsPerSecond);
|
||
|
if (seconds != t_lastSecond){
|
||
|
t_lastSecond = seconds;
|
||
|
struct tm tm_time;
|
||
|
|
||
|
::gmtime_r(&seconds, &tm_time); // FIXME TimeZone::fromUtcTime
|
||
|
|
||
|
int len = snprintf(t_time, sizeof(t_time), "%4d-%02d-%02d %02d:%02d:%02d",
|
||
|
tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
|
||
|
tm_time.tm_hour + 8, tm_time.tm_min, tm_time.tm_sec);
|
||
|
assert(len == 19); (void)len;
|
||
|
}
|
||
|
|
||
|
Fmt us(".%06d ", microseconds);
|
||
|
assert(us.length() == 8);
|
||
|
m_stream << t_time << us.data();
|
||
|
}
|