在程序开发时,我们为了排查异常或者收集信息。会经常用到日志、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());