一、使用原因:
在实现高并发的服务器日志系统过程中,由于在工作线程中直接进行IO操作,相比较于高速的CPU,IO磁盘操作是很慢的,直接在某些工作线程(包括UI线程)写文件,程序执行速度太慢,尤其是当日志数据比较多的时候,此时,我们可以使用一个队列,需要写日志时,将日志加入队列中,另外一个专门的日志线程来写日志。
二、代码如下:
Logger.h:
#ifndef __LOGGER_H__ #define __LOGGER_H__ #include <string> #include <memory> #include <thread> #include <mutex> #include <condition_variable> #include <list> //struct FILE; #define LogInfo(...) Logger::GetInstance().AddToQueue("INFO", __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) #define LogWarning(...) Logger::GetInstance().AddToQueue("WARNING", __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) #define LogError(...) Logger::GetInstance().AddToQueue("ERROR", __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) class Logger { public: static Logger& GetInstance(); void SetFileName(const char* filename); bool Start(); void Stop(); void AddToQueue(const char* pszLevel, const char* pszFile, int lineNo, const char* pszFuncSig, char* pszFmt, ...); private: Logger() = default; Logger(const Logger& rhs) = delete; Logger& operator =(Logger& rhs) = delete; void threadfunc(); private: std::string filename_; FILE* fp_{}; std::shared_ptr<std::thread> spthread_; std::mutex mutex_; std::condition_variable cv_; //有新的日志到来的标识 bool exit_{ false }; std::list<std::string> queue_; }; #endif //!__LOGGER_H__
Logger.cpp:
#include "stdafx.h" #include <windows.h> #include "Logger.h" #include <time.h> #include <stdio.h> #include <memory> #include <stdarg.h> Logger& Logger::GetInstance() { static Logger logger; return logger; } void Logger::SetFileName(const char* filename) { filename_ = filename; } bool Logger::Start() { if (filename_.empty()) { time_t now = time(NULL); struct tm* t = localtime(&now); char timestr[64] = { 0 }; sprintf(timestr, "%04d%02d%02d%02d%02d%02d.imserver.log", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); filename_ = timestr; } fp_ = fopen(filename_.c_str(), "wt+"); if (fp_ == NULL) return false; spthread_.reset(new std::thread(std::bind(&Logger::threadfunc, this))); return true; } void Logger::Stop() { exit_ = true; cv_.notify_one(); //等待时间线程结束 spthread_->join(); } void Logger::AddToQueue(const char* pszLevel, const char* pszFile, int lineNo, const char* pszFuncSig, char* pszFmt, ...) { char msg[256] = { 0 }; va_list vArgList; va_start(vArgList, pszFmt); vsnprintf(msg, 256, pszFmt, vArgList); va_end(vArgList); time_t now = time(NULL); struct tm* tmstr = localtime(&now); char content[512] = { 0 }; /*sprintf(content, "[%04d-%02d-%02d %02d:%02d:%02d][%s][0x%04x][%d] ", tmstr->tm_year + 1900, tmstr->tm_mon + 1, tmstr->tm_mday, tmstr->tm_hour, tmstr->tm_min, tmstr->tm_sec, pszLevel, std::this_thread::get_id(), lineNo );*/ sprintf_s(content, ARRAYSIZE(content), "[%04d-%02d-%02d %02d:%02d:%02d][%s][0x%04x][%s:%d %s]%s ", tmstr->tm_year + 1900, tmstr->tm_mon + 1, tmstr->tm_mday, tmstr->tm_hour, tmstr->tm_min, tmstr->tm_sec, pszLevel, GetCurrentThreadId(), pszFile, lineNo, pszFuncSig, msg); { std::lock_guard<std::mutex> guard(mutex_); queue_.emplace_back(content); } cv_.notify_one(); } void Logger::threadfunc() { if (fp_ == NULL) return; while (!exit_) { //写日志 std::unique_lock<std::mutex> guard(mutex_); while (queue_.empty()) { if (exit_) return; cv_.wait(guard); } //写日志 const std::string& str = queue_.front(); fwrite((void*)str.c_str(), str.length(), 1, fp_); fflush(fp_); queue_.pop_front(); } }