自实现日志
全局宏定义
common.h
#pragma once
#if !defined(__PRETTY_FUNCTION__) && !defined(__GNUC__)
#define __PRETTY_FUNCTION__ __FUNCSIG__
#endif
// 允许打印在控制台的单条日志的最大长度
const int MAX_CONSOLE_LOG_LENGTH = 512;
注意common.h被多个文件包含时, MAX_CONSOLE_LOG_LENGTH会被编译器替换为512, 所以不会造成重复定义
头文件
#pragma once
#include "common.h"
#include <queue>
#include <string>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <functional>
#include <ctime>
#define LogInfo(...) Logger::GetInstance().AddToQueue("info", __FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
#define LogError(...) Logger::GetInstance().AddToQueue("error", __FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
#define LogWarning(...) Logger::GetInstance().AddToQueue("warning", __FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
class Logger {
public:
Logger();
~Logger();
static Logger& GetInstance();
void AddToQueue(const char* logLevel, const char* fileName, int lineNo, const char* funcName, const char* msg);
void WriteLog();
private:
std::queue<std::string> m_queue;
FILE* m_fp;
std::mutex m_mutex;
std::atomic<bool> m_isRunning;
std::shared_ptr<std::thread> m_threadPtr;
std::condition_variable m_cv;
};
实现
#include "Logger.h"
Logger::Logger()
{
// 创建日志文件并启动写线程
time_t now = time(nullptr);
struct tm* tp = localtime(&now);
char logFileName[128] = { 0 };
sprintf(logFileName, "D:\\mlog_%04d%02d%02d.log", tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday);
// 以只写模式打开文本文件
m_fp = fopen(logFileName, "at+");
if (m_fp == nullptr) {
return;
}
m_isRunning = true;
m_threadPtr.reset(new std::thread(std::bind(&Logger::WriteLog, this)));
}
Logger::~Logger()
{
m_isRunning = false;
// 等待写线程结束
m_cv.notify_one();
m_threadPtr->join();
fclose(m_fp);
m_fp = nullptr;
printf("析构函数执行完毕");
}
Logger& Logger::GetInstance()
{
static Logger logger;
return logger;
}
void Logger::AddToQueue(const char* logLevel, const char* fname, int lineNo, const char* funcName, const char* msg)
{
time_t now = time(NULL);
struct tm* tp = localtime(&now);
char formatStr[512] = { 0 };
// [年-月-日 时:分:秒][日志等级][文件:行号 函数名] 信息
sprintf(formatStr, "[%04d-%02d-%02d %02d:%02d:%02d][%s][%s:%d %s]%s\n",
tp->tm_year+ 1900, tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec,
logLevel, fname, lineNo, funcName, msg);
{
std::lock_guard<std::mutex> lock(m_mutex);
m_queue.emplace(formatStr);
}
m_cv.notify_one();
}
void Logger::WriteLog()
{
if (m_fp == nullptr) {
return;
}
while (m_isRunning) {
std::unique_lock<std::mutex> lock(m_mutex);
while (m_queue.empty()) {
// 阻塞等待
m_cv.wait(lock);
}
// 向控制台和文件文件写日志
const std::string &msg = m_queue.front();
size_t len = msg.size();
if (len < MAX_CONSOLE_LOG_LENGTH) {
printf(msg.c_str());
}
fwrite(msg.c_str(), len, 1, m_fp);
fflush(m_fp);
m_queue.pop();
}
}