Win32 简单日志实现

news2024/11/16 7:24:46

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

CLogUtils.h

#pragma once

#include <string>
#include <windows.h>
#include <vector>
#include <map>
#include <mutex>
#include <tchar.h>
#include <thread>

#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<int64_t, _tstring> 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<int64_t, _tstring> 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 <strsafe.h>
#include <tchar.h>

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<std::mutex> 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<std::mutex> 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<std::mutex> 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<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;
    }

    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 <iostream>
#include "CLogUtils.h"
#include <tchar.h>


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效果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1110742.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

04、MySQL-------MyCat实现分库分表

目录 九、MyCat实现分库分表1、分库分表介绍&#xff1a;横向&#xff08;水平&#xff09;拆分**垂直分表**&#xff1a;水平分表&#xff1a;**分库分表** 纵向&#xff08;垂直&#xff09;拆分分表字段选择 2、分库分表操作&#xff1a;1、分析图&#xff1a;2、克隆主从3、…

【Docker从入门到入土 1】Docker管理命令

Part1 一、容器1.1 虚拟化技术的演变1.2 容器简介1.3 为什么要用到容器&#xff1f;1.4 常用的容器引擎1.5 容器和虚拟机 二、Docker2.1 什么是docker&#xff1f;&#xff08;面试题&#xff09;2.2 Docker和虚拟机的区别&#xff08;面试常问&#xff0c;记住&#xff09;2.3…

SLAM 14 notes

4.23 推导 f ( x ) f(x) f(x)在点a处的泰勒展开 f ( x ) ∑ n 0 ∞ f ( n ) a n ! ( x − a ) n f(x) \sum_{n0}^\infty \frac{f^{(n)}a}{n!}(x-a)^n f(x)∑n0∞​n!f(n)a​(x−a)n l n x lnx lnx的n阶导数 l n ( n ) x ( − 1 ) n − 1 ( n − 1 ) ! x n ln^{(n)}x \fr…

C++进阶语法之函数和指针【学习笔记(二)】

文章目录 1、C 函数1.1 函数的定义1.2 函数原型&#xff08;function prototypes&#xff09;1.3 参数&#xff08;parameter&#xff09;——值传递&#xff08;pass by value&#xff09;1.4 重载&#xff08;overloading&#xff09;1.5 函数传参——传递数组&#xff08;ar…

drawio都能做那些事情和模板示例

drawio都能做那些事情和模板示例 你可以使用drawio&#xff0c;并使用drawio提供的扩展模板库和大量的形状库&#xff0c;针对很多不同的工业领域创建不同类型的图表。 针对如下的内容中的所有的图&#xff0c;均可以下载源文件并导入到drawio中再次编辑&#xff08;供学习者…

如何远程通过内网穿透实现微信公众号在本地的完整调试

文章目录 前言1. 配置本地服务器2. 内网穿透2.1 下载安装cpolar内网穿透2.2 创建隧道 3. 测试公网访问4. 固定域名4.1 保留一个二级子域名4.2 配置二级子域名 5. 使用固定二级子域名进行微信开发 前言 在微信公众号开发中&#xff0c;微信要求开发者需要拥有自己的服务器资源来…

什么是兼容性测试? 重要性是什么?

在数字化时代&#xff0c;用户使用各种不同类型的设备和操作系统来访问应用程序和网站。为了确保用户体验的一致性和应用程序的可用性&#xff0c;兼容性测试变得至关重要。本文将深入探讨兼容性测试的概念及重要性。 一、什么是兼容性测试? 兼容性测试是一种软件测试方法&…

混入组件 (mixin)

1 什么是混入以及作用 *混入 (mixin) 提供了一种非常灵活的方式&#xff0c;来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时&#xff0c;所有混入对象的选项将被“混合”进入该组件本身的选项。作用&#xff1a;主要作用是继承和封装…

【python】什么是网络爬虫?

什么是网络爬虫&#xff1f; 网络爬虫是一种自动化程序&#xff0c;用于从互联网上抓取信息。这些信息可以是文本、图像、视频、数据表格等各种形式的数据。爬虫程序通过模拟浏览器的行为&#xff0c;自动访问网页、抓取内容&#xff0c;并将其保存或处理。这对于数据挖掘、搜索…

elasticsearch的docker安装与使用

安装 docker network create elasticdocker pull docker.elastic.co/elasticsearch/elasticsearch:8.10.4# 增加虚拟内存&#xff0c; 此处适用于linux vim /etc/sysctl.conf # 添加 vm.max_map_count262144 # 重新启动 sysctl vm.max_map_countdocker run --name es01 --net …

element中form表单验证

type&#xff1a;限定类型 pattern&#xff1a;添加正则表达式规则 transform&#xff1a;规则验证之前执行的函数 参考Element Ui使用技巧——Form表单的校验规则rules详细说明&#xff1b;element的 form 表单rules详细用法_橙cplvfx-技术踩坑记的技术博客_51CTO博客

【java】【重构二】分模块开发版本锁定以及耦合(打包)实战

目录 一、创建dependencyManagement标签 二、 将需要版本控制的依赖版本进行标签设置 三、将需要版本控制的依赖从各子模块迁移到此处 四、将父模块的依赖版本控制 五、删除子模块的全部版本 1、bocai-web-management模块 2、bocai-utils模块 六、打包 1、确定代码都…

三相电表倍率是什么?

三相电表的倍率是指电能表所匹配的互感器的倍率&#xff0c;也称为变比变流(压)比、电流(压)比&#xff0c;就是缩小的比例。 实际的用电量等于电能表的表值乘以倍率。 三相电能表是用来测量三相交流电路中电源输出(或负载消耗)的电能的电度表。 按用途可分为工业与民用表、电子…

QCA中质蕴项选择问题

一、关于质蕴项的基础知识 &#xff08;一&#xff09;什么是质蕴项&#xff1f; &#xff08;1&#xff09;质蕴项定义 逻辑余项是指缺少经验实例的构型&#xff0c;可以被纳入布尔最小化。——QCA设计原理与应用&#xff1a;超越定性与定记研究的新方法 &#xff08;2&…

黄金眼PAAS化数据服务DIFF测试工具的建设实践 | 京东云技术团队

一、背景介绍 黄金眼PAAS化数据服务是一系列实现相同指标服务协议的数据服务&#xff0c;各个服务间按照所生产指标的主题作划分&#xff0c;比如交易实时服务提供实时交易指标的查询&#xff0c;财务离线服务提供离线财务指标的查询。黄金眼PAAS化数据服务支撑了黄金眼APP、黄…

出租房水电表管理系统:实现节能减排与经济效益双赢

随着我国城市化进程的加速&#xff0c;出租房市场日益繁荣&#xff0c;房东与租户之间的水电费管理问题也日益凸显。传统的出租房水电费管理方式存在诸多不便&#xff0c;如收费不透明、数据不准确、浪费严重等问题。出租房水电表管理系统应运而生&#xff0c;那么今天小编就来…

AI四维彩超预测宝宝长相图片生成流量主小程序开发

AI四维彩超预测宝宝长相图片生成流量主小程序开发 以下是AI四维彩超预测宝宝长相图片生成流量主小程序的功能列表&#xff1a; 用户注册和登录功能&#xff1a;允许用户注册新账户或使用现有账户登录。用户上传宝宝四维彩超照片&#xff1a;用户可以上传宝宝的四维彩超照片&am…

标准模板库--STL

这里写目录标题 STL初始STL诞生STL基本概念STL六大组件STL中的容器、算法、迭代器容器算法迭代器初识vector存放内置数据类型vector存放自定义数据类型vector中嵌套vector 二级目录二级目录二级目录二级目录二级目录二级目录二级目录二级目录 STL初始 STL诞生 STL基本概念 STL…

工业RFID厂家与您分享工业生产制造的应用案例

随着科技的不断进步&#xff0c;RFID技术在工业生产制造领域的应用越来越广泛。AGV/RGV小车运输、立体仓库、生产线、物料跟踪与管理等各行业工业自动化的使用上都有着RFID的身影。为工业生产制造智能化自动化提供了助力。下面&#xff0c;为大家分享RFID技术在工业生产制造上的…