个人实现的轻量化日志助手类, 实现了日志常用功能, 目的是使用简便快捷, 直接包含头文件和源文件即可使用
功能
1.可设置日志文件存放路径以及文件名前缀
2.可设置单个日志文件最大容量, 到限后自动转储到下一个日志文件
3.可设置日志自动保存, 按照时间间隔或日志缓存条目数量
4.可设置最多保存的日志文件数量, 超限会自动删除最旧的日志文件
5.可设置管道模式, 在管道模式下可使用另一个进程来处理日志转存
调用: CLogUtils::LOG_INFO(_T("%d %s"), 1024, _T("FlameCyclone"));
日志文件输出内容: 2023-10-25 16:44:57.379 INFO [29288:32416] [E:\gitee\c-log-utils\CLogUtils\CLogUtils\main.cpp:20] [wmain] 1024 FlameCyclone
CPU: AMD Ryzen 7 6800U with Radeon Graphics
内存: 8GB(6400Mhz) * 2
Release x64 默认模式下速度 50万条 / 秒

Release x64 管道模式下速度 5万条 / 秒

CPU: Intel(R) Core(TM) i9-14900K
内存: 16GB(6000Mhz) * 2
Release x64 默认模式下 140万条 / 秒

Release x64 管道模式 54万条 / 秒

CLogUtils.h
- #pragma once
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- //请在VS项目属性页 -> C/C++ -> 预处理器 中 添加如下宏指定日志根目录, 用于截取源码文件路径相对路径
- //LOG_ROOT_DIR=R"($(ProjectDir))"
- //效果:
- //添加宏之前: 2023-11-22 13:14:39.644 INFO [34688:22188] [D:\Gitee_FlameCyclone\c-log-utils\CLogUtils\CLogUtils\main.cpp:155] [CLogUtilsTest] 1024 FlameCyclone
- //添加宏之后: 2023-11-22 12:31:33.45 INFO [20884:7996] [.\main.cpp:133] [CLogUtilsTest] 1024 FlameCyclone
-
- #ifdef _UNICODE
- using _tstring = std::wstring;
- #else
- using _tstring = std::string;
- #endif
-
- #pragma warning(disable:4200)
-
- namespace CLogUtils
- {
- class CNamedPipe;
-
- #define STRING_CONTENT_BUFFER_ENABLE (1) //使用字符串作为日志缓冲
- #define LOG_FILE_COUNT (16) //最多日志文件历史数量
- #define LOG_TIMEOUT (1000 * 60) //自动保存超时时间(毫秒)
- #define LOG_FILE_SIZE (1024 * 1024 * 16) //单个日志文件大小阈值(字节)
- #define LOG_BUF_COUNT (10000) //日志缓冲大小阈值
-
- #define LOG_INFO(format, ...)\
- GetInstance().Logging(_T(" INFO"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);
-
- #ifdef _DEBUG
- #define LOG_DEBUG(format, ...)\
- GetInstance().Logging(_T("DEBUG"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);
- #else
- #define LOG_DEBUG(format, ...)\
- GetInstance().DoNothing();
- #endif
-
- #define LOG_WARN(format, ...)\
- GetInstance().Logging(_T(" WARN"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);
-
- #define LOG_ERROR(format, ...)\
- GetInstance().Logging(_T("ERROR"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);
-
- class CLogHelper
- {
-
- public:
-
- #define Info(format, ...)\
- Logging(_T(" INFO"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);
-
- #ifdef _DEBUG
- #define Debug(format, ...)\
- Logging(_T("DEBUG"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);
- #else
- #define Debug(format, ...)\
- DoNothing();
- #endif
-
- #define Warn(format, ...)\
- Logging(_T(" WARN"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);
-
- #define Error(format, ...)\
- Logging(_T("ERROR"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);
-
- public:
-
- //默认构造
- CLogHelper();
-
- //删除拷贝构造与赋值重载
- CLogHelper(const CLogHelper&) = delete;
-
- //删除赋值重载
- CLogHelper& operator = (const CLogHelper&) = delete;
-
- //析构
- ~CLogHelper();
-
- //
- // @brief: 初始化
- // @param: bPipeMode 管道模式 true: 管道模式 false: 默认模式
- // @param: strPipeName 管道名
- // @param: strDir 存放目录
- // @param: strPreName 文件名前缀
- // @param: nFileSize 单个日志文件大小阈值(字节), 到达限制转储到下一个文件
- // @param: nTempCount 日志缓存条目限制(到达限制转储到文件)
- // @param: nTimeout 自动存储时间间隔(毫秒), 到达限制转储到文件
- // @param: nFileCount 日志文件数量限制, 到达限制删除最旧的日志文件
- // @ret: bool 执行结果
- bool Initialize(
- bool bPipeMode = false,
- const _tstring& strPipeName = _T(""),
- const _tstring& strDir = _T(""),
- const _tstring& strPreName = _T(""),
- DWORD nFileSize = LOG_FILE_SIZE,
- DWORD nFileCount = LOG_FILE_COUNT,
- DWORD nTempCount = LOG_BUF_COUNT,
- DWORD nTimeout = LOG_TIMEOUT
- );
-
- //
- // @brief: 反初始化
- // @ret: void
- void Uninitialize();
-
- //
- // @brief: 记录一条日志
- // @param: pstrLevel 日志等级
- // @param: pstrFile 源码文件
- // @param: pstrFunc 源码函数
- // @param: nLine 行数
- // @param: pstrFormat 格式化字符串
- // @param: ... 可变参数
- // @ret: void
- void Logging(
- LPCTSTR pstrLevel,
- LPCTSTR pstrFile,
- LPCTSTR pstrFunc,
- UINT nLine,
- LPCTSTR pstrFormat,
- ...
- );
-
- //
- // @brief: 清空已经存储的日志文件
- // @ret: void
- void Clear();
-
- //
- // @brief: 刷新日志缓冲(输出日志到文件)
- // @ret: bool 执行结果
- bool FlushBuffers();
-
- //
- // @brief: 啥也不干
- // @ret: void
- void DoNothing();
-
- private:
-
- //
- // @brief: 获取目录下文件路径
- // @ret: std::vector<_tstring> 日志文件列表
- std::map<int64_t, _tstring> _GetLogFileList(const _tstring& strDir);
-
- //
- // @brief: 调整日志文件数量
- // @param: void
- // @ret: void
- void _AdjustLogFile();
-
- //
- // @brief: 通过管道处理日志
- // @ret: bool 执行结果
- bool _ProcessByPipe();
-
- //
- // @brief: 超时保存处理
- // @ret: bool 执行结果
- bool _ProcessTimeoutSave();
-
- //
- // @brief: 管道方式记录日志
- // @ret: bool 执行结果
- bool _LoggingByPipe(const _tstring& strLogContent);
-
- //
- // @brief: 刷新日志缓冲(输出日志到文件)
- // @ret: bool 执行结果
- bool _FlushLogBuffers();
-
- //
- // @brief: 记录日志
- // @ret: bool 执行结果
- bool _LoggingContent(const _tstring& strLogContent);
-
- //
- // @brief: 初始化
- // @param: void
- // @ret: bool 执行结果
- bool _Initialize();
-
- //
- // @brief: 取消初始化
- // @param: void
- // @ret: void
- void _Uninitialize();
-
- //
- // @brief: 初始化日志文件
- // @param: void
- // @ret: int 日志文件索引
- void _InitLogFile();
-
- //
- // @brief: 生成日志转储文件路径
- // @param: void
- // @ret: void
- void _GenerateLogFilePath();
-
- //
- // @brief: 获取默认日志管道名
- // @param: void
- // @ret: _tstring 管道名
- _tstring _GetDefaultPipeName() const;
-
- private:
-
- std::vector<_tstring> m_logList; //日志记录缓冲
- std::map<int64_t, _tstring> m_logFileList; //日志文件记录, 按照时间戳排序
- std::thread m_tAutoSaveTask; //超时自动保存任务线程
- std::thread m_tPipeRecvTask; //管道接收任务线程
- std::mutex m_Lock; //线程安全锁
- CNamedPipe* m_pRecvPipe = nullptr; //日志接收管道
- CNamedPipe* m_pSendPipe = nullptr; //日志发送管道
-
- HANDLE m_hEvent = nullptr; //通知事件, 使用自动转储的超时等待
- HANDLE m_hFile = INVALID_HANDLE_VALUE; //文件句柄, 日志文件写入使用
- int64_t m_nFileTimetamp = 0; //日志文件时间戳
- _tstring m_strSaveDir; //日志存放目录
- _tstring m_strSaveName; //日志文件名
- _tstring m_strFilePath; //当前日志文件路径
- _tstring m_strLogContent; //日志内容
- _tstring m_strPipeName; //管道名
- bool m_bStop = false; //停止标记
- bool m_bFirst = false; //首次记录日志标记
- bool m_bPipeMode = false; //管道模式
-
- DWORD m_nFileSize = 0; //文件大小限制(到达限制则转储到文件)
- DWORD m_nTempCount = 0; //缓存条目限制(到达限制则转储到文件)
- DWORD m_nFileCount = 0; //历史文件数量限制(超限则删除旧文件)
- DWORD m_nTimeout = 0; //自动保存超时限制(超时则转储到文件)
- DWORD m_nCurFileSize = 0; //日志文件统计
- DWORD m_nNextItemSize = 0; //下一条日志大小
- DWORD m_nLogItemCount = 0; //日志缓冲统计
- };
-
- class CNamedPipe
- {
- public:
- CNamedPipe();
-
- ~CNamedPipe();
-
- CNamedPipe(const CNamedPipe& r) = delete;
- CNamedPipe& operator = (const CNamedPipe& r) = delete;
-
- //
- // @brief: 创建命名管道
- // @param: lpName 管道名
- // @ret: bool true: 创建成功 false: 创建失败
- bool Create(LPCTSTR lpName);
-
- //
- // @brief: 等待客户端连接命名管道
- // @param: nTimeOut 超时等待(毫秒)
- // @ret: bool true: 连接成功 false: 连接失败
- bool WaitConnect(DWORD nTimeOut = INFINITE);
-
- //
- // @brief: 关闭由Create 创建的管道
- // @param: void
- // @ret: bool true: 关闭 成功 false: 关闭 失败
- bool Disconnect();
-
- //
- // @brief: 打开已存在的命名管道
- // @param: lpName 管道名
- // @ret: bool true: 打开成功 false: 打开失败
- bool Open(LPCTSTR lpName, DWORD nTimeOut = INFINITE);
-
- //
- // @brief: 管道是否有效
- // @param: void
- // @ret: bool true: 可用 false: 无效
- bool IsValid();
-
- //
- // @brief: 关闭管道
- // @param: void
- // @ret: void
- void Close(void);
-
- //
- // @brief: 从读取管道数据
- // @param: lpData 数据存放缓冲
- // @param: nSize 缓冲大小(字节)
- // @param: lpBytesRead 指向实际读取大小(字节)的指针
- // @param: nTimeOut 读取超时(毫秒)
- // @ret: bool true: 读取成功 false: 读取失败
- bool Read(LPVOID lpData, DWORD nSize, LPDWORD lpBytesRead = nullptr, DWORD nTimeOut = INFINITE);
-
- //
- // @brief: 向管道写入数据
- // @param: lpData 写入数据指针
- // @param: nSize 写入数据大小(字节)
- // @param: lpBytesWritten 指向实际写入大小(字节)的指针
- // @param: nTimeOut 写入超时(毫秒)
- // @ret: bool true: 写入成功 false: 写入失败
- bool Write(LPCVOID lpData, DWORD nSize, LPDWORD lpBytesWritten = nullptr, DWORD nTimeOut = INFINITE);
-
- private:
-
- //
- // @brief: 初始化对象占用
- // @param: void
- // @ret: void
- bool Initialize();
-
- //
- // @brief: 释放对象占用
- // @param: void
- // @ret: void
- void Uninitialize();
-
- private:
- HANDLE m_hNamedPipe = INVALID_HANDLE_VALUE;
- HANDLE m_hReadEvent = NULL;
- HANDLE m_hWriteEvent = NULL;
- HANDLE m_hConnectEvent = NULL;
- LPVOID m_pBuffer = nullptr;
- bool m_bInit = false;
- bool m_bConnected = false;
- };
-
- //
- // @brief: 格式化字符串
- // @param: void
- // @ret: bool 执行结果
- _tstring Format(LPCTSTR pstrFormat, ...);
-
- //
- // @brief: 获取当前进程完全路径
- // @ret: 当前进程完全路径 如 D:\Software\HxDPortableSetup.exe
- _tstring GetCurrentModulePath();
-
- //
- // @brief: 获取当前进程所在目录
- // @ret: 当前进程所在目录 如 D:\Software
- _tstring GetCurrentModuleDir();
-
- //
- // @brief: 获取当前进程名
- // @ret: 当前进程名 如 HxDPortableSetup.exe
- _tstring GetCurrentModuleName(bool bHasExt = false);
-
- //
- // @brief: 获取文件所在文件夹
- // @param: strPath 文件名, 如: D:\Software\HxDPortableSetup.exe
- // @ret: 文件夹 如 D:\Software
- _tstring GetFileDir(const _tstring& strPath);
-
- //
- // @brief: 获取文件名
- // @param: strPath 文件名, 如: D:\Software\HxDPortableSetup.exe
- // @param: bHasExt 是否包含扩展名
- // @ret: 文件夹 如 HxDPortableSetup
- _tstring GetFileName(const _tstring& strPath, bool bHasExt = false);
-
- //
- // @brief: 检查文件是否存在
- // @param: strPath 文件名, 如: D:\Software\HxDPortableSetup.exe
- // @ret: 是否存在 存在返回 true
- bool IsDirectory(const _tstring& strPath);
-
- //
- // @brief: 创建目录(递归)
- // @param: strPath 路径
- // @ret: 成功返回true
- bool CreateDir(const _tstring& strPath);
-
- //
- // @brief: 删除文件
- // @param: strPath 路径
- // @ret: 成功返回true
- bool DeleteArchive(const _tstring& strPath);
-
- //
- // @brief: 获取当前时间戳字符串
- // @param: void
- // @ret: _tstring 时间戳字符串 如: 2023-10-11 17:43:00.617
- _tstring GetCurrentTimeString();
-
- //
- // @brief: 获取当前时间戳
- // @param: void
- // @ret: 时间戳(单位: 毫秒) 如: 1697017380617
- int64_t GetCurrentTimestamp();
-
- //
- // @brief: 时间戳转字符串
- // @param: strFormat 格式化字符串 如: "%04d-%02d-%02d %02d:%02d:%02d.03%d"
- // @param: timestamp 时间戳 如: 1697017380617
- // @ret: 时间字符串 如: 2023-10-11 17:43:00.617
- _tstring TimestampToString(
- int64_t timestamp = 0,
- const _tstring& strFormat = _T("%04d-%02d-%02d %02d:%02d:%02d.03%d")
- );
-
- //
- // @brief: 获取文件大小
- // @param: strPath 路径
- // @ret: 文件大小
- unsigned long long GetFileSize(const _tstring& strPath);
-
- CLogHelper& GetInstance();
- }
CLogUtils.cpp
- #include "CLogUtils.h"
- #include
- #include
- #include
- #include
-
- #pragma comment(lib, "Shlwapi.lib")
-
- namespace CLogUtils
- {
- #define DEFAULT_LOG_FILE_COUNT_MIN (4) //默认最少日志文件历史数量
- #define DEFAULT_LOG_TIMEOUT_MIN (1000 * 5) //默认自动保存超时时间(毫秒)
- #define DEFAULT_LOG_FILE_SIZE_MIN (1024 * 1024 * 1) //默认单个日志文件大小阈值(字节)
- #define DEFAULT_LOG_BUF_COUNT_MIN (256) //默认日志缓冲数量阈值
-
- #define DEFAULT_LOG_FILE_COUNT_MAX (256) //默认最多日志文件历史数量
- #define DEFAULT_LOG_TIMEOUT_MAX (1000 * 60) //默认自动保存超时时间(毫秒)
- #define DEFAULT_LOG_FILE_SIZE_MAX (1024 * 1024 * 256) //默认单个日志文件大小阈值(字节)
- #define DEFAULT_LOG_BUF_COUNT_MAX (1024 * 4) //默认日志缓冲数量阈值
-
- #define PIPE_NAME_PREFIX TEXT(R"(\\.\pipe\)") //管道前缀名
- #define PIPE_MAX_TIMEOUT (3000) //管道打开超时
- #define PIPE_BUF_MAX_SIZE (1024 * 1024) //管道发送缓冲大小(字节)
- #define PIPE_MAX_CONNECT (1) //管道最大实例数量
-
- typedef struct _PIPE_DATA
- {
- DWORD dwSize = 0;
- BYTE data[0];
- }PIPE_DATA, * PPIPE_DATA;
-
- //全局实例构造
- static CLogHelper g_Instance;
-
- CLogHelper::CLogHelper() :
- m_bPipeMode(false),
- m_strPipeName(_T("")),
- m_strSaveDir(_T("")),
- m_strSaveName(_T("")),
- m_nFileSize(LOG_FILE_SIZE),
- m_nTempCount(LOG_BUF_COUNT),
- m_nTimeout(LOG_TIMEOUT),
- m_nFileCount(LOG_FILE_COUNT)
- {
- }
-
- CLogHelper::~CLogHelper()
- {
- this->Uninitialize();
- }
-
- void CLogHelper::DoNothing()
- {
- }
-
- bool CLogHelper::Initialize(
- bool bPipeMode/* = false*/,
- const _tstring& strPipeName/* = _T("")*/,
- const _tstring& strDir/* = _T("")*/,
- const _tstring& strPreName/* = _T("")*/,
- DWORD nFileSize/* = LOG_FILE_SIZE*/,
- DWORD nFileCount/* = LOG_FILE_COUNT*/,
- DWORD nTmpCount/* = LOG_BUF_COUNT*/,
- DWORD nTimeout/* = LOG_TIMEOUT*/
- )
- {
- m_bPipeMode = bPipeMode;
- m_strPipeName = strPipeName;
- m_strSaveDir = strDir;
- m_strSaveName = strPreName;
- m_nFileSize = nFileSize;
- m_nTempCount = nTmpCount;
- m_nTimeout = nTimeout;
- m_nFileCount = nFileCount;
-
- //最小参数限制
- if (m_nFileSize < DEFAULT_LOG_FILE_SIZE_MIN)
- {
- m_nFileSize = DEFAULT_LOG_FILE_SIZE_MIN;
- }
-
- if (m_nFileCount < DEFAULT_LOG_FILE_COUNT_MIN)
- {
- m_nFileCount = DEFAULT_LOG_FILE_COUNT_MIN;
- }
-
- if (m_nTempCount < DEFAULT_LOG_BUF_COUNT_MIN)
- {
- m_nTempCount = DEFAULT_LOG_BUF_COUNT_MIN;
- }
-
- if (m_nTimeout < DEFAULT_LOG_TIMEOUT_MIN)
- {
- m_nTimeout = DEFAULT_LOG_TIMEOUT_MIN;
- }
-
- //最大参数限制
- if (m_nFileSize > DEFAULT_LOG_FILE_SIZE_MAX)
- {
- m_nFileSize = DEFAULT_LOG_FILE_SIZE_MAX;
- }
-
- if (m_nFileCount > DEFAULT_LOG_FILE_COUNT_MAX)
- {
- m_nFileCount = DEFAULT_LOG_FILE_COUNT_MAX;
- }
-
- if (m_nTempCount > DEFAULT_LOG_BUF_COUNT_MAX)
- {
- m_nTempCount = DEFAULT_LOG_BUF_COUNT_MAX;
- }
-
- if (m_nTimeout > DEFAULT_LOG_TIMEOUT_MAX)
- {
- m_nTimeout = DEFAULT_LOG_TIMEOUT_MAX;
- }
-
- //默认目录为当前进程目录
- if (m_strSaveDir.empty())
- {
- m_strSaveDir = GetCurrentModuleDir();
- }
-
- //默认文件名为当前进程名
- if (m_strSaveName.empty())
- {
- m_strSaveName = GetCurrentModuleName(true);
- }
-
- //目录不存在就创建目录
- if (!IsDirectory(m_strSaveDir))
- {
- CreateDir(m_strSaveDir);
- }
-
- if (m_strPipeName.empty())
- {
- m_strPipeName = _GetDefaultPipeName();
- }
-
- return this->_Initialize();
- }
-
- void CLogHelper::Uninitialize()
- {
- _Uninitialize();
- }
-
- bool CLogHelper::_Initialize()
- {
- _Uninitialize();
-
- // 管道模式
- if (m_bPipeMode)
- {
- m_pRecvPipe = new (std::nothrow) CNamedPipe;
- m_pSendPipe = new (std::nothrow) CNamedPipe;
-
- if (nullptr == m_pRecvPipe)
- {
- return false;
- }
-
- if (nullptr == m_pSendPipe)
- {
- return false;
- }
-
- std::promise<bool> m;
- std::future<bool> p = m.get_future();
-
- m_tPipeRecvTask = std::move(
- std::thread([this, &m]() -> void
- {
- m.set_value(true);
- _ProcessByPipe();
- }
- )
- );
-
- //等待线程启动完毕
- p.get();
- }
-
- // 超时自动保存任务
- {
- if (NULL == m_hEvent)
- {
- m_hEvent = ::CreateEvent(nullptr, false, false, nullptr);
- }
-
- if (NULL == m_hEvent)
- {
- return false;
- }
-
- std::promise<bool> m;
- std::future<bool> p = m.get_future();
-
- m_tAutoSaveTask = std::move(
- std::thread([this, &m]()
- {
- m.set_value(true);
- _ProcessTimeoutSave();
- }
- )
- );
-
- //等待线程启动完毕
- p.get();
- }
-
- return true;
- }
-
- void CLogHelper::_Uninitialize()
- {
- if (!m_logList.empty() || !m_strLogContent.empty())
- {
- FlushBuffers();
- }
-
- if (INVALID_HANDLE_VALUE != m_hFile)
- {
- ::CloseHandle(m_hFile);
- m_hFile = INVALID_HANDLE_VALUE;
- }
-
- if (NULL != m_hEvent)
- {
- m_bStop = true;
- ::SetEvent(m_hEvent);
- }
-
- if (m_tAutoSaveTask.joinable())
- {
- m_tAutoSaveTask.join();
- }
-
- if (NULL != m_hEvent)
- {
- ::CloseHandle(m_hEvent);
- m_hEvent = NULL;
- }
-
- if (m_bPipeMode)
- {
- if (m_pRecvPipe)
- {
- m_pRecvPipe->Close();
- }
-
- if (m_pSendPipe)
- {
- m_pSendPipe->Close();
- }
-
- if (m_tPipeRecvTask.joinable())
- {
- m_tPipeRecvTask.join();
- }
- }
-
- if (nullptr != m_pRecvPipe)
- {
- delete m_pRecvPipe;
- m_pRecvPipe = nullptr;
- }
-
- if (nullptr != m_pSendPipe)
- {
- delete m_pSendPipe;
- m_pSendPipe = nullptr;
- }
-
- m_bFirst = false;
- m_bStop = false;
- }
-
- bool CLogHelper::_LoggingByPipe(const _tstring& strLogContent)
- {
- bool bSuccess = false;
-
- if (nullptr == m_pSendPipe)
- {
- return false;
- }
-
- if (!m_pSendPipe->IsValid())
- {
- if (!m_pSendPipe->Open(m_strPipeName.c_str(), 1000))
- {
- return false;
- }
- }
-
- //写入日志内容到管道, 交给另一端处理(可用跨进程)
- bSuccess = m_pSendPipe->Write(strLogContent.c_str(), (DWORD)((strLogContent.size() + 1) * sizeof(TCHAR)));
- if (!bSuccess)
- {
- m_pSendPipe->Close();
- }
-
- return bSuccess;
- }
-
- bool CLogHelper::_FlushLogBuffers()
- {
- DWORD dwNumberOfBytesWrite = 0;
- bool bSuccess = false;
-
- if (INVALID_HANDLE_VALUE == m_hFile)
- {
- return false;
- }
-
- #if STRING_CONTENT_BUFFER_ENABLE
- //没有需要写入的日志
- if (m_strLogContent.empty())
- {
- return true;
- }
-
- bSuccess = ::WriteFile(m_hFile, m_strLogContent.c_str(), (DWORD)(m_strLogContent.size() * sizeof(TCHAR)), &dwNumberOfBytesWrite, NULL);
- m_strLogContent.clear();
- #else
- //没有需要写入的日志
- if (m_logList.empty())
- {
- return true;
- }
-
- for (const auto& item : m_logList)
- {
- bSuccess = ::WriteFile(m_hFile, item.c_str(), (DWORD)(item.size() * sizeof(TCHAR)), &dwNumberOfBytesWrite, NULL);
- if (!bSuccess)
- {
- break;
- }
- }
-
- #endif
-
- return bSuccess;
- }
-
- bool CLogHelper::_LoggingContent(const _tstring& strLogContent)
- {
- //获取单行日志内容 + 固定前缀内容 + 真实内容
- m_nNextItemSize = (DWORD)(strLogContent.size() * sizeof(TCHAR));
-
- std::lock_guard
lock(m_Lock) ; -
- //首次启动时, 重置大小统计
- if (!m_bFirst)
- {
- _InitLogFile();
- _AdjustLogFile();
- m_nCurFileSize = (DWORD)GetFileSize(m_strFilePath);
- m_bFirst = true;
- }
-
- //单个日志文件大小即将达到或超过阈值则输出到文件, 启用新的文件存储
- if ((m_nCurFileSize + m_nNextItemSize) >= m_nFileSize)
- {
- _FlushLogBuffers();
- m_logList.clear();
-
- ::CloseHandle(m_hFile);
- m_hFile = INVALID_HANDLE_VALUE;
-
- (void)_GenerateLogFilePath();
- m_nCurFileSize = (DWORD)GetFileSize(m_strFilePath);
- _AdjustLogFile();
- }
-
- //已缓存条目达到阈值则输出到文件
- #if STRING_CONTENT_BUFFER_ENABLE
- else if (m_nLogItemCount >= m_nTempCount)
- {
- _FlushLogBuffers();
- m_strLogContent.clear();
- m_nLogItemCount = 0;
- }
- #else
- else if (m_logList.size() >= m_nTempCount)
- {
- _FlushLogBuffers();
- m_logList.clear();
- }
-
- #endif
-
- #if STRING_CONTENT_BUFFER_ENABLE
- m_strLogContent += strLogContent;
- m_nLogItemCount++;
- #else
- m_logList.emplace_back(strLogContent);
- #endif
-
- //累加统计单个日志文件大小
- m_nCurFileSize += m_nNextItemSize;
- return true;
- }
-
- bool CLogHelper::_ProcessTimeoutSave()
- {
- while (!m_bStop)
- {
- DWORD dwWait = ::WaitForSingleObject(m_hEvent, m_nTimeout);
- switch (dwWait)
- {
- case WAIT_TIMEOUT:
- case WAIT_OBJECT_0:
- {
- std::lock_guard
lock(m_Lock) ; - this->_FlushLogBuffers();
- m_logList.clear();
- }
- break;
- default:
- break;
- }
- }
-
- return true;
- }
-
- bool CLogHelper::_ProcessByPipe()
- {
- if (m_pRecvPipe->IsValid())
- {
- return true;
- }
-
- if (!m_pRecvPipe->Create(m_strPipeName.c_str()))
- {
- return false;
- }
-
- DWORD dwPipeBufSize = PIPE_BUF_MAX_SIZE;
- LPTSTR lpData = nullptr;
- bool bFailed = false;
-
- lpData = (LPTSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwPipeBufSize);
- if (nullptr == lpData)
- {
- return false;
- }
-
- while (!m_bStop && !bFailed)
- {
- if (!m_pRecvPipe->WaitConnect())
- {
- _tprintf(_T("WaitConnect failed!\r\n"));
- break;
- }
-
- while (!m_bStop)
- {
- bool isSuccess = m_pRecvPipe->Read(lpData, dwPipeBufSize);
- if (isSuccess)
- {
- _LoggingContent(lpData);
- }
-
- if (ERROR_BROKEN_PIPE == ::GetLastError())
- {
- m_pRecvPipe->Disconnect();
- if (!m_pRecvPipe->WaitConnect())
- {
- _tprintf(_T("WaitConnect failed!\r\n"));
- bFailed = true;
- break;
- }
- }
-
- if (!m_pRecvPipe->IsValid())
- {
- break;
- }
- }
- }
-
- this->_FlushLogBuffers();
- m_logList.clear();
-
- if (nullptr != lpData)
- {
- ::HeapFree(::GetProcessHeap(), 0, lpData);
- lpData = nullptr;
- }
-
- return true;
- }
-
- void CLogHelper::Logging(
- LPCTSTR pstrLevel,
- LPCTSTR pstrFile,
- LPCTSTR pstrFunc,
- UINT nLine,
- LPCTSTR pstrFormat,
- ...
- )
- {
- if (nullptr == pstrFormat)
- {
- return;
- }
-
- TCHAR szBuf[MAX_PATH] = { 0 };
- DWORD dwPid = ::GetCurrentProcessId();
- DWORD dwTid = ::GetCurrentThreadId();
- _tstring strLogContent;
- SYSTEMTIME st = { 0 };
- (void)::GetLocalTime(&st);
-
- #ifdef LOG_ROOT_DIR
-
- //日志格式前缀 [时间] [等级] [十进制进程ID:十进制线程ID] [源码位置:行数] [函数名]
- //相对路径显示源码文件路径
-
- _TCHAR szRelativePath[MAX_PATH] = { 0 };
- if (::PathRelativePathTo(szRelativePath, _T(LOG_ROOT_DIR), FILE_ATTRIBUTE_DIRECTORY, pstrFile, 0))
- {
- ::StringCchPrintf(
- szBuf,
- _countof(szBuf),
- _T("%04d-%02d-%02d %02d:%02d:%02d.%03d %s [%d:%d] [%s:%d] [%s] "),
- st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
- pstrLevel,
- dwPid,
- dwTid,
- szRelativePath,
- nLine,
- pstrFunc
- );
- }
- else
- {
- ::StringCchPrintf(
- szBuf,
- _countof(szBuf),
- _T("%04d-%02d-%02d %02d:%02d:%02d.%03d %s [%d:%d] [%s:%d] [%s] "),
- st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
- pstrLevel,
- dwPid,
- dwTid,
- pstrFile,
- nLine,
- pstrFunc
- );
- }
-
- #else
- //日志格式前缀 [时间] [等级] [十进制进程ID:十进制线程ID] [源码位置:行数] [函数名]
- ::StringCchPrintf(
- szBuf,
- _countof(szBuf),
- _T("%04d-%02d-%02d %02d:%02d:%02d.%03d %s [%d:%d] [%s:%d] [%s] "),
- st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
- pstrLevel,
- dwPid,
- dwTid,
- pstrFile,
- nLine,
- pstrFunc
- );
-
- #endif
-
- strLogContent = szBuf;
-
- va_list args;
- va_start(args, pstrFormat);
-
- LPTSTR pFormatBuf = nullptr; //格式化日志缓冲
- DWORD dwFormatBufCch = MAX_PATH; //格式化日志缓冲大小
-
- do
- {
- //分配缓冲
- pFormatBuf = (LPTSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwFormatBufCch * sizeof(TCHAR));
- if (nullptr == pFormatBuf)
- {
- break;
- }
-
- //成功则赋值字符串并终止循环
- if (-1 != _vsntprintf_s(pFormatBuf, dwFormatBufCch, _TRUNCATE, pstrFormat, args))
- {
- strLogContent += pFormatBuf;
- break;
- }
-
- //释放缓冲, 待下次重新分配
- ::HeapFree(::GetProcessHeap(), 0, pFormatBuf);
- pFormatBuf = nullptr;
-
- //超限终止处理
- if (dwFormatBufCch >= INT32_MAX)
- {
- break;
- }
-
- dwFormatBufCch *= 2;
-
- } while (true);
- va_end(args);
-
- //释放缓冲
- if (nullptr != pFormatBuf)
- {
- ::HeapFree(::GetProcessHeap(), 0, pFormatBuf);
- pFormatBuf = nullptr;
- }
-
- strLogContent += _T("\r\n");
-
- if (m_bPipeMode)
- {
- _LoggingByPipe(strLogContent);
- }
- else
- {
- _LoggingContent(strLogContent);
- }
- }
-
- void CLogHelper::Clear()
- {
- std::lock_guard
lock(m_Lock) ; -
- if (INVALID_HANDLE_VALUE != m_hFile)
- {
- ::CloseHandle(m_hFile);
- m_hFile = INVALID_HANDLE_VALUE;
- }
-
- m_logFileList = _GetLogFileList(m_strSaveDir);
- for (const auto& item : m_logFileList)
- {
- DeleteArchive(item.second);
- }
-
- m_logFileList.clear();
- }
-
- bool CLogHelper::FlushBuffers()
- {
- std::lock_guard
lock(m_Lock) ; - return _FlushLogBuffers();
- }
-
- void CLogHelper::_AdjustLogFile()
- {
- //检查文件数量是否到达阈值, 到达的话删除前面的文件
- if (m_logFileList.size() > m_nFileCount)
- {
- size_t nDeleteCount = m_logFileList.size() - m_nFileCount;
-
- //从日志文件记录列表中删除
- for (size_t i = 0; i < nDeleteCount; i++)
- {
- auto itBegin = m_logFileList.begin();
- DeleteArchive(itBegin->second);
- m_logFileList.erase(m_logFileList.begin());
- }
- }
- }
-
- void CLogHelper::_InitLogFile()
- {
- //如果上次最后一个日志文件大小还能存储日志, 就沿用上次的日志文件
- m_logFileList = _GetLogFileList(m_strSaveDir);
- if (!m_logFileList.empty())
- {
- auto itLast = m_logFileList.end();
- itLast--;
-
- m_nFileTimetamp = itLast->first;
- m_strFilePath = itLast->second;
-
- //上次最后一个日志文件不能存储更多日志, 则生成新的日志文件路径
- unsigned long long ullFileSize = (DWORD)GetFileSize(m_strFilePath);
- if ((ullFileSize + m_nNextItemSize) >= m_nFileSize)
- {
- (void)_GenerateLogFilePath();
- }
- else
- {
- //打开文件以续写日志
- m_hFile = CreateFile(
- m_strFilePath.c_str(),
- GENERIC_WRITE,
- FILE_SHARE_READ,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL
- );
-
- //在文件末尾追加内容
- LARGE_INTEGER liDistanceToMove = { 0 };
- ::SetFilePointerEx(m_hFile, liDistanceToMove, NULL, FILE_END);
- }
- }
- else
- {
- (void)_GenerateLogFilePath();
- }
- }
-
- //
- // @brief: 获取日志管道名
- // @param: void
- // @ret: _tstring 管道名
- _tstring CLogHelper::_GetDefaultPipeName() const
- {
- return Format(_T("%s_%s"), m_strSaveDir.c_str(), m_strSaveName.c_str());
- }
-
- void CLogHelper::_GenerateLogFilePath()
- {
- //得到日志文件时间戳
- m_nFileTimetamp = GetCurrentTimestamp();
-
- //得到日志文件路径
- m_strFilePath = Format(_T("%s\\%s_%s.log"),
- m_strSaveDir.c_str(),
- m_strSaveName.c_str(),
- TimestampToString(m_nFileTimetamp, _T("%04d-%02d-%02d_%02d-%02d-%02d-%03d")).c_str()
- );
-
- //创建一下文件(防止在资源管理器中看不到新的日志文件)
- m_hFile = CreateFile(
- m_strFilePath.c_str(),
- GENERIC_WRITE,
- FILE_SHARE_READ,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL
- );
-
- //在文件末尾追加内容
- LARGE_INTEGER liDistanceToMove = { 0 };
- ::SetFilePointerEx(m_hFile, liDistanceToMove, NULL, FILE_END);
-
- m_logFileList.insert(std::make_pair(m_nFileTimetamp, m_strFilePath));
- }
-
- CNamedPipe::CNamedPipe() :
- m_pBuffer(nullptr),
- m_hNamedPipe(INVALID_HANDLE_VALUE),
- m_hReadEvent(NULL),
- m_hWriteEvent(NULL),
- m_bConnected(false),
- m_bInit(false)
- {
- //初始化读写缓冲与事件句柄
- Initialize();
- }
-
- CNamedPipe::~CNamedPipe()
- {
- //释放读写缓冲与事件句柄
- Uninitialize();
- }
-
- bool CNamedPipe::Create(LPCTSTR lpName)
- {
- TCHAR szPipeName[MAX_PATH];
- SECURITY_ATTRIBUTES sa = { 0 };
- SECURITY_DESCRIPTOR sd = { 0 };
- bool isSuccess = false;
-
- sa.nLength = sizeof(sa);
- sa.bInheritHandle = FALSE;
- sa.lpSecurityDescriptor = &sd;
-
- if (INVALID_HANDLE_VALUE != m_hNamedPipe)
- {
- return true;
- }
-
- //设置权限, 防止低权限进程不能打开高权限进程创建的管道
- (void)::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
- (void)::SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
- (void)::StringCchPrintf(szPipeName, _countof(szPipeName), TEXT("%s%s"), PIPE_NAME_PREFIX, lpName);
-
- do
- {
- m_hNamedPipe = ::CreateNamedPipe(
- szPipeName,
- PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
- PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
- PIPE_MAX_CONNECT,
- PIPE_BUF_MAX_SIZE,
- PIPE_BUF_MAX_SIZE,
- PIPE_MAX_TIMEOUT,
- &sa
- );
-
- if (INVALID_HANDLE_VALUE == m_hNamedPipe)
- {
- break;
- }
-
- isSuccess = true;
-
- } while (false);
-
- if (!isSuccess)
- {
- this->Close();
- }
-
- return isSuccess;
- }
-
- bool CNamedPipe::Open(LPCTSTR lpName, DWORD nTimeOut/* = INFINITE*/)
- {
- TCHAR szPipeName[MAX_PATH] = { 0 };
- bool isSuccess = false;
-
- (void)::StringCchPrintf(szPipeName, _countof(szPipeName), TEXT("%s%s"), PIPE_NAME_PREFIX, lpName);
-
- if (INVALID_HANDLE_VALUE != m_hNamedPipe)
- {
- return true;
- }
-
- ULONGLONG ullCurTick = ::GetTickCount64();
-
- do
- {
- m_hNamedPipe = ::CreateFile(
- szPipeName,
- GENERIC_READ | GENERIC_WRITE,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
- NULL
- );
-
- //管道句柄有效则终止循环
- if (INVALID_HANDLE_VALUE != m_hNamedPipe)
- {
- isSuccess = true;
- break;
- }
-
- //若错误原因不是因为所有管道范例都在使用中, 则退出循环
- if (ERROR_PIPE_BUSY != ::GetLastError())
- {
- break;
- }
-
- //等待命名管道的实例可用于连接
- if (::WaitNamedPipe(szPipeName, 1000))
- {
- continue;
- }
-
- //无限等待则不需要检查超时
- if (INFINITE == nTimeOut)
- {
- continue;
- }
-
- //执行操作超时则退出循环
- if (::GetTickCount64() - ullCurTick > nTimeOut)
- {
- break;
- }
-
- } while (INVALID_HANDLE_VALUE == m_hNamedPipe);
-
- if (!isSuccess)
- {
- this->Close();
- }
-
- return isSuccess;
- }
-
- bool CNamedPipe::WaitConnect(DWORD nTimeOut)
- {
- OVERLAPPED Overlapped = { 0 };
- bool isConnected = false;
-
- if (INVALID_HANDLE_VALUE == m_hNamedPipe)
- {
- return false;
- }
-
- Overlapped.hEvent = m_hConnectEvent;
- isConnected = ::ConnectNamedPipe(m_hNamedPipe, &Overlapped);
- if (!isConnected)
- {
- DWORD dwError = ::GetLastError();
-
- //管道关闭中
- if (ERROR_NO_DATA == dwError)
- {
- isConnected = false;
- }
- else if (ERROR_IO_PENDING == dwError)//操作处于挂起状态
- {
- if (WAIT_OBJECT_0 == ::WaitForSingleObject(Overlapped.hEvent, nTimeOut))
- {
- isConnected = true;
- }
- }
- else if (ERROR_PIPE_CONNECTED == dwError)//管道已经连接
- {
- isConnected = true;
- }
- }
-
- m_bConnected = isConnected;
-
- return isConnected;
- }
-
- bool CNamedPipe::Disconnect()
- {
- if (INVALID_HANDLE_VALUE == m_hNamedPipe)
- {
- return false;
- }
-
- //参数句柄必须由 CreateNamedPipe 函数创建
- return ::DisconnectNamedPipe(m_hNamedPipe);
- }
-
- void CNamedPipe::Close()
- {
- if (INVALID_HANDLE_VALUE != m_hNamedPipe)
- {
- if (m_bConnected)
- {
- ::FlushFileBuffers(m_hNamedPipe);
- ::DisconnectNamedPipe(m_hNamedPipe);
- m_bConnected = false;
- }
- ::CloseHandle(m_hNamedPipe);
- m_hNamedPipe = INVALID_HANDLE_VALUE;
- }
-
- if (m_hReadEvent)
- {
- ::SetEvent(m_hReadEvent);
- ::CloseHandle(m_hReadEvent);
- m_hReadEvent = NULL;
- }
-
- if (m_hWriteEvent)
- {
- ::SetEvent(m_hWriteEvent);
- ::CloseHandle(m_hWriteEvent);
- m_hWriteEvent = NULL;
- }
-
- if (m_hConnectEvent)
- {
- ::SetEvent(m_hConnectEvent);
- ::CloseHandle(m_hConnectEvent);
- m_hConnectEvent = NULL;
- }
- }
-
- bool CNamedPipe::IsValid()
- {
- return INVALID_HANDLE_VALUE != m_hNamedPipe;
- }
-
- bool CNamedPipe::Read(LPVOID lpData, DWORD nSize, LPDWORD lpBytesRead/* = nullptr*/, DWORD nTimeOut)
- {
- OVERLAPPED Overlapped = { 0 };
- Overlapped.hEvent = m_hReadEvent;
- DWORD dwBytesTransferred = 0;
- bool isSuccess = false;
-
- if (nullptr == m_pBuffer ||
- nullptr == lpData ||
- 0 == nSize ||
- nSize > PIPE_BUF_MAX_SIZE
- )
- {
- return false;
- }
-
- PPIPE_DATA pData = reinterpret_cast
(m_pBuffer); - if (!::ReadFile(m_hNamedPipe, &pData->dwSize, sizeof(PIPE_DATA), NULL, &Overlapped))
- {
- //管道已结束
- if (ERROR_BROKEN_PIPE == ::GetLastError())
- {
- return false;
- }
-
- if (ERROR_IO_PENDING != ::GetLastError())
- {
- return false;
- }
-
- if (WAIT_OBJECT_0 != ::WaitForSingleObject(Overlapped.hEvent, nTimeOut))
- {
- return false;
- }
- }
-
- if (pData->dwSize > PIPE_BUF_MAX_SIZE)
- {
- return false;
- }
-
- if (!::ReadFile(m_hNamedPipe, pData->data, pData->dwSize, NULL, &Overlapped))
- {
- if (ERROR_IO_PENDING != ::GetLastError())
- {
- return false;
- }
-
- if (WAIT_OBJECT_0 != ::WaitForSingleObject(Overlapped.hEvent, nTimeOut))
- {
- return false;
- }
- }
-
- if (::GetOverlappedResult(m_hNamedPipe, &Overlapped, &dwBytesTransferred, true))
- {
- isSuccess = true;
- if (lpBytesRead)
- {
- *lpBytesRead = dwBytesTransferred;
- }
- }
-
- if (isSuccess)
- {
- if (nSize < pData->dwSize)
- {
- ::memcpy_s(lpData, nSize, pData->data, nSize);
- }
- else
- {
- ::memcpy_s(lpData, nSize, pData->data, pData->dwSize);
- }
- }
-
- return isSuccess;
- }
-
- bool CNamedPipe::Write(LPCVOID lpData, DWORD nSize, LPDWORD lpBytesWritten/* = nullptr*/, DWORD nTimeOut)
- {
- OVERLAPPED Overlapped = { 0 };
- Overlapped.hEvent = m_hWriteEvent;
- DWORD dwBytesTransferred = 0;
- bool isSuccess = false;
-
- if (nullptr == m_pBuffer ||
- nullptr == lpData ||
- 0 == nSize ||
- nSize > PIPE_BUF_MAX_SIZE
- )
- {
- return false;
- }
-
- PPIPE_DATA pData = reinterpret_cast
(m_pBuffer); - DWORD dwBytesToWrite = nSize + sizeof(PIPE_DATA);
- pData->dwSize = nSize;
- ::memcpy_s(pData->data, PIPE_BUF_MAX_SIZE, lpData, nSize);
-
- if (::WriteFile(m_hNamedPipe, pData, dwBytesToWrite, NULL, &Overlapped))
- {
- return true;
- }
-
- //管道正在被关闭
- if (ERROR_NO_DATA == ::GetLastError())
- {
- return false;
- }
-
- //管道已结束
- if (ERROR_BROKEN_PIPE == ::GetLastError())
- {
- return false;
- }
-
- //重叠
- if (ERROR_IO_PENDING != ::GetLastError())
- {
- return false;
- }
-
- if (WAIT_OBJECT_0 != ::WaitForSingleObject(Overlapped.hEvent, nTimeOut))
- {
- return false;
- }
-
- if (::GetOverlappedResult(m_hNamedPipe, &Overlapped, &dwBytesTransferred, true))
- {
- isSuccess = true;
- if (lpBytesWritten)
- {
- *lpBytesWritten = dwBytesTransferred;
- }
- }
-
- return isSuccess;
- }
-
- bool CNamedPipe::Initialize()
- {
- bool isSuccess = false;
-
- if (m_bInit)
- {
- return true;
- }
-
- do
- {
- if (nullptr == m_pBuffer)
- {
- m_pBuffer = ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, PIPE_BUF_MAX_SIZE + sizeof(PIPE_DATA));
- }
-
- if (NULL == m_hReadEvent)
- {
- m_hReadEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
- }
-
- if (NULL == m_hWriteEvent)
- {
- m_hWriteEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
- }
-
- if (NULL == m_hConnectEvent)
- {
- m_hConnectEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
- }
-
- //任意事件创建失败则终止
- if ((NULL == m_hReadEvent) || (NULL == m_hWriteEvent) || (NULL == m_hConnectEvent) )
- {
- break;
- }
-
- isSuccess = true;
-
- } while (false);
-
- if (!isSuccess)
- {
- Uninitialize();
- }
-
- m_bInit = isSuccess;
-
- return m_bInit;
- }
-
- void CNamedPipe::Uninitialize()
- {
- if (!m_bInit)
- {
- return;
- }
-
- //关闭管道
- this->Close();
-
- //释放读写缓冲
- if (nullptr != m_pBuffer)
- {
- ::HeapFree(::GetProcessHeap(), 0, m_pBuffer);
- m_pBuffer = nullptr;
- }
-
- m_bInit = false;
- }
-
- std::map<int64_t, _tstring> CLogHelper::_GetLogFileList(const _tstring& strDir)
- {
- std::map<int64_t, _tstring> fileList;
-
- WIN32_FIND_DATA findData = { 0 };
- HANDLE hFindHandle = INVALID_HANDLE_VALUE;
- hFindHandle = FindFirstFile((strDir + _T("\\*.*")).c_str(), &findData);
- if (INVALID_HANDLE_VALUE == hFindHandle)
- {
- return fileList;
- }
-
- do
- {
- _tstring strName = findData.cFileName;
-
- //非目录
- if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
- {
- //检查输入规则
- int nConverted = 0;
- SYSTEMTIME st = { 0 };
-
- _tstring strPath = strDir + _T("\\") + strName;
- _tstring strPrefix = Format(_T("%s_%%4hd-%%2hd-%%2hd_%%2hd-%%2hd-%%2hd-%%3hd.log"), m_strSaveName.c_str());
- nConverted = _stscanf_s(findData.cFileName, strPrefix.c_str(),
- &st.wYear, &st.wMonth, &st.wDay, &st.wHour,
- &st.wMinute, &st.wSecond, &st.wMilliseconds);
-
- //检查文件名规则是否符合要求
- if (7 == nConverted)
- {
- FILETIME ftFile = { 0 };
- FILETIME ftLocal = { 0 };
- int64_t timestamp = 0;
-
- ::SystemTimeToFileTime(&st, &ftLocal);
- ::LocalFileTimeToFileTime(&ftLocal, &ftFile);
-
- timestamp = ((int64_t)ftFile.dwHighDateTime << 32) | ftFile.dwLowDateTime;
- timestamp = (timestamp - 116444736000000000) / 10000;
-
- fileList.insert(std::make_pair(timestamp, strPath));
- }
- }
-
- //上一级目录与当前目录跳过
- if (0 == _tcscmp(findData.cFileName, _T(".")) || 0 == _tcscmp(findData.cFileName, _T("..")))
- {
- continue;
- }
-
- } while (::FindNextFile(hFindHandle, &findData));
-
- ::FindClose(hFindHandle);
-
- return fileList;
- }
-
- _tstring CLogUtils::Format(LPCTSTR pstrFormat, ...)
- {
- _tstring strResult;
- LPTSTR lpBuf = nullptr;
- DWORD dwCchCount = MAX_PATH;
-
- if (nullptr == pstrFormat)
- {
- return strResult;
- }
-
- va_list args;
- va_start(args, pstrFormat);
- do
- {
- //分配缓冲
- lpBuf = (LPTSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwCchCount * sizeof(TCHAR));
- if (nullptr == lpBuf)
- {
- break;
- }
-
- //成功则赋值字符串并终止循环
- if (-1 != _vsntprintf_s(lpBuf, dwCchCount, _TRUNCATE, pstrFormat, args))
- {
- strResult = lpBuf;
- break;
- }
-
- //释放缓冲, 待下次重新分配
- ::HeapFree(::GetProcessHeap(), 0, lpBuf);
- lpBuf = nullptr;
-
- //缓冲字符数在合理范围内则扩大2倍
- if (dwCchCount < INT32_MAX)
- {
- dwCchCount *= 2;
- }
- else
- {
- //超限终止处理
- break;
- }
-
- } while (true);
- va_end(args);
-
- //释放缓冲
- if (nullptr != lpBuf)
- {
- ::HeapFree(::GetProcessHeap(), 0, lpBuf);
- lpBuf = nullptr;
- }
-
- return strResult;
- }
-
- _tstring CLogUtils::GetCurrentTimeString()
- {
- TCHAR szBuf[MAX_PATH] = { 0 };
- SYSTEMTIME st = { 0 };
- (void)::GetLocalTime(&st);
-
- ::StringCchPrintf(
- szBuf,
- _countof(szBuf),
- _T("%04d-%02d-%02d %02d:%02d:%02d.%03d"),
- st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds
- );
-
- return szBuf;
- }
-
- int64_t CLogUtils::GetCurrentTimestamp()
- {
- int64_t timeStamp = 0;
- (void)::GetSystemTimeAsFileTime((FILETIME*)&timeStamp);
- return (timeStamp - 116444736000000000) / 10000;
- }
-
- _tstring CLogUtils::TimestampToString(int64_t timestamp, const _tstring& strFormat)
- {
- TCHAR szBuf[MAX_PATH] = { 0 };
- SYSTEMTIME st = { 0 };
- FILETIME ftFile = { 0 };
- FILETIME ftLocal = { 0 };
-
- timestamp = timestamp * 10000 + 116444736000000000;
- ftFile.dwLowDateTime = timestamp & 0xFFFFFFFF;
- ftFile.dwHighDateTime = timestamp >> 32;
-
- ::FileTimeToLocalFileTime(&ftFile, &ftLocal);
- ::FileTimeToSystemTime(&ftLocal, &st);
-
- ::StringCchPrintf(
- szBuf,
- _countof(szBuf),
- strFormat.c_str(),
- st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds
- );
-
- return szBuf;
- }
-
- _tstring CLogUtils::GetCurrentModulePath()
- {
- TCHAR szCurPath[MAX_PATH] = { 0 };
- ::GetModuleFileName(NULL, szCurPath, _countof(szCurPath));
-
- _tstring strResult = szCurPath;
- return strResult;
- }
-
- _tstring CLogUtils::GetCurrentModuleDir()
- {
- return GetFileDir(GetCurrentModulePath());
- }
-
- _tstring CLogUtils::GetCurrentModuleName(bool bHasExt/* = true*/)
- {
- return GetFileName(GetCurrentModulePath(), bHasExt);
- }
-
- _tstring CLogUtils::GetFileDir(const _tstring& strPath)
- {
- _tstring strResult;
- size_t nIndex = strPath.find_last_of(_T('\\'));
- if (nIndex != _tstring::npos)
- {
- strResult = strPath.substr(0, nIndex);
- }
-
- return strResult;
- }
-
- _tstring CLogUtils::GetFileName(const _tstring& strPath, bool bHasExt/* = true*/)
- {
- _tstring strResult = strPath;
- size_t nIndex = strResult.find_last_of(_T('\\'));
- if (nIndex != _tstring::npos)
- {
- strResult = strResult.substr(nIndex + 1);
- }
-
- if (!bHasExt)
- {
- nIndex = strResult.find_last_of(_T('.'));
- if (nIndex != _tstring::npos)
- {
- return strResult.substr(0, nIndex);
- }
- }
-
- return strResult;
- }
-
- bool CLogUtils::IsDirectory(const _tstring& strPath)
- {
- WIN32_FILE_ATTRIBUTE_DATA attr = { 0 };
- if (!::GetFileAttributesEx(strPath.c_str(), GetFileExInfoStandard, &attr))
- {
- return false;
- }
-
- return attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
- }
-
- bool CLogUtils::CreateDir(const _tstring& strPath)
- {
- _tstring strDriver; //驱动器号, 如 D:
- _tstring strSubPath = strPath; //路径, 如 Test\1\2\3
-
- if (strPath.empty())
- {
- return false;
- }
-
- //获取盘符
- do
- {
- size_t nFindIndex = strPath.find_first_of(':'); //检查是否有驱动器号
- if (nFindIndex == _tstring::npos)
- {
- break;
- }
-
- strDriver = strPath.substr(0, nFindIndex + 1); //得到驱动器号, 如 D:
- nFindIndex = strPath.find(_T("\\"), nFindIndex);
- if (nFindIndex == _tstring::npos)
- {
- break;
- }
-
- strSubPath = strPath.substr(nFindIndex + 1); //得到路径, 如 Test\1\2\3
- } while (false);
-
- _tstring strDestDir;
- size_t nFindBegin = 0;
- size_t nFindIndex = 0;
- do
- {
- nFindIndex = strSubPath.find(_T("\\"), nFindBegin);
- if (nFindIndex != _tstring::npos)
- {
- strDestDir = strSubPath.substr(0, nFindIndex);
- nFindBegin = nFindIndex + 1;
- }
- else
- {
- strDestDir = strSubPath;
- }
-
- if (!strDriver.empty())
- {
- strDestDir = strDriver + _T("\\") + strDestDir;
- }
-
- if (!::CreateDirectory(strDestDir.c_str(), NULL) && ERROR_ALREADY_EXISTS != ::GetLastError())
- {
- return false;
- }
-
- } while (nFindIndex != _tstring::npos);
-
- return true;
- }
-
- bool CLogUtils::DeleteArchive(const _tstring& strPath)
- {
- if (strPath.empty())
- {
- return false;
- }
-
- return ::DeleteFile(strPath.c_str());
- }
-
- unsigned long long CLogUtils::GetFileSize(const _tstring& strPath)
- {
- unsigned long long ullSize = 0;
- WIN32_FILE_ATTRIBUTE_DATA attr = { 0 };
- if (!::GetFileAttributesEx(strPath.c_str(), GetFileExInfoStandard, &attr))
- {
- return 0;
- }
-
- ullSize = (unsigned long long)attr.nFileSizeHigh << 32 | attr.nFileSizeLow;
-
- return ullSize;
- }
-
- CLogHelper& GetInstance()
- {
- return g_Instance;
- }
- }
使用
main.cpp
- #include
- #include
- #include
- #include
- #include "CLogUtils.h"
-
- void PrintHelp();
- void PrintHelp_en_us();
- void PrintHelp_zh_cn();
- void CLogUtilsTest(int nTestCount, _tstring strPipe, _tstring strDir, _tstring strName, int nFileCount, int nFileSize, int nBufCount, int nTimeOut);
-
- int _tmain(int argc, LPCTSTR argv[])
- {
- ::setlocale(LC_ALL, "");
-
- _tstring strLogDir;
- _tstring strLogName;
- _tstring strPipeName;
- DWORD nTestCount = 100000;
- DWORD nFileSize = 1024 * 1024 * 4;
- DWORD nTempCount = 1000;
- DWORD nTimeout = 5000;
- DWORD nFileCount = 16;
- bool bSrv = true;
-
- if (argc <= 1)
- {
- PrintHelp();
- //return -1;
- }
-
- for (int i = 1; i < argc; i++)
- {
- _tstring strSwitchValue = argv[i];
-
- if (!(_T('-') == strSwitchValue[0] || _T('/') == strSwitchValue[0]))
- {
- continue;
- }
-
- _tstring strHelp = strSwitchValue.substr(1);
- if (strHelp == _T("help") || strHelp == _T("?"))
- {
- PrintHelp();
- return 0;
- }
-
- size_t nEqual = strSwitchValue.find(_T('='));
- if (_tstring::npos == nEqual)
- {
- continue;
- }
-
- _tstring strName = strSwitchValue.substr(1, nEqual - 1);
- _tstring strValue = strSwitchValue.substr(nEqual + 1);
-
- std::transform(strName.begin(), strName.end(), strName.begin(), [](TCHAR ch)->TCHAR {
- if (ch >= _T('A') && ch <= _T('Z')) ch &= ~0x20;
- return ch;
- });
-
- if (strName == _T("d"))
- {
- strLogDir = strValue;
- continue;
- }
-
- if (strName == _T("n"))
- {
- strLogName = strValue;
- continue;
- }
-
- if (strName == _T("a"))
- {
- _stscanf_s(strValue.c_str(), _T("%d"), &nTestCount);
- continue;
- }
-
- if (strName == _T("p"))
- {
- strPipeName = strValue;
- continue;
- }
-
- if (strName == _T("f"))
- {
- _stscanf_s(strValue.c_str(), _T("%d"), &nFileCount);
- continue;
- }
-
- if (strName == _T("s"))
- {
- _stscanf_s(strValue.c_str(), _T("%d"), &nFileSize);
- continue;
- }
-
- if (strName == _T("c"))
- {
- _stscanf_s(strValue.c_str(), _T("%d"), &nTempCount);
- continue;
- }
-
- if (strName == _T("t"))
- {
- _stscanf_s(strValue.c_str(), _T("%d"), &nTimeout);
- continue;
- }
-
- if (strName == _T("srv"))
- {
- bSrv = _tcstoul(strValue.c_str(), nullptr, 10) != 0;
- continue;
- }
- }
-
- if (bSrv)
- {
- CLogUtils::GetInstance().Initialize(true, _T("1"), _T(""), _T("Srv"), nFileSize, nFileCount, nTempCount, nTimeout * 1000);
- system("pause");
- }
- else
- {
- while (true)
- {
- CLogUtilsTest(nTestCount, strPipeName, strLogDir, strLogName, nFileSize, nFileCount, nTempCount, nTimeout * 1000);
- system("pause");
- }
- }
-
- return 0;
- }
-
- void CLogUtilsTest(int nTestCount, _tstring strPipe, _tstring strDir, _tstring strName, int nFileSize, int nFileCount, int nBufCount, int nTimeOut)
- {
- uint64_t uBegin = 0;
- uint64_t uEnd = 0;
- uint64_t uCost = 0;
- double lfSpeed = 0.0f;
-
- if (!strPipe.empty())
- {
- CLogUtils::GetInstance().Initialize(true, strPipe, strDir, strName, nFileSize, nFileCount, nBufCount, nTimeOut);
- uBegin = CLogUtils::GetCurrentTimestamp();
-
- for (int i = 0; i < nTestCount; i++)
- {
- CLogUtils::GetInstance().Info(_T("%d %s"), 1024, _T("FlameCyclone"));
- }
- CLogUtils::GetInstance().FlushBuffers();
-
- uEnd = CLogUtils::GetCurrentTimestamp();
- uCost = uEnd - uBegin;
- lfSpeed = (double)nTestCount * 1000 / (uCost);
-
- _tprintf(
- _T("Repeat %d tims, cost time: %lld, speed: %lf/S\r\n"),
- nTestCount,
- uCost,
- lfSpeed
- );
- }
- else
- {
- CLogUtils::GetInstance().Initialize(false, strPipe, strDir, strName, nFileSize, nFileCount, nBufCount, nTimeOut);
- uBegin = CLogUtils::GetCurrentTimestamp();
-
- for (int i = 0; i < nTestCount; i++)
- {
- CLogUtils::GetInstance().Info(_T("%d %s"), 1024, _T("FlameCyclone"));
- }
- CLogUtils::GetInstance().FlushBuffers();
-
- uEnd = CLogUtils::GetCurrentTimestamp();
- uCost = uEnd - uBegin;
- lfSpeed = (double)nTestCount * 1000 / (uCost);
-
- _tprintf(
- _T("Repeat %d tims, cost time: %lld, speed: %lf/S\r\n"),
- nTestCount,
- uCost,
- lfSpeed
- );
- }
- }
-
- void PrintHelp()
- {
- LANGID langID = ::GetThreadUILanguage();
-
- if (LANG_CHINESE == PRIMARYLANGID(langID))
- {
- PrintHelp_zh_cn();
- }
- else
- {
- PrintHelp_en_us();
- }
- }
-
- void PrintHelp_en_us()
- {
- std::wcout << _T("How to use:") << std::endl;
- std::wcout << _T("CLogUtils -options=value") << std::endl;
- std::wcout << std::endl;
-
- std::wcout << _T("-p=[number]: Enable pipe mode and specify the pipe name.") << std::endl;
- std::wcout << _T("-a=[number]: Number of test output logs.") << std::endl;
- std::wcout << _T("-d=[string]: The log file output directory.") << std::endl;
- std::wcout << _T("-n=[string]: The log file name prefix.") << std::endl;
- std::wcout << _T("-f=[number]: Number of log files.") << std::endl;
- std::wcout << _T("-s=[number]: Single log file size(bytes).") << std::endl;
- std::wcout << _T("-c=[number]: Number of log caches.") << std::endl;
- std::wcout << _T("-t=[number]: Automatically save logs to a file timeout value(seconds).") << std::endl;
- std::wcout << _T("-?/-help: Help for CLogSrv.") << std::endl;
- }
-
- void PrintHelp_zh_cn()
- {
- std::wcout << _T("使用方法:") << std::endl;
- std::wcout << _T("CLogUtils.exe -选项=值") << std::endl;
- std::wcout << std::endl;
-
- std::wcout << _T("-p=[字符串]: 启用管道模式并指定管道名.") << std::endl;
- std::wcout << _T("-a=[整数]: 测试输出日志数量.") << std::endl;
- std::wcout << _T("-d=[字符串]: 日志输出目录.") << std::endl;
- std::wcout << _T("-n=[字符串]: 日志文件名前缀.") << std::endl;
- std::wcout << _T("-f=[整数]: 日志文件数量.") << std::endl;
- std::wcout << _T("-s=[整数]: 单个日志文件大小(字节).") << std::endl;
- std::wcout << _T("-c=[整数]: 日志缓冲数量.") << std::endl;
- std::wcout << _T("-t=[整数]: 自动保存超时间隔(秒).") << std::endl;
- std::wcout << _T("-?/-help: 日志单元帮助.") << std::endl;
- }