袖珍Windows日志记录工具

    xiaoxiao2025-11-19  8

    <span style="font-family: Arial, Helvetica, sans-serif;">#ifndef _CLOGGER_H__</span> #define _CLOGGER_H__ #include <string> #include <list> #include <atlstr.h> #include <Windows.h> // 一条日志最长长度 #define MAX_BUFFER_LENGTH 500 class CLogger { public: CLogger(void); ~CLogger(void); BOOL Init(const std::string& strFilePath); BOOL Uninit(void); void Log(LPCSTR lpszFormat, ...); private: static DWORD WINAPI LoggerThreadFunc(LPVOID lpVoid); DWORD LogFunc(void); BOOL OpenLogFile(void); void CloseLogFile(void); void WriteLogFile(LPCSTR lpszData); BOOL m_bExit; HANDLE m_hLoggerHandle; // 日志记录通知事件 HANDLE m_hNotifyEvent; std::string m_strLogFile; FILE* m_pFileStm; CRITICAL_SECTION m_logLock; std::list<std::string> m_logDataList; }; extern CLogger g_logger; #endif // _CLOGGER_H__

    #include "Logger.h"

    CLogger::CLogger(void) { m_bExit = FALSE; m_hLoggerHandle = NULL; m_hNotifyEvent = NULL; m_pFileStm = NULL; } CLogger::~CLogger(void) { } BOOL CLogger::Init(const std::string& strFilePath) { m_strLogFile = strFilePath; // 初始化关键段 ::InitializeCriticalSection(&m_logLock); // 创建通知日志记录事件 m_hNotifyEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); if (!m_hNotifyEvent) return FALSE; // 创建日志记录线程 m_hLoggerHandle = ::CreateThread(NULL, 0, LoggerThreadFunc, this, 0, NULL); if (!m_hLoggerHandle) return FALSE; return TRUE; } BOOL CLogger::Uninit(void) { // 首先设置标记让线程结束 m_bExit = TRUE; if (m_hLoggerHandle) { // 设置日志记录事件为激活状态,为了把暂时还没有写到日志文件的日志继续写完 if (m_hNotifyEvent) ::SetEvent(m_hNotifyEvent); // 等待线程结束 DWORD dwRet = WaitForSingleObject(m_hLoggerHandle, 60 * 1000); if (dwRet == WAIT_TIMEOUT) { ::TerminateThread(m_hLoggerHandle, 0); } } if (m_hNotifyEvent) { CloseHandle(m_hNotifyEvent); m_hNotifyEvent = NULL; } return TRUE; } DWORD CLogger::LoggerThreadFunc(LPVOID lpVoid) { CLogger *logger = (CLogger*)lpVoid; if (logger) return logger->LogFunc(); return 0; } DWORD CLogger::LogFunc(void) { std::list<std::string> logDataList; do { DWORD dwWaitRet = WaitForSingleObject(m_hNotifyEvent, INFINITE); // 暂时没有日志需要记录 if (dwWaitRet != WAIT_OBJECT_0) break; do { ::EnterCriticalSection(&m_logLock); // 交换链表数据,此时logDataList一定为空 logDataList.swap(m_logDataList); // 设置事件为无信号状态 ::ResetEvent(m_hNotifyEvent); ::LeaveCriticalSection(&m_logLock); if (logDataList.empty()) break; if (OpenLogFile()) { // 把所有的日志写到文件 std::string strLog; while (!logDataList.empty()) { strLog = logDataList.front(); logDataList.pop_front(); WriteLogFile(strLog.c_str()); } CloseLogFile(); } else { break; } } while (TRUE); if (m_bExit) break; } while (TRUE); return 0; } void CLogger::Log(LPCSTR lpszFormat, ...) { va_list arglist; char buffer[MAX_BUFFER_LENGTH + 1] = {0}; va_start(arglist, lpszFormat); _vsnprintf(buffer, MAX_BUFFER_LENGTH, lpszFormat, arglist); va_end(arglist); SYSTEMTIME sys_time; ::GetLocalTime(&sys_time); // 得到当前时间 char timeBuf[50] = {0}; sprintf(timeBuf, "%d-%d-%d %02d:%02d:%02d %03d ", sys_time.wYear, sys_time.wMonth, sys_time.wDay, sys_time.wHour, sys_time.wMinute, sys_time.wSecond, sys_time.wMilliseconds); std::string strLog; strLog = timeBuf; strLog += buffer; ::EnterCriticalSection(&m_logLock); m_logDataList.push_back(strLog); ::LeaveCriticalSection(&m_logLock); // 设置通知日志记录事件为激活状态 ::SetEvent(m_hNotifyEvent); } BOOL CLogger::OpenLogFile(void) { if (!m_pFileStm) { m_pFileStm = fopen(m_strLogFile.c_str(), "ab"); } return m_pFileStm != NULL; } void CLogger::CloseLogFile(void) { if (m_pFileStm) { fclose(m_pFileStm); m_pFileStm = NULL; } } void CLogger::WriteLogFile(LPCSTR lpszData) { if (m_pFileStm) { long length = strlen(lpszData); fwrite(lpszData, 1, length, m_pFileStm); fwrite("\r\n", 1, 2, m_pFileStm); } } CLogger g_logger;

    分享一个简单的日志记录小工具,这个工具是有一个独立的类,比较独立,相比市面上比较大的日志记录工具,比如log4plus(可参考这篇文章:http://blog.csdn.net/augusdi/article/details/8989494) ,zlog(可以参考这篇文章:http://blog.csdn.net/yangzhenzhen/article/details/8439459),这个工具比较小巧,不过功能也很有限。 大致的思路 首先会启动一个线程来负责写日志,但是不是有数据了就会立刻写,而是先在一个链表中把需要写入的日志数据缓存起来, 线程执行的时候会把缓存中的数据拿出来,加上本地时间之后写入到文件中。 那么如果一直没有日志数据需要写,线程不就会一直运行,看起来就比较消耗系统资源了,毕竟日志记录一般来说是一个辅助的功能, 为了解决这个问题,代码中会使用一个事件来进行处理,如果缓存中没有数据需要写,那么日志线程就会等待日志事件,而不是一直循环查询缓存数据, 而在日志记录函数中,会向缓存中写数据,并且同时会设置日志事件为激活状态,这时日志线程就会从等待函数中返回,继续进行日志的写入。 另外一个需要注意的问题是要对数据进行加锁。因为日志记录函数中会访问缓存数据(具体来说是对数据进行写入),线程函数会对缓存数据进行访问(也有写的部分), 日志记录函数是在程序的主线程,或者其它线程中进行的,所以一定要加锁,从而保证数据的一致性,不会被写乱。

    使用方法:

    1. 初始化 g_logger.Init(...);

    2. 写入日志 g_logger.Log(...)

    3. 反初始化g_logger.Uninit(...)

    这里是使用了一个全局变量来使用的,这里也可以使用一个单件模式来实现。或者使用三个宏来对着三个函数进行封装,从而简化使用。

    存在的问题:

    1. 只能在windows下使用

    2. 对文件大小没有做限制,一般来说够用,如有特殊需求,可以给单个文件设置一个大小比如10M,达到超过指定大小之后再生产一个日志文件,之后依次类推

    3. 日志记录方式比较单一,不够灵活,比如行号,文件名等,而且也不支持控制台日志输出

    转载请注明原文地址: https://ju.6miu.com/read-1304370.html
    最新回复(0)