参考glog写了现有的Logging系统。
直接有
enum LoggingEnum{
LOG_INFO,
LOG_DBBUG,
LOG_ERROR,
LOG_WARNNING,
LOG_END
};
几种等级的日志,实时刷到console上,异步延迟写到日志上,建立队列缓存日志,时间一到一起刷到file,好了,看下Logging实现:
class Active; struct Buffer; enum LoggingEnum{ LOG_INFO, LOG_DBBUG, LOG_ERROR, LOG_WARNNING, LOG_END }; enum GLogColor { COLOR_DEFAULT, COLOR_RED, COLOR_GREEN, COLOR_YELLOW }; class Logging { public: Logging(); ~Logging(); void WriteWithFunLine(LoggingEnum eLoggingEnum, char* fun, int line, char* msg, ...); void WriteBuffer(Buffer*& pBuffer); private: INT32 CreateLogFile(LoggingEnum aLoggingEnum); void Write(LoggingEnum eLoggingEnum, char* msg, int msgLen); private: FILE* m_File[LOG_END]; Active* m_pActive; typedef boost::posix_time::ptime PTIME; boost::atomic<BOOLEAN> m_IfCanFlush[LOG_END]; PTIME m_PrelushTime[LOG_END]; std::queue<Buffer*> m_BufferQueue[LOG_END]; };
Logging g_Logging; string LOGPreStr[LOG_END] = {"LOG_INFO", "LOG_DBBUG", "LOG_ERROR", "LOG_WARNNING"}; #ifdef _WINDOWS // Returns the character attribute for the given color. WORD GetColorAttribute(GLogColor color) { switch (color) { case COLOR_RED: return FOREGROUND_RED; case COLOR_GREEN: return FOREGROUND_GREEN; case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; default: return 0; } } #else // Returns the ANSI color code for the given color. const char* GetAnsiColorCode(GLogColor color) { switch (color) { case COLOR_RED: return "1"; case COLOR_GREEN: return "2"; case COLOR_YELLOW: return "3"; case COLOR_DEFAULT: return ""; }; return NULL; // stop warning about return type. } #endif // OS_WINDOWS GLogColor GLogColorVec[LOG_END] = {COLOR_GREEN, COLOR_DEFAULT, COLOR_RED, COLOR_YELLOW}; Logging::Logging(){ PTIME nowTime = boost::posix_time::microsec_clock::local_time(); INT32 n32Res = eNormal; for (INT32 i = LOG_INFO; i < LOG_END; ++i){ m_File[i] = NULL; m_IfCanFlush[i] = FALSE; m_PrelushTime[i] = nowTime; n32Res = CreateLogFile((LoggingEnum)i); if (n32Res != eNormal){ printf("Createfile(i) failed", i); } } m_pActive = Active::CreateActive(std::bind(&Logging::WriteBuffer, this, std::placeholders::_1)); } void Logging::Write(LoggingEnum eLoggingEnum, char* msg, int msgLen){ Buffer* pBuffer = m_pActive->GetBuffer(); pBuffer->m_LogLevel = eLoggingEnum; char* curData = pBuffer->m_pMsg; pBuffer->m_Len = msgLen; memcpy(curData, msg, msgLen); m_pActive->Send(pBuffer); } void Logging::WriteBuffer(Buffer*& pBuffer){ char* pContent = pBuffer->m_pMsg; pContent[pBuffer->m_Len] = '\0'; const GLogColor color = GLogColorVec[pBuffer->m_LogLevel]; #ifdef _WINDOWS const HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); CONSOLE_SCREEN_BUFFER_INFO buffer_info; GetConsoleScreenBufferInfo(stderr_handle, &buffer_info); const WORD old_color_attrs = buffer_info.wAttributes; fflush(stderr); SetConsoleTextAttribute(stderr_handle, GetColorAttribute(color) | FOREGROUND_INTENSITY); fwrite(pContent, pBuffer->m_Len+1, 1, stderr); fflush(stderr); SetConsoleTextAttribute(stderr_handle, old_color_attrs); #else fprintf(stderr, "\033[0;3%sm", GetAnsiColorCode(color)); fwrite(message, len, 1, stderr); fprintf(stderr, "\033[m"); // Resets the terminal to default. #endif FILE* pNowFile = m_File[pBuffer->m_LogLevel]; if (pNowFile == NULL){ return; } std::queue<Buffer*>& bufferQueue = m_BufferQueue[pBuffer->m_LogLevel]; if (FALSE == m_IfCanFlush[pBuffer->m_LogLevel]){ bufferQueue.push(pBuffer); pBuffer = NULL; } else{ while (FALSE == bufferQueue.empty()){ Buffer* pPreBuffer = bufferQueue.front(); bufferQueue.pop(); fwrite(pPreBuffer->m_pMsg, 1, pPreBuffer->m_Len, pNowFile); fflush(pNowFile); m_pActive->ReleaseBuffer(pPreBuffer); } fwrite(pContent, 1, pBuffer->m_Len, pNowFile); fflush(pNowFile); m_IfCanFlush[pBuffer->m_LogLevel] = FALSE; m_PrelushTime[pBuffer->m_LogLevel] = boost::posix_time::microsec_clock::local_time(); } } void Logging::WriteWithFunLine(LoggingEnum eLoggingEnum, char* fun, int line, char* msg, ...){ if (eLoggingEnum < LOG_INFO || eLoggingEnum > LOG_WARNNING){ eLoggingEnum = eLoggingEnum; } char tmp[1024] = {0}; PTIME nowTime = boost::posix_time::microsec_clock::local_time(); boost::posix_time::time_duration timeFidd = nowTime - m_PrelushTime[eLoggingEnum]; if (timeFidd.total_microseconds() > 1000){ m_IfCanFlush[eLoggingEnum] = TRUE; } sprintf(tmp, "%s.%d %s:%d ", boost::posix_time::to_iso_string(nowTime).c_str(), boost::this_thread::get_id(), fun, line); int curPos = strlen(tmp); va_list pArg = NULL; va_start(pArg, msg); vsprintf(tmp+curPos, msg, pArg); va_end(pArg); curPos = strlen(tmp); char end[] = "\n"; sprintf(tmp + curPos, "%s", end); Write(eLoggingEnum, tmp, strlen(tmp)); } Logging::~Logging(){ for (INT32 i = 0; i < 4; ++i){ if (NULL != m_File[i]){ fclose(m_File[i]); } } delete m_pActive; } INT32 Logging::CreateLogFile(LoggingEnum aLoggingEnum){ string strPre; switch (aLoggingEnum){ case LOG_DBBUG: strPre = "LOG_DBBUG"; break; case LOG_INFO: strPre = "LOG_INFO"; break; case LOG_WARNNING: strPre = "LOG_WARNNING"; break; case LOG_ERROR: strPre = "LOG_ERROR"; break; } char str[128]; sprintf(str, "./Log/%s-%d-%s", strPre.c_str() ,boost::this_thread::get_id(), boost::posix_time::to_iso_string(boost::posix_time::microsec_clock::local_time()).c_str()); string fileName(str); fileName += ".log"; int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0664); if (fd == -1){ printf("%s create(%d) file error\n", __FUNCTION__, aLoggingEnum); return -1; } m_File[aLoggingEnum] = fdopen(fd, "a"); if (NULL == m_File[aLoggingEnum]){ printf("%s open file(%d) failed\n", __FUNCTION__, aLoggingEnum); return -1; } return 0; }
其中用到的Active就是简单的生产者消费者模型:
struct Buffer { Buffer():m_LogLevel(0), m_Len(1024), m_pMsg(new char[m_Len]){} ~Buffer(){ if (NULL != m_pMsg) delete []m_pMsg; } Buffer(int size):m_LogLevel(0), m_Len(size) , m_pMsg(new char[m_Len]){ } int m_LogLevel; int m_Len; char* m_pMsg; }; typedef std::function<void(Buffer*&)> Callback; class Active { private: Active(const Active&); Active& operator=(const Active&); Active(); void doDone(){m_IfDone = true;} void Run(); void setCallBack(Callback aCallBack); bool IfEmpty(); int Consume(Buffer*& apBuffer); std::queue<Buffer*> m_Queue; boost::thread m_Thread; volatile bool m_IfDone; Callback m_Callback; boost::mutex m_Mutex; boost::mutex m_ObjectMutex; CSSObejctPool<Buffer> m_pBufferPool; boost::condition_variable m_ConditionVar; public: ~Active(); Buffer* GetBuffer(); void ReleaseBuffer(Buffer*& pBuffer); void Send(Buffer* apBuffer); void Stop(); static Active* CreateActive(Callback aCallBack); };
Active::Active(): m_IfDone(false){} Active::~Active() { Stop(); } void Active::Send( Buffer* apBuffer ){ if (NULL != apBuffer){ boost::mutex::scoped_lock lock(m_Mutex); bool bNeedSig = m_Queue.empty(); m_Queue.push(apBuffer); if (bNeedSig){ m_ConditionVar.notify_one(); } } } void Active::Run() { Buffer* pBuffer = NULL; while (!m_IfDone && 0 == Consume(pBuffer)){ m_Callback(pBuffer); if (NULL != pBuffer){ boost::mutex::scoped_lock lock(m_ObjectMutex); m_pBufferPool.ReleaseObejct(pBuffer); } } } Active* Active::CreateActive(Callback aCallBack){ Active* aPtr = new Active(); aPtr->m_Thread = boost::thread(&Active::Run, aPtr); aPtr->m_Callback = aCallBack; return aPtr; } void Active::setCallBack(Callback aCallBack){ m_Callback = aCallBack; } Buffer* Active::GetBuffer(){ boost::mutex::scoped_lock lock(m_ObjectMutex); return m_pBufferPool.AcquireObject(); } bool Active::IfEmpty(){ return m_Queue.empty(); } int Active::Consume(Buffer*& apBuffer){ boost::mutex::scoped_lock lock(m_Mutex); while (m_Queue.empty()){ if (m_IfDone){ return 1; } m_ConditionVar.wait(lock); } if (m_IfDone){ return 1; } apBuffer = m_Queue.front(); m_Queue.pop(); return 0; } void Active::Stop(){ m_IfDone = true; m_ConditionVar.notify_one(); } void Active::ReleaseBuffer(Buffer*& pBuffer){ if (NULL != pBuffer){ boost::mutex::scoped_lock lock(m_ObjectMutex); m_pBufferPool.ReleaseObejct(pBuffer); pBuffer = NULL; } }
ok!只有4个文件,只要有boost库就可以编译使用了。用到的objectpool是之前博客介绍过的内存池。
简单的日志系统
时间: 2024-10-05 04:25:06