C++ 日志收集

在程序开发时,我们为了排查异常或者收集信息。会经常用到日志、dump文件或者埋点。
这里介绍一个C++写日志相关的方法

这里是日志内容
在这里插入图片描述

相关代码
#ifndef __TMPLOG_H__
#define __TMPLOG_H__

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <string>

// 宽字符转换宏
#define __W(str)    L##str
#define _W(str)     __W(str)

typedef enum LOGGER_LEVEL
{
    TrackLevel      = 0,
    InfoLevel       = 1,
    WarningLevel    = 2,
    ErrorLevel      = 3,
    FatalLevel      = 4,

    // 标识函数进入和退出
    InLevel         = 5,
    OutLevel        = 6
} ENM_LOGGER_LEVEL;

class Log
{
public:
    Log(const std::wstring& strFilePath, const std::wstring& strFunctionName, int iLine);
    ~Log();
public:
    static void Write(ENM_LOGGER_LEVEL logLevel,
        const std::wstring& strFilePath,
        const std::wstring& strFunctionName,
        int iLine,
        LPCWSTR pszFormat, ...);
private:
    static FILE* _CreateFile();
    static std::wstring _GetModuleName();
    static std::wstring _GetShortFuncName(const std::wstring& strFunctionName);
    static std::wstring _GetDateTimeString();
    static std::wstring _GetDWORString(DWORD dwVal);
    static std::wstring _GetLevel(ENM_LOGGER_LEVEL logLevel);
private:
    const std::wstring      m_strFilePath;
    const std::wstring      m_strFunctionName;
    int                     m_iLine;
};

#if defined(USE_LOG)
    #define LTRACE(formatstr, ...)      Log::Write(TrackLevel,    _W(__FILE__), _W(__FUNCTION__), __LINE__, formatstr, ##__VA_ARGS__)
    #define LINFO(formatstr, ...)       Log::Write(InfoLevel,     _W(__FILE__), _W(__FUNCTION__), __LINE__, formatstr, ##__VA_ARGS__)

    #define LWARNING(formatstr, ...)                                                                            \
            do                                                                                                  \
            {                                                                                                   \
                Log::Write(WarningLevel, _W(__FILE__), _W(__FUNCTION__), __LINE__, formatstr, ##__VA_ARGS__);   \
                assert(FALSE);                                                                               \
            } while (false)

    #define LERROR(formatstr, ...)                                                                              \
            do                                                                                                  \
            {                                                                                                   \
                Log::Write(ErrorLevel, _W(__FILE__), _W(__FUNCTION__), __LINE__, formatstr, ##__VA_ARGS__);     \
                assert(FALSE);                                                                               \
            } while (false)

    #define LFATAL(formatstr, ...)                                                                              \
            do                                                                                                  \
            {                                                                                                   \
                Log::Write(FatalLevel, _W(__FILE__), _W(__FUNCTION__), __LINE__, formatstr, ##__VA_ARGS__);     \
                assert(FALSE);                                                                               \
            } while (false)

    #define LOGOUT(level, formatstr, ...)                                                                       \
            do                                                                                                  \
            {                                                                                                   \
                Log::Write(level, _W(__FILE__), _W(__FUNCTION__), __LINE__, formatstr, ##__VA_ARGS__)           \
                if (WarningLevel == level || ErrorLevel == level || FatalLevel == level)                        \
                {                                                                                               \
                    assert(FALSE);                                                                           \
                }                                                                                               \
            } while (false)

    #define LOGGER                      Log __tmp_logger__(_W(__FILE__), _W(__FUNCTION__), __LINE__)
#else
    #define LTRACE(formatstr, ...)
    #define LINFO(formatstr, ...)
    #define LWARNING(formatstr, ...)
    #define LERROR(formatstr, ...)
    #define LFATAL(formatstr, ...)
    #define LOGOUT(level, formatstr, ...)

    #define LOGGER
#endif

#endif /* __TMPLOG_H__ */

#include "log.h"

#include <assert.h>

static FILE* file = NULL;
void onExitClean()
{
    if (NULL != file)
    {
        ::fclose(file);
        file = NULL;
    }
}

Log::Log(const std::wstring& strFilePath, const std::wstring& strFunctionName, int iLine)
    : m_strFilePath(strFilePath)
    , m_strFunctionName(strFunctionName)
    , m_iLine(iLine)
{
    Log::Write(InLevel, m_strFilePath, m_strFunctionName, m_iLine, _W(""));
}

Log::~Log()
{
    Log::Write(OutLevel, m_strFilePath, m_strFunctionName, m_iLine, _W(""));
}

void Log::Write(ENM_LOGGER_LEVEL logLevel,
    const std::wstring& strFilePath,
    const std::wstring& strFunctionName,
    int iLine,
    LPCWSTR pszFormat, ...)
{
    size_t iLastSlash = strFilePath.rfind(_W('\\'));
    std::wstring strFileName = (iLastSlash < strFilePath.size() - 1 ? strFilePath.substr(iLastSlash + 1) : strFilePath);

    std::wstring strModuleName = _W("[#");
    strModuleName.append(_GetModuleName());
    strModuleName.append(_W("#]"));

    std::wstring strPidAndTid = _W("[");
    strPidAndTid.append(_GetDWORString(::GetCurrentProcessId()));
    strPidAndTid.append(_W(", "));
    strPidAndTid.append(_GetDWORString(::GetCurrentThreadId()));
    strPidAndTid.append(_W("]"));

    std::wstring strDateTime = _GetDateTimeString();

    if (OutLevel != logLevel)
    {
        strFileName.append(_W(":"));
        strFileName.append(_GetDWORString(static_cast<DWORD>(iLine)));
    }

    const size_t nLogBufferLength = 14 * 4096;
    WCHAR szLog[nLogBufferLength] = {0};
    int nCount = ::swprintf_s(szLog, nLogBufferLength, _W("%-20s %-14s %-24s %-8s %-48s %-32s [")
        , strModuleName.c_str()
        , strPidAndTid.c_str()
        , strDateTime.c_str()
        , _GetLevel(logLevel).c_str()
        , _GetShortFuncName(strFunctionName).c_str()
        , strFileName.c_str());

    if (nCount < 0 || static_cast<size_t>(nCount) >= nLogBufferLength)
    {
        assert(FALSE);
        return;
    }

    va_list ap;
    va_start(ap, pszFormat);
    nCount += ::vswprintf_s(szLog + static_cast<size_t>(nCount), nLogBufferLength - static_cast<size_t>(nCount), pszFormat, ap);
    va_end(ap);

    if (nCount < 0 || static_cast<size_t>(nCount) >= nLogBufferLength)
    {
        assert(FALSE);
        return;
    }

    nCount += ::swprintf_s(szLog + static_cast<size_t>(nCount), nLogBufferLength - static_cast<size_t>(nCount), _W("]\n"));

    ::OutputDebugStringW(szLog);

    if (NULL == file)
    {
        std::atexit(onExitClean);
        file = _CreateFile();
    }

    if (NULL != file)
    {
        ::fputws(szLog, file);
    }
}

FILE* Log::_CreateFile()
{
    WCHAR fullPath[MAX_PATH] = { 0 };
    DWORD pathLength = ::GetModuleFileNameW(NULL, fullPath, MAX_PATH);
    if (0 == pathLength)
    {
        ::OutputDebugStringW(L"GetModuleFileNameW failed.\n");

        return NULL;
    }

    LPCWSTR lastSlash = ::wcsrchr(fullPath, L'\\');
    if (NULL == lastSlash)
    {
        ::OutputDebugStringW(L"wcsrchr return wrong.\n");

        return NULL;
    }

    std::wstring parentDir(fullPath, lastSlash - fullPath + 1);
    parentDir.append(L"demolog\\");
    BOOL bRet = ::CreateDirectoryW(parentDir.c_str(), NULL);
    if (FALSE == bRet && ERROR_ALREADY_EXISTS != ::GetLastError())
    {
        return NULL;
    }

    WCHAR filePath[MAX_PATH] = { 0 };
    for (int i = 0; ; ++i)  // 避免同名
    {
        SYSTEMTIME sys_time = { 0 };
        ::GetLocalTime(&sys_time);

        ::swprintf_s(filePath, _countof(filePath) - 1, L"%s%04u_%02u_%02u_%02u_%02u_%02u_%d.log"
            , parentDir.c_str()
            , sys_time.wYear, sys_time.wMonth, sys_time.wDay
            , sys_time.wHour, sys_time.wMinute, sys_time.wSecond, i);

        if (::GetFileAttributes(filePath) == INVALID_FILE_ATTRIBUTES)
        {
            break;
        }
    }

    FILE* file = NULL;
    ::_wfopen_s(&file, filePath, L"wb+");

    return file;
}

std::wstring Log::_GetModuleName()
{
    // 获取当前代码所在模块的路径
    MEMORY_BASIC_INFORMATION stMemInfo = {0};
    ::VirtualQuery((PVOID)_GetModuleName, &stMemInfo, sizeof(MEMORY_BASIC_INFORMATION));

    HMODULE hModule = (HMODULE)stMemInfo.AllocationBase;

    WCHAR szFullPath[MAX_PATH] = {0};
    ::GetModuleFileNameW(hModule, szFullPath, MAX_PATH);

    LPWSTR lpszLastSlash = ::wcsrchr(szFullPath, _W('\\'));
    return (NULL == lpszLastSlash ? _W("") : lpszLastSlash + 1);
}

std::wstring Log::_GetShortFuncName(const std::wstring& strFunctionName)
{
    // 只包含类名和函数名

    if (strFunctionName.size() <= 2)
    {
        return strFunctionName;
    }

    std::size_t index1 = strFunctionName.rfind(_W("::"));
    if (std::wstring::npos == index1)
    {
        return strFunctionName;
    }
    else
    {
        std::size_t index2 = strFunctionName.rfind(_W("::"), 0 == index1 ? 0 : index1 - 1);
        return (std::wstring::npos == index2 ? strFunctionName : strFunctionName.substr(index2 + 2));
    }
}

std::wstring Log::_GetDateTimeString()
{
    SYSTEMTIME stTime = {0};
    ::GetLocalTime(&stTime);

    WCHAR szTmp[32] = {0};
    ::swprintf_s(szTmp, _countof(szTmp), _W("[%02u-%02u %02u:%02u:%02u.%u]"), stTime.wMonth, stTime.wDay
        , stTime.wHour, stTime.wMinute, stTime.wSecond, stTime.wMilliseconds);

    return szTmp;
}

std::wstring Log::_GetDWORString(DWORD dwVal)
{
    WCHAR szTmp[16] = {0};
    ::swprintf_s(szTmp, _countof(szTmp), _W("%ld"), dwVal);

    return szTmp;
}

std::wstring Log::_GetLevel(ENM_LOGGER_LEVEL logLevel)
{
    if (InLevel == logLevel)
    {
        return (_W("[IN]"));
    }
    else if (OutLevel == logLevel)
    {
        return (_W("[OUT]"));
    }
    else if (TrackLevel == logLevel)
    {
        return (_W("[T]"));
    }
    else if (InfoLevel == logLevel)
    {
        return (_W("[I]"));
    }
    else if (WarningLevel == logLevel)
    {
        return (_W("[W]"));
    }
    else if (ErrorLevel == logLevel)
    {
        return (_W("[E]"));
    }
    else if (FatalLevel == logLevel)
    {
        return (_W("[F]"));
    }
    else
    {
        assert(FALSE);
        return _W("");
    }
}

//示例(LINFO)
//1.
LINFO(L"测试log");
//2.
LPCWSTR test = L"测试log";
LINFO(test);
//3.
QString  test = QStringLiteral("测试log");
LINFO(L"%s", test.toStdWString().c_str());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值