C++ 实现日志系统

发布时间 2023-05-27 01:54:49作者: shmilyt

自实现日志

全局宏定义

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();
	}
}