概述
仿muduo日志库:c++小巧日志库实现
- logger.h
#pragma once
#include <iostream>
#include <chrono>
#include <string.h>
#include "log_stream.h"
class Logger
{
public:
/**
* @brief 日志等级
*/
enum LogLevel
{
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL,
NUM_LOG_LEVELS,
};
//compile time calculation of basename of source file
/**
* @brief 获取日志打印所在文件
*/
class SourceFile
{
public:
template<int N>
inline SourceFile(const char(&arr)[N])
:m_data(arr),
m_size(N - 1)
{
const char* slash = strrchr(m_data, '/'); // builtin function
if (slash)
{
m_data = slash + 1;
m_size -= static_cast<int>(m_data - arr);
}
}
explicit SourceFile(const char* filename)
: m_data(filename)
{
const char* slash = strrchr(filename, '/');
if (slash)
{
m_data = slash + 1;
}
m_size = static_cast<int>(strlen(m_data));
}
const char* m_data; // 当前字符串
int m_size; // 当前字符串大小
};
/**
* @brief Logger构造
* @param p_file:所在文件
* @param p_nline:所在行
*/
Logger(SourceFile p_file, int p_nline);
/**
* @brief Logger构造
* @param p_file:所在文件
* @param p_nline:所在行
* @param p_enumLevel:日志等级
*/
Logger(SourceFile p_file, int p_nline, LogLevel p_enumLevel);
/**
* @brief Logger构造
* @param p_file:所在文件
* @param p_nline:所在行
* @param p_enumLevel:日志等级
* @param p_func: 附带字符串
*/
Logger(SourceFile p_file, int p_nline, LogLevel p_enumLevel, const char* p_func);
/**
* @brief Logger构造
* @param p_file:所在文件
* @param p_nline:所在行
* @param p_btoAbort: 布尔判断:传true:FATAL false:ERROR
*/
Logger(SourceFile p_file, int p_nline, bool p_btoAbort);
~Logger();
/**
* @brief 设置日志等级
* @param p_enumLevel:日志等级
*/
static void setLogLevel(LogLevel p_enumLevel);
/**
* @brief 获取日志等级
*/
static LogLevel logLevel();
/**
* @brief 获取输出字符串流
*/
LogStream& stream() { return m_impl.m_stream; }
typedef void (*outputFunc)(const char* msg, int len);
typedef void (*flushFunc)();
/**
* @brief 设置Output回调
*/
static void setOutput(outputFunc);
/**
* @brief 设置flushFunc回调
*/
static void setFlush(flushFunc);
private:
Logger(const Logger& lg); //no copyable
Logger& operator=(const Logger& lg);
class Impl
{
public:
typedef Logger::LogLevel LogLevel;
/**
* @brief Impl构造
* @param p_enumLevel:日志等级
* @param p_nOldErrno: 错误码
* @param p_file:所在文件
* @param p_nline:所在行
*/
Impl(LogLevel p_enumLevel, int p_nOldErrno, const SourceFile& p_file, int p_nline);
/**
* @brief 获取当前时间,精确到纳秒
*/
void formatTime();
/**
* @brief 结束符-换行
*/
void finish();
std::chrono::system_clock::time_point m_time; // 时间参数
LogStream m_stream; // 字符串流
LogLevel m_level; // 日志等级
int m_line; // 行号
SourceFile m_fileBaseName; // 文件名
};
Impl m_impl; //
};
#define log_trace if (Logger::logLevel() <= Logger::TRACE) Logger(__FILE__, __LINE__, Logger::TRACE, __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_debug Logger(__FILE__, __LINE__, Logger::DEBUG).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()
- logger.cpp
//Logger
#define _CRT_SECURE_NO_WARNINGS
#include <time.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "logger.h"
#include <ctime>
#include <sstream>
Logger::LogLevel g_logLevel = Logger::INFO;
void Logger::setLogLevel(LogLevel p_enumLevel)
{
g_logLevel = p_enumLevel;
}
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 p_file, int p_nline)
: m_impl(INFO, 0, p_file, p_nline)
{
}
Logger::Logger(SourceFile p_file, int p_nline, LogLevel p_enumLevel)
: m_impl(p_enumLevel, 0, p_file, p_nline)
{
}
Logger::Logger(SourceFile p_file, int p_nline, bool p_btoAbort)
: m_impl(p_btoAbort ? FATAL : ERROR, errno, p_file, p_nline)
{
}
Logger::Logger(SourceFile p_file, int p_nline, LogLevel p_enumLevel, const char* p_func)
: m_impl(p_enumLevel, 0, p_file, p_nline)
{
m_impl.m_stream << '[' << p_func << "] ";
}
Logger::~Logger()
{
m_impl.finish();
const LogStream::Buffer& buf(stream().buffer());
g_output(buf.data(), buf.length());
// FATAL 致命bug日志,程序崩溃
if (m_impl.m_level == FATAL)
{
g_flush();
//abort();
}
}
Logger::Impl::Impl(LogLevel p_enumLevel, int p_nOldErrno, const SourceFile& p_file, int p_nline)
: m_time(std::chrono::system_clock::now()),
m_stream(),
m_level(p_enumLevel),
m_fileBaseName(p_file),
m_line(p_nline)
{
formatTime();
m_stream << LogLevelName[p_enumLevel] << ' ';
m_stream << '[' << m_fileBaseName.m_data << ':' << m_line << "] ";
std::ostringstream oss;
oss << std::this_thread::get_id();
m_stream << '[' << oss.str().c_str() << "] ";
if (p_nOldErrno != 0)
{
m_stream /*<< strerror_tl(savedErrno)*/ << " (errno=" << p_nOldErrno << ") ";
}
}
void Logger::Impl::finish()
{
m_stream << 'n';
}
void Logger::Impl::formatTime()
{
auto tt = std::chrono::system_clock::to_time_t(m_time);
struct tm* ptm = localtime(&tt);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(m_time.time_since_epoch()) % 1000;
auto cs = std::chrono::duration_cast<std::chrono::microseconds>(m_time.time_since_epoch()) % 1000000;
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(m_time.time_since_epoch()) % 1000000000;
char date[128] = { 0 };
sprintf(date, "[%d-%02d-%02d %02d:%02d:%02d:%03ld:%03ld:%03ld] ",
(int)ptm->tm_year + 1900, (int)ptm->tm_mon + 1, (int)ptm->tm_mday,
(int)ptm->tm_hour, (int)ptm->tm_min, (int)ptm->tm_sec, ms.count(), cs.count() % 1000, ns.count() % 1000);
m_stream << date;
}
- log_stream.h
#pragma once
#include <stdio.h>
#include <string.h>
#include <thread>
const int kSmallBuffer = 4096;
const int kLargeBuffer = 4096;
template<int SIZE>
class LogBuffer
{
public:
LogBuffer() : m_cur(m_data) {}
~LogBuffer() {}
/**
* @brief 拼接字符串
* @param p_chBuf: 字符串
* @param p_nlen: 字符串长度
*/
void append(const char* p_chBuf, size_t p_nlen)
{
// append partially
if ((avail()) > p_nlen)
{
memcpy(m_cur, p_chBuf, p_nlen);
m_cur += p_nlen;
}
}
// write in m_data directly
/**
* @brief 获取当前字符串
*/
char* current() { return m_cur; };
/**
* @brief 判断当前追加字符串是否超过最大限制
*/
int avail() const { return static_cast<int> (end() - m_cur); }
/**
* @brief 字符串偏移
* @param p_nlen: 偏移量
*/
void add(size_t p_nlen) { m_cur += p_nlen; }
/**
* @brief 获取长度
*/
int length() const { return m_cur - m_data; }
/**
* @brief 获取字符串
*/
const char* data() const { return m_data; }
private:
/**
* @brief 获取字符串最后位置节点
*/
const char* end() const { return m_data + sizeof(m_data); }
char m_data[SIZE]; // 输出日志的char数组
char* m_cur; // 传入字符串
};
class LogStream
{
public:
LogStream();
~LogStream();
using Buffer = LogBuffer<kSmallBuffer>;
using self = LogStream;
/**
* @ 一系列重载
*/
self& operator<<(bool v);
self& operator<<(short);
self& operator<<(unsigned short);
self& operator<<(int);
self& operator<<(unsigned int);
self& operator<<(long);
self& operator<<(unsigned long);
self& operator<<(long long);
self& operator<<(unsigned long long);
self& operator<<(const void*);
self& operator<<(float v);
self& operator<<(double);
self& operator<<(char v);
self& operator<<(const char*);
/**
* @brief 往流缓存追加字符串
* @param p_chData:字符串
* @param p_nlen:字符串长度
*/
void append(const char* p_chData, int p_nlen) { return m_buffer.append(p_chData, p_nlen); }
/**
* @brief 获取流缓存
*/
const Buffer& buffer() const { return m_buffer; }
private:
LogStream(const LogStream& ls); //no copyable
LogStream& operator=(const LogStream& ls);
template<typename T>
void formatInteger(T v);
Buffer m_buffer; // 流缓存
static const int kMaxNumericSize = 32;
};
class Fmt
{
public:
template<typename T>
Fmt(const char* fmt, T val);
const char* data() const { return m_buf; }
int length() const { return m_length; }
private:
char m_buf[32]; // 字符串
int m_length; // 字符串长度
};
/**
* @brief 追加流(实现与cout一样输出格式的关键)
* @param p_stLog: 流
* @param p_fmt:格式化类
*/
inline LogStream& operator<<(LogStream& p_stLog, const Fmt& p_fmt)
{
p_stLog.append(p_fmt.data(), p_fmt.length());
return p_stLog;
}
- log_stream.cpp
//LogStream
#include <stdio.h>
#include <assert.h>
#include <algorithm>
#include "log_stream.h"
LogStream::LogStream() {}
LogStream::~LogStream() {}
LogStream& LogStream::operator<<(bool v)
{
m_buffer.append(v ? "1" : "0", 1);
return *this;
}
LogStream& LogStream::operator<<(short v)
{
*this << static_cast<int>(v);
return *this;
}
LogStream& LogStream::operator<<(unsigned short v)
{
*this << static_cast<unsigned int>(v);
return *this;
}
LogStream& LogStream::operator<<(int v)
{
formatInteger(v);
return *this;
}
LogStream& LogStream::operator<<(unsigned int v)
{
formatInteger(v);
return *this;
}
LogStream& LogStream::operator<<(long v)
{
formatInteger(v);
return *this;
}
LogStream& LogStream::operator<<(unsigned long v)
{
formatInteger(v);
return *this;
}
LogStream& LogStream::operator<<(long long v)
{
formatInteger(v);
return *this;
}
LogStream& LogStream::operator<<(unsigned long long v)
{
formatInteger(v);
return *this;
}
LogStream& LogStream::operator<<(const void*)
{
printf("undefinen");
return *this;
}
LogStream& LogStream::operator<<(float v)
{
*this << static_cast<double>(v);
return *this;
}
LogStream& LogStream::operator<<(double v)
{
if (m_buffer.avail() >= kMaxNumericSize)
{
int len = snprintf(m_buffer.current(), kMaxNumericSize, "%.12g", v);
m_buffer.add(len);
}
return *this;
}
LogStream& LogStream::operator<<(char v)
{
m_buffer.append(&v, 1);
return *this;
}
LogStream& LogStream::operator<<(const char* str)
{
if (str)
{
m_buffer.append(str, strlen(str));
}
else
{
m_buffer.append("(NULL)", 6);
}
return *this;
}
const char digits[] = "9876543210123456789";
const char* zero = digits + 9;
//convert to str
template<typename T>
size_t convert(char buf[], T value)
{
T i = value;
char* p = buf;
do
{
int lsd = static_cast<int>(i % 10);
i /= 10;
*p++ = zero[lsd];
} while (i != 0);
if (value < 0)
{
*p++ = '-';
}
*p = '