Win32 简单日志实现

简单实现日志保存, 支持设置日志文件数量, 单个日志文件大小上限, 自动超时保存日志, 日志缓存超限保存

CLogUtils.h

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif

namespace CLogUtils
{
#define HISTORY_FILE_MAX_COUNT          (16)                //最多日志文件历史数量
#define AUTO_SAVE_TIME_OUT              (1000 * 60)         //自动保存超时时间(毫秒)
#define LOG_FILE_MAX_SIZE               (1024 * 1024 * 4)   //单个日志文件大小阈值(字节)
#define LOG_BUF_MAX_COUNT               (1000)              //日志缓冲大小阈值

#define LOG_INFO(format, ...)\
    global_logger.Logging(_T(" INFO"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);

#define LOG_DEBUG(format, ...)\
    global_logger.Logging(_T("DEBUG"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);

#define LOG_WARN(format, ...)\
    global_logger.Logging(_T(" WARN"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);

#define LOG_ERROR(format, ...)\
    global_logger.Logging(_T("ERROR"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);

    class CLogHelper
    {

    public:

#define LogInfo(format, ...)\
    Logging(_T(" INFO"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);

#define LogDebug(format, ...)\
    Logging(_T("DEBUG"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);

#define LogWarn(format, ...)\
    Logging(_T(" WARN"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);

#define LogError(format, ...)\
    Logging(_T("ERROR"), _T(__FILE__), _T(__FUNCTION__), __LINE__, format, ##__VA_ARGS__);

    public:

        //
        // @brief: 构造
        // @param: nFileSize        文件大小阈值(字节)
        // @param: nTmpCount        缓存条目阈值
        // @param: nIntervalTime    自动存储时间间隔(毫秒)
        // @ret: void
        CLogHelper(
            const _tstring& strDir = _T(""),
            const _tstring& strName = _T(""),
            DWORD nFileSize = LOG_FILE_MAX_SIZE,
            DWORD nTmpCount = LOG_BUF_MAX_COUNT,
            DWORD nIntervalTime = AUTO_SAVE_TIME_OUT
        );

        //删除拷贝构造与赋值重载
        CLogHelper(const CLogHelper&) = delete;
        CLogHelper& operator = (const CLogHelper&) = delete;

        ~CLogHelper();

        //
        // @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: 格式化字符串
        // @param: void
        // @ret: bool 执行结果
        _tstring Format(LPCTSTR pstrFormat, ...);

        //
        // @brief: 获取目录下文件路径
        // @ret: std::vector<_tstring> 日志文件列表
        std::map GetLogFileList(const _tstring& strDir);

        // 
        // @brief: 获取当前进程完全路径
        // @ret: 当前进程完全路径 如 D:\Software\HxDPortableSetup.exe
        static _tstring GetCurrentModulePath();

        // 
        // @brief: 获取当前进程所在目录
        // @ret: 当前进程所在目录 如 D:\Software
        static _tstring GetCurrentModuleDir();

        // 
        // @brief: 获取当前进程名
        // @ret: 当前进程名 如 HxDPortableSetup.exe
        static _tstring GetCurrentModuleName(bool bHasExt = false);

        // 
        // @brief: 获取文件所在文件夹
        // @param: strPath     文件名, 如: D:\Software\HxDPortableSetup.exe
        // @ret: 文件夹        如 D:\Software
        static _tstring GetFileDir(const _tstring& strPath);

        // 
        // @brief: 获取文件名
        // @param: strPath     文件名, 如: D:\Software\HxDPortableSetup.exe
        // @param: bHasExt     是否包含扩展名
        // @ret: 文件夹        如 HxDPortableSetup
        static _tstring GetFileName(const _tstring& strPath, bool bHasExt = false);

        // 
        // @brief: 检查文件是否存在
        // @param: strPath     文件名, 如: D:\Software\HxDPortableSetup.exe
        // @ret: 是否存在      存在返回 true
        static bool IsArchive(const _tstring& strPath);

        // 
        // @brief: 检查文件是否存在
        // @param: strPath     文件名, 如: D:\Software\HxDPortableSetup.exe
        // @ret: 是否存在      存在返回 true
        static bool IsDirectory(const _tstring& strPath);

        //
        // @brief: 创建目录(递归)
        // @param: strPath      路径
        // @ret: 成功返回true
        static bool CreateDir(const _tstring& strPath);

        //
        // @brief: 删除文件
        // @param: strPath      路径
        // @ret: 成功返回true
        static bool DeleteArchive(const _tstring& strPath);

        //
        // @brief: 获取当前时间戳字符串
        // @param: void
        // @ret: _tstring 时间戳字符串          如: 2023-10-11 17:43:00.617
        static _tstring GetCurrentTimeString();

        //
        // @brief: 获取当前日期字符串
        // @param: void
        // @ret: _tstring 时间戳字符串          如: 2023-10-11
        static _tstring GetCurrentDateString();

        //
        // @brief: 获取当前时间戳
        // @param: void
        // @ret: 时间戳(单位: 毫秒)    如: 1697017380617
        static int64_t GetCurrentTimestamp();

        //
        // @brief: 时间戳转字符串  
        // @param: strFormat    格式化字符串 如: "%04d-%02d-%02d %02d:%02d:%02d.%d"
        // @param: timestamp    时间戳 如: 1697017380617
        // @ret: 时间字符串            如: 2023-10-11 17:43:00.617
        static _tstring TimestampToString(
            const _tstring& strFormat = _T("%04d-%02d-%02d %02d:%02d:%02d.%d"), 
            int64_t timestamp = 0
        );

        //
        // @brief: 获取文件大小
        // @param: strPath      路径
        // @ret: 文件大小
        unsigned long long GetFileSize(const _tstring& strPath);

    private:

        //
        // @brief: 调整日志文件数量
        // @param: void
        // @ret: void
        void AdjustLogFile();

        //
        // @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: 输出日志到文件
        // @ret: bool 执行结果
        bool OutputToFile();

    private:

        LPTSTR m_lpBuf = nullptr;                   //格式化字符串使用的缓冲指针
        std::mutex  m_Lock;                         //线程安全锁
        std::vector<_tstring> m_logList;            //日志记录
        std::map m_logFileList;  //日志文件记录, 按照时间戳排序
        std::thread m_threadAutoSave;               //自动保存线程对象

        HANDLE m_hEvent = nullptr;                  //通知事件, 使用自动转储的超时等待
        HANDLE m_hFile = INVALID_HANDLE_VALUE;      //文件句柄, 日志文件写入使用
        int64_t  m_nFileTimetamp = 0;               //日志文件时间戳
        _tstring m_strSaveDir;                      //日志存放目录
        _tstring m_strSaveName;                     //日志文件名
        _tstring m_strFilePath;                     //当前日志文件路径
        bool m_bStop = false;                       //停止标记
        bool m_bFirst = false;                      //首次记录日志标记

        DWORD m_MaxFileSize = 0;                    //文件大小阈值(到达阈值则转储到文件)
        DWORD m_MaxTempCount = 0;                   //缓存条目阈值(到达阈值则转储到文件)
        DWORD m_AutoSaveTime = 0;                   //自动保存间隔时间阈值(到达阈值则转储到文件)
        DWORD m_LogTotalSize = 0;                   //日志文件统计
        DWORD m_NextItemSize = 0;                   //下一条目日志大小
    };

    extern CLogHelper global_logger;                //全局静态实例
}

CLogUtils.cpp

#include "CLogUtils.h"
#include 
#include 

namespace CLogUtils
{

#define FORMAT_BUFFER_CH_SIZE           (1024 * 4)       //字符串格式化字符缓冲大小(字符)

    //全局实例构造
    CLogHelper global_logger(
        _T(""), 
        CLogHelper::GetCurrentModuleName(true) + _T("_global"), 
        LOG_FILE_MAX_SIZE, 
        LOG_BUF_MAX_COUNT, 
        AUTO_SAVE_TIME_OUT
    );

    CLogHelper::CLogHelper(
        const _tstring& strDir/* = _T("")*/,
        const _tstring& strName/* = _T("")*/,
        DWORD nFileSize/* = 1024 * 1024*/,
        DWORD nTmpCount/* = 100*/,
        DWORD nIntervalTime/* = 60*/
    ) : 
        m_strSaveDir(strDir),
        m_strSaveName(strName),
        m_MaxFileSize(nFileSize), 
        m_MaxTempCount(nTmpCount), 
        m_AutoSaveTime(nIntervalTime)
    {
        if (m_MaxFileSize < LOG_FILE_MAX_SIZE)
        {
            m_MaxFileSize = LOG_FILE_MAX_SIZE;
        }

        if (m_AutoSaveTime < AUTO_SAVE_TIME_OUT)
        {
            m_AutoSaveTime = AUTO_SAVE_TIME_OUT;
        }

        //默认目录为当前进程目录
        if (m_strSaveDir.empty())
        {
            m_strSaveDir = GetCurrentModuleDir();
        }

        //默认文件名为当前进程名
        if (m_strSaveName.empty())
        {
            m_strSaveName = GetCurrentModuleName(true);
        }

        //目录不存在就创建目录
        if (!IsDirectory(m_strSaveDir))
        {
            CreateDir(m_strSaveDir);
        }

        this->Initialize();
    }

    CLogHelper::~CLogHelper()
    {
        this->Uninitialize();
    }

    bool CLogHelper::Initialize()
    {
        if (nullptr == m_lpBuf)
        {
            m_lpBuf = (LPTSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, FORMAT_BUFFER_CH_SIZE * sizeof(TCHAR));
        }

        if (nullptr == m_hEvent)
        {
            m_hEvent = ::CreateEvent(nullptr, false, false, nullptr);
        }

        //开启一个线程进行自动保存
        m_threadAutoSave = std::move(
            std::thread([this]()
                {
                    //超时或者退出时转储日志到文件
                    while (!m_bStop)
                    {
                        DWORD dwWait = ::WaitForSingleObject(m_hEvent, m_AutoSaveTime);
                        switch (dwWait)
                        {
                        case WAIT_TIMEOUT:
                        case WAIT_OBJECT_0:
                        {
                            std::lock_guard lock(m_Lock);
                            this->OutputToFile();
                            m_logList.clear();
                        }
                        break;
                        default:
                            break;
                        }
                    }
                }
            )
        );

        return nullptr != m_lpBuf;
    }

    void CLogHelper::Uninitialize()
    {
        if (nullptr != m_lpBuf)
        {
            ::HeapFree(::GetProcessHeap(), 0, m_lpBuf);
            m_lpBuf = nullptr;
        }

        if (nullptr != m_hEvent)
        {
            m_bStop = true;
            ::SetEvent(m_hEvent);
        }

        if (m_threadAutoSave.joinable())
        {
            m_threadAutoSave.join();
        }

        if (INVALID_HANDLE_VALUE != m_hFile)
        {
            ::CloseHandle(m_hFile);
            m_hFile = INVALID_HANDLE_VALUE;
        }
    }

    void CLogHelper::Logging(
        LPCTSTR pstrLevel,
        LPCTSTR pstrFile,
        LPCTSTR pstrFunc,
        UINT nLine,
        LPCTSTR pstrFormat,
        ...
    )
    {
        if (nullptr == m_lpBuf)
        {
            return;
        }

        DWORD dwTid = ::GetCurrentThreadId();
        _tstring strLogContent = Format(_T("[%s] [%s] [%0.8X(%d)] [%s : %d] [%s] "),
            GetCurrentTimeString().c_str(),
            pstrLevel,
            dwTid,
            dwTid,
            pstrFile,
            nLine,
            pstrFunc
        );

        //格式化日志内容
        if (nullptr != m_lpBuf)
        {
            int nSize = 0;
            va_list args;

            va_start(args, pstrFormat);
            nSize = _vsntprintf_s(m_lpBuf, FORMAT_BUFFER_CH_SIZE, _TRUNCATE, pstrFormat, args);
            va_end(args);
        }

        //获取单行日志内容 + 固定前缀内容 + 真实内容
        strLogContent += m_lpBuf;
        strLogContent += _T("\r\n");
        m_NextItemSize = (DWORD)(strLogContent.size() * sizeof(TCHAR));

        std::lock_guard lock(m_Lock);

        //首次启动时, 重置大小统计
        if (!m_bFirst)
        {
            InitLogFile();
            AdjustLogFile();
            m_LogTotalSize = (DWORD)GetFileSize(m_strFilePath);
            m_bFirst = true;
        }

        //单个日志文件大小即将达到或超过阈值则输出到文件, 启用新的文件存储
        if ((m_LogTotalSize + m_NextItemSize) >= m_MaxFileSize)
        {
            OutputToFile();
            m_logList.clear();

            ::CloseHandle(m_hFile);
            m_hFile = INVALID_HANDLE_VALUE;
            
            (void)GenerateLogFilePath();
            m_LogTotalSize = (DWORD)GetFileSize(m_strFilePath);
            AdjustLogFile();
        }
        
        //已缓存条目达到阈值则输出到文件
        else if (m_logList.size() >= m_MaxTempCount)
        {
            OutputToFile();
            m_logList.clear();
        }

        //累加统计单个日志文件大小
        m_LogTotalSize += m_NextItemSize;

        //缓存日志
        m_logList.emplace_back(strLogContent);

        return;
    }

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

    void CLogHelper::AdjustLogFile()
    {
        //检查文件数量是否到达阈值, 到达的话删除前面的文件
        if (m_logFileList.size() > HISTORY_FILE_MAX_COUNT)
        {
            size_t nDeleteCount = m_logFileList.size() - HISTORY_FILE_MAX_COUNT;
            size_t nIndex = 0;

            //删除多出的文件
            for (const auto& item : m_logFileList)
            {
                DeleteArchive(item.second);
                nIndex++;
                if (nIndex >= nDeleteCount)
                {
                    break;
                }
            }

            //文件名整体向前移动
            _tstring lastPath;
            for (const auto& item : m_logFileList)
            {
                if (lastPath.empty())
                {
                    ::MoveFileEx(item.second.c_str(), lastPath.c_str(), MOVEFILE_DELAY_UNTIL_REBOOT);
                }

                lastPath = item.second;
            }

            //从日志文件记录列表中删除
            for (size_t i = 0; i < nDeleteCount; i++)
            {
                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_NextItemSize) >= m_MaxFileSize)
            {
                (void)GenerateLogFilePath();
            }
        }
        else
        {
            (void)GenerateLogFilePath();
        }
    }

    void CLogHelper::GenerateLogFilePath()
    {
        //得到日志文件时间戳
        m_nFileTimetamp = GetCurrentTimestamp();

        //得到日志文件路径
        m_strFilePath = Format(_T("%s\\%s_%s.log"),
            m_strSaveDir.c_str(),
            m_strSaveName.c_str(),
            TimestampToString(
                _T("%04d-%02d-%02d_%02d-%02d-%02d-%03d"),
                m_nFileTimetamp).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));
    }

    std::map CLogHelper::GetLogFileList(const _tstring& strDir)
    {
        std::map 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;
    }

    bool CLogHelper::OutputToFile()
    {
        bool bSuccess = false;

        //没有需要写入的日志
        if (m_logList.empty())
        {
            return true;
        }

        if (INVALID_HANDLE_VALUE == m_hFile)
        {
            return false;
        }

        //写入日志
        DWORD dwNumberOfBytesWrite = 0;
        for (const auto& item : m_logList)
        {
            bSuccess = ::WriteFile(m_hFile, item.c_str(), (DWORD)(item.size() * sizeof(TCHAR)), &dwNumberOfBytesWrite, NULL);
            if (!bSuccess)
            {
                break;
            }
        }

        return bSuccess;
    }

    _tstring CLogHelper::Format(LPCTSTR pstrFormat, ...)
    {
        _tstring strResult;

        if (nullptr != m_lpBuf)
        {
            int nSize = 0;
            va_list args;

            va_start(args, pstrFormat);
            nSize = _vsntprintf_s(m_lpBuf, FORMAT_BUFFER_CH_SIZE, _TRUNCATE, pstrFormat, args);
            va_end(args);

            strResult = m_lpBuf;
        }

        return strResult;
    }

    _tstring CLogHelper::GetCurrentTimeString()
    {
        TCHAR szBuf[MAX_PATH] = { 0 };
        SYSTEMTIME st = { 0 };
        (void)::GetLocalTime(&st);

        ::StringCchPrintf(szBuf, _countof(szBuf),
            _T("%04d-%02d-%02d %02d:%02d:%02d.%d"),
            st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds
        );

        return szBuf;
    }

    _tstring CLogHelper::GetCurrentDateString()
    {
        TCHAR szBuf[MAX_PATH] = { 0 };
        SYSTEMTIME st = { 0 };
        (void)::GetLocalTime(&st);

        ::StringCchPrintf(szBuf, _countof(szBuf), _T("%04d-%02d-%02d"), st.wYear, st.wMonth, st.wDay);
        return szBuf;
    }

    int64_t CLogHelper::GetCurrentTimestamp()
    {
        int64_t timeStamp = 0;
        (void)::GetSystemTimeAsFileTime((FILETIME*)&timeStamp);
        return (timeStamp - 116444736000000000) / 10000;
    }

    _tstring CLogHelper::TimestampToString(const _tstring& strFormat, int64_t timestamp)
    {
        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 CLogHelper::GetCurrentModulePath()
    {
        TCHAR szCurPath[MAX_PATH] = { 0 };
        ::GetModuleFileName(NULL, szCurPath, _countof(szCurPath));

        _tstring strResult = szCurPath;
        return strResult;
    }

    _tstring CLogHelper::GetCurrentModuleDir()
    {
        return GetFileDir(GetCurrentModulePath());
    }

    _tstring CLogHelper::GetCurrentModuleName(bool bHasExt/* = true*/)
    {
        return GetFileName(GetCurrentModulePath(), bHasExt);
    }

    _tstring CLogHelper::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 CLogHelper::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 CLogHelper::IsArchive(const _tstring& strPath)
    {
        WIN32_FILE_ATTRIBUTE_DATA attr = { 0 };
        if (!::GetFileAttributesEx(strPath.c_str(), GetFileExInfoStandard, &attr))
        {
            return false;
        }

        return attr.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE;
    }

    bool CLogHelper::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 CLogHelper::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 CLogHelper::DeleteArchive(const _tstring& strPath)
    {
        if (strPath.empty())
        {
            return false;
        }

        return ::DeleteFile(strPath.c_str());
    }

    unsigned long long CLogHelper::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;
    }

}

使用

main.cpp

#include 
#include "CLogUtils.h"
#include 


int _tmain(int argc, LPCTSTR argv[])
{
    while (true)
    {
        uint64_t uBegin = CLogUtils::CLogHelper::GetCurrentTimestamp();
        uint64_t uEnd = 0;
        int nCount = 10000;
        std::cout << " CurrentTimestamp: " << uBegin << std::endl;
        for (int i = 0; i < nCount; i++)
        {
            CLogUtils::LOG_INFO(_T("%d %s"), 1024, _T("FlameCyclone"));
        }
        uEnd = CLogUtils::CLogHelper::GetCurrentTimestamp();
        std::cout << "Repeat " << nCount << " Cost time: " << uEnd - uBegin << std::endl;
        system("pause");
    }

    return 0;
}

x64 Debug效果:

Win32 简单日志实现_第1张图片

Win32 简单日志实现_第2张图片

你可能感兴趣的:(c++,windows,Win32)