日志类实现
log.h
#ifndef LOG_H
#define LOG_H
#include "../buffer/buffer.h"
#include "blockqueue.hpp"
#include <assert.h>
#include <mutex>
#include <stdarg.h> //...
#include <string>
#include <sys/stat.h>
#include <sys/time.h>
#include <thread>
class Log
{
public:
void init(int level = 0,
const char* path = "./log",
const char* suffix = ".log",
int maxQueueCapacity = 1024);
static Log* Instance();
static void FlushLogThread();
void write(int level, const char* format, ...);
void flush();
int GetLevel();
void SetLevel(int level);
bool IsOpen() { return _isOpen; }
private:
Log();
void _AppendLogLevelTitle(int level);
virtual ~Log();
void _AsyncWrite();
static const int LOG_PATH_LEN = 256;
static const int LOG_NAME_LEN = 256;
static const int MAX_LINES = 50000;
const char* _path;
const char* _suffix;
int _MAX_LINES;
int _lineCount;
int _toDay;
bool _isOpen;
Buffer _buff;
int _level;
bool _isAsync;
FILE* _fp;
std::unique_ptr<BlockDeque<std::string>> _deque;
std::unique_ptr<std::thread> _writeThread;
std::mutex _mtx;
};
#define LOG_BASE(level, format, ...) \
do { \
Log* log = Log::Instance(); \
if (log->IsOpen() && log->GetLevel() <= level) { \
log->write(level, format, ##__VA_ARGS__); \
log->flush(); \
} \
} while (0);
#define LOG_DEBUG(format, ...) \
do { \
LOG_BASE(0, format, ##__VA_ARGS__) \
} while (0);
#define LOG_INFO(format, ...) \
do { \
LOG_BASE(1, format, ##__VA_ARGS__) \
} while (0);
#define LOG_WARN(format, ...) \
do { \
LOG_BASE(2, format, ##__VA_ARGS__) \
} while (0);
#define LOG_ERROR(format, ...) \
do { \
LOG_BASE(3, format, ##__VA_ARGS__) \
} while (0);
#endif //! LOG_H
log.cpp
#include "log.h"
#include <iostream>
using namespace std;
Log::Log()
{
_lineCount = 0;
_isAsync = false;
_writeThread = nullptr;
_deque = nullptr;
_toDay = 0;
_fp = nullptr;
}
Log::~Log()
{
if (_writeThread && _writeThread->joinable()) {
while (!_deque->empty()) {
_deque->flush(); //将队列中的数据 pop()
}
_deque
->Close(); //里面会唤醒阻塞队列的生产者和消费者,把剩余的数据冲刷到_deque中
_writeThread->join(); //等待线程把最后的数据写完结束
}
if (_fp) {
std::lock_guard<std::mutex> locker(_mtx);
flush();
fclose(_fp);
}
}
void
Log::init(int level, const char* path, const char* suffix, int maxQueneSize)
{
_isOpen = true;
_level = level;
if (maxQueneSize > 0) {
_isAsync = true;
if (!_deque) {
//阻塞队列
std::unique_ptr<BlockDeque<std::string>> newDeque(
new BlockDeque<std::string>);
_deque = std::move(newDeque);
std::unique_ptr<std::thread> NewThread(new std::thread(FlushLogThread));
_writeThread = std::move(NewThread);
}
} else {
_isAsync = false;
}
_lineCount = 0;
time_t timer = time(nullptr);
struct tm* systime = localtime(&timer);
struct tm t = *systime;
_path = path;
_suffix = suffix;
char fileName[LOG_NAME_LEN] = { 0 };
//将可变参数 “…” 按照format的格式格式化为字符串,然后再将其拷贝至fileName中。
snprintf(fileName,
LOG_NAME_LEN - 1,
"%s/%04d_%02d_%02d%s",
_path,
t.tm_year + 1900,
t.tm_mon + 1,
t.tm_mday,
_suffix);
_toDay = t.tm_mday;
{
std::lock_guard<std::mutex> locker(_mtx);
_buff.RetrieveAll(); // clear
if (_fp) {
//将上次的数据冲刷出缓冲区
flush();
fclose(_fp);
}
//打开新的日志文件
_fp = fopen(fileName, "a");
if (_fp == nullptr) {
mkdir(_path, 0777);
_fp = fopen(fileName, "a");
}
assert(_fp != nullptr);
}
}
void
Log::write(int level, const char* format, ...)
{
struct timeval now = { 0, 0 };
gettimeofday(&now, nullptr);
time_t tSec = now.tv_sec;
struct tm* sysTime = localtime(&tSec);
struct tm t = *sysTime;
va_list vaList;
/*日志日期 日志行数*/
if (_toDay != t.tm_mday || (_lineCount && (_lineCount % MAX_LINES == 0))) {
std::unique_lock<std::mutex> locker(_mtx);
locker.unlock();
char newFile[LOG_NAME_LEN];
char tail[36] = { 0 };
snprintf(
tail, 36, "%04d_%02d_%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
if (_toDay != t.tm_mday) {
snprintf(newFile, LOG_NAME_LEN - 72, "%s/%s%s", _path, tail, _suffix);
_toDay = t.tm_mday;
_lineCount = 0;
} else {
snprintf(newFile,
LOG_NAME_LEN - 72,
"%s/%s-%d%s",
_path,
tail,
(_lineCount / MAX_LINES),
_suffix);
}
locker.lock();
//冲刷上次的日志文件
flush();
fclose(_fp);
_fp = fopen(newFile, "a");
assert(_fp != nullptr);
}
{
std::unique_lock<std::mutex> locker(_mtx);
_lineCount++;
int n = snprintf(_buff.BeginWrite(),
128,
"%d-%02d-%02d %02d:%02d:%02d.%06ld ",
t.tm_year + 1900,
t.tm_mon + 1,
t.tm_mday,
t.tm_hour,
t.tm_min,
t.tm_sec,
now.tv_usec);
_buff.HasWritten(n);
_AppendLogLevelTitle(level);
va_start(vaList, format);
int m =
vsnprintf(_buff.BeginWrite(), _buff.WritableBytes(), format, vaList);
va_end(vaList);
_buff.HasWritten(m);
_buff.Append("\n\0", 2);
if (_isAsync && _deque && !_deque->full()) {
//不是异步,队列还没满
_deque->push_back(_buff.RetrieveAllToStr());
} else {
//直接写入数据
fputs(_buff.Peek(), _fp);
}
_buff.RetrieveAll();
}
}
int
Log::GetLevel()
{
std::lock_guard<std::mutex> locker(_mtx);
return _level;
}
void
Log::SetLevel(int level)
{
std::lock_guard<std::mutex> locker(_mtx);
_level = level;
}
void
Log::flush()
{
if (_isAsync) {
_deque->flush();
}
fflush(_fp);
}
void
Log::_AppendLogLevelTitle(int level)
{
switch (level) {
case 0:
_buff.Append("[debug]: ", 9);
break;
case 1:
_buff.Append("[info]: ", 9);
break;
case 2:
_buff.Append("[warn]: ", 9);
break;
case 3:
_buff.Append("[error]: ", 9);
break;
default:
_buff.Append("[info]: ", 9);
break;
}
}
void
Log::_AsyncWrite()
{
std::string str = "";
while (_deque->pop(str)) {
std::lock_guard<std::mutex> locker(_mtx);
fputs(str.c_str(), _fp);
}
}
//日志对象单例
Log*
Log::Instance()
{
static Log inst;
return &inst;
}
void
Log::FlushLogThread()
{
Log::Instance()->_AsyncWrite();
}