本文重点在Linux操作系统上实现日志系统,用于记录部分业务或者重要参数便于以后追查问题。
logging.h
#pragma once
#include <string.h>
#include "timestamp.h"
#include "logstream.h"
class Logger
{
public:
enum LogLevel
{
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL,
NUM_LOG_LEVELS,
};
// compile time calculation of basename of source file
class SourceFile
{
public:
template<int N>
inline SourceFile(const char(&arr)[N])
: data_(arr),
size_(N - 1)
{
const char* slash = strrchr(data_, '/'); // builtin function
if (slash)
{
data_ = slash + 1;
size_ -= static_cast<int>(data_ - arr);
}
}
explicit SourceFile(const char* filename)
: data_(filename)
{
const char* slash = strrchr(filename, '/');
if (slash)
{
data_ = slash + 1;
}
size_ = static_cast<int>(strlen(data_));
}
const char* data_;
int size_;
};
Logger(SourceFile file, int line);
Logger(SourceFile file, int line, LogLevel level);
Logger(SourceFile file, int line, LogLevel level, const char* func);
Logger(SourceFile file, int line, bool toAbort);
~Logger();
LogStream& stream() { return impl_.stream_; }
static LogLevel logLevel();
static void setLogLevel(LogLevel level);
typedef void(*OutputFunc)(const char* msg, int len);
typedef void(*FlushFunc)();
static void setOutput(OutputFunc);
static void setFlush(FlushFunc);
void WriteLog(unsigned char* buffer, size_t size);
private:
class Impl
{
public:
typedef Logger::LogLevel LogLevel;
Impl(LogLevel level, int old_errno, const SourceFile& file, int line);
void formatTime();
void finish();
Timestamp time_;
LogStream stream_;
LogLevel level_;
int line_;
SourceFile basename_;
};
Impl impl_;
};
extern Logger::LogLevel g_logLevel;
inline Logger::LogLevel Logger::logLevel()
{
return g_logLevel;
}
#undef LOG_TRACE
#undef LOG_DEBUG
#undef LOG_INFO
#undef LOG_WARN
#undef LOG_ERROR
#undef LOG_FATAL
#undef LOG_SYSERR
#define LOG_TRACE if (Logger::logLevel() <= Logger::TRACE) \
Logger(__FILE__, __LINE__, Logger::TRACE, __func__).stream()
#define LOG_DEBUG if (Logger::logLevel() <= Logger::DEBUG) \
Logger(__FILE__, __LINE__, Logger::DEBUG, __func__).stream()
#define LOG_INFO if (Logger::logLevel() <= Logger::INFO) \
Logger(__FILE__, __LINE__).stream()
#define LOG_WARN Logger(__FILE__, __LINE__, Logger::WARN).stream()
#define LOG_ERROR Logger(__FILE__, __LINE__, Logger::ERROR).stream()
#define LOG_FATAL Logger(__FILE__, __LINE__, Logger::FATAL).stream()
#define LOG_SYSERR Logger(__FILE__, __LINE__, false).stream()
#define LOG_SYSFATAL Logger(__FILE__, __LINE__, true).stream()
#define LOG_DEBUG_BIN(x,l) if (Logger::logLevel() <= Logger::DEBUG) \
Logger(__FILE__, __LINE__, Logger::DEBUG, __func__).WriteLog((x), (l))
const char* strerror_tl(int savedErrno);
// Taken from glog/logging.h
//
// Check that the input is non NULL. This very useful in constructor
// initializer lists.
#define CHECK_NOTNULL(val) \
CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val))
// A small helper for CHECK_NOTNULL().
template <typename T>
T* CheckNotNull(Logger::SourceFile file, int line, const char *names, T* ptr)
{
if (ptr == NULL)
{
Logger(file, line, Logger::FATAL).stream() << names;
}
return ptr;
}
logging.cpp
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sstream>
#include <thread>
#include "logging.h"
__thread char t_errnobuf[512];
__thread char t_time[32];
__thread time_t t_lastSecond;
const char* strerror_tl(int savedErrno)
{
return strerror_r(savedErrno, t_errnobuf, sizeof t_errnobuf);
}
Logger::LogLevel initLogLevel()
{
//if (::getenv("MUDUO_LOG_TRACE"))
// return Logger::TRACE;
//else if (::getenv("MUDUO_LOG_DEBUG"))
// return Logger::DEBUG;
//else
return Logger::INFO;
}
Logger::LogLevel g_logLevel = initLogLevel();
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)
:str_(str),
len_(len)
{
assert(strlen(str) == len_);
}
const char* str_;
const unsigned len_;
};
inline LogStream& operator<<(LogStream& s, T v)
{
s.append(v.str_, v.len_);
return s;
}
inline LogStream& operator<<(LogStream& s, const Logger::SourceFile& v)
{
s.append(v.data_, v.size_);
return s;
}
void defaultOutput(const char* msg, int len)
{
size_t n = fwrite(msg, 1, len, stdout);
//FIXME check n
(void)n;
}
void defaultFlush()
{
fflush(stdout);
}
Logger::OutputFunc g_output = defaultOutput;
Logger::FlushFunc g_flush = defaultFlush;
Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)
: time_(Timestamp::now()),
stream_(),
level_(level),
line_(line),
basename_(file)
{
formatTime();
stream_ << T(LogLevelName[level], 6);
if (savedErrno != 0)
{
stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";
}
}
void Logger::Impl::formatTime()
{
int64_t microSecondsSinceEpoch = 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;
::localtime_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, tm_time.tm_min, tm_time.tm_sec);
assert(len == 17); (void)len;
}
stream_ << T(t_time, 17) << " ";
}
void Logger::Impl::finish()
{
stream_ << " - " << basename_ << ':' << line_ << '\n';
}
Logger::Logger(SourceFile file, int line)
: impl_(INFO, 0, file, line)
{
}
Logger::Logger(SourceFile file, int line, LogLevel level, const char* func)
: impl_(level, 0, file, line)
{
impl_.stream_ << func << ' ';
}
Logger::Logger(SourceFile file, int line, LogLevel level)
: impl_(level, 0, file, line)
{
}
Logger::Logger(SourceFile file, int line, bool toAbort)
: impl_(toAbort ? FATAL : ERROR, errno, file, line)
{
}
Logger::~Logger()
{
impl_.finish();
const LogStream::Buffer& buf(stream().buffer());
g_output(buf.data(), buf.length());
if (impl_.level_ == FATAL)
{
g_flush();
abort();
}
}
void Logger::setLogLevel(Logger::LogLevel level)
{
g_logLevel = level;
}
void Logger::setOutput(OutputFunc out)
{
g_output = out;
}
void Logger::setFlush(FlushFunc flush)
{
g_flush = flush;
}
namespace{
const char * ullto4Str(int n)
{
static char buf[64 + 1];
memset(buf, 0, sizeof(buf));
sprintf(buf, "%06u", n);
return buf;
}
char g_szchar[17] = "0123456789abcdef";
char* FormLog(int &index,char *szbuf, size_t size_buf, unsigned char* buffer, size_t size)
{
size_t len = 0;
size_t lsize = 0;
int headlen = 0;
char szhead[64 + 1];
memset(szhead, 0, sizeof(szhead));
while (size > lsize && len + 10 < size_buf)
{
if (lsize % 32 == 0)
{
if (0 != headlen)
{
szbuf[len++] = '\n';
}
memset(szhead, 0, sizeof(szhead));
strncpy(szhead, ullto4Str(index++), sizeof(szhead)-1);
headlen = strlen(szhead);
szhead[headlen++] = ' ';
strcat(szbuf, szhead);
len += headlen;
}
if (lsize % 16 == 0 && 0 != headlen)
szbuf[len++] = ' ';
szbuf[len++] = g_szchar[(buffer[lsize] >> 4) & 0xf];
szbuf[len++] = g_szchar[(buffer[lsize]) & 0xf];
lsize++;
}
szbuf[len++] = '\n';
szbuf[len++] = '\0';
return szbuf;
}
}
void Logger::WriteLog(unsigned char* buffer, size_t size)
{
static const size_t PRINTSIZE = 512;
char szbuf[PRINTSIZE * 3 + 8];
size_t lsize = 0;
size_t lprintbufsize = 0;
int index = 0;
stream() << "adress[" << (long)buffer << "] size[" << size << "] \n";
while (true)
{
memset(szbuf, 0, sizeof(szbuf));
if (size > lsize)
{
lprintbufsize = (size - lsize);
lprintbufsize = lprintbufsize > PRINTSIZE ? PRINTSIZE : lprintbufsize;
FormLog(index, szbuf, sizeof(szbuf), buffer + lsize, lprintbufsize);
size_t len = strlen(szbuf);
if (stream().buffer().avail() < static_cast<int>(len))
{
stream() << szbuf + (len - stream().buffer().avail());
break;
}
else
{
stream() << szbuf;
}
lsize += lprintbufsize;
}
else
{
break;
}
}
}