简介在C++程序运行过程中,特别是对应高并发的系统,经常需要将程序的运行信息输出到文件中,以便跟踪程序运行结果。本文实现多线程安全日志记录工具类,直接在项目开发中复用即可,感兴趣的朋友可以参考一下。
在C++程序运行过程中,特别是对应高并发的系统,经常需要将程序的运行信息输出到文件中,以便跟踪程序运行结果。
Logger.h
// Logger.h
// 类名:CLogger
// 功能介绍:Win平台日志记录功能,多线程安全
#ifndef _LOGGER_H_
#define _LOGGER_H_
#include <Windows.h>
#include <stdio.h>
#include <string>
class CLogger
{
public:
// 构造函数
// @strLogPath:日志目录,可空
// @strLogName:日志名称,可空
CLogger(const std::string strLogPath = "log", const std::string strLogName = "");
virtual ~CLogger();
public:
// 写日志
void WriteLog(const char *lpcszFormat, ...);
// 获取程序运行路径
static std::string GetAppPathA();
// 格式化字符串
static std::string FormatString(const char *lpcszFormat, ...);
private:
// 写文件操作
void Write(const std::string &strLog);
// 获取当前系统时间
std::string GetTime();
private:
FILE * m_pFileStream; // 写日志文件流
std::string m_strLogPath; // 日志目录
std::string m_strLogName; // 日志名称
std::string m_strLogFilePath; // 日志文件全路径
CRITICAL_SECTION m_cs; // 线程同步的临界区变量
};
#endif
Logger.cpp
// Logger.cpp
#include "Logger.h"
#include <time.h>
#include <stdarg.h>
#include <direct.h>
#include <vector>
#include <Dbghelp.h>
#pragma comment(lib,"Dbghelp.lib")
using std::string;
using std::vector;
CLogger::CLogger(const std::string strLogPath, const std::string strLogName)
:m_strLogPath(strLogPath),
m_strLogName(strLogName)
{
//初始化
m_pFileStream = NULL;
if (m_strLogPath.empty())
{
m_strLogPath = GetAppPathA();
}
if (m_strLogPath[m_strLogPath.length() - 1] != '\\')
{
m_strLogPath.append("\\");
}
// 创建文件夹
MakeSureDirectoryPathExists(m_strLogPath.c_str());
// 创建日志文件
if (m_strLogName.empty())
{
time_t curTime;
time(&curTime);
tm tm1;
localtime_s(&tm1, &curTime);
// 日志的名称如:201601012130.log
//m_strLogName = FormatString("%04d%02d%02d_%02d%02d%02d.log", tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday, tm1.tm_hour, tm1.tm_min, tm1.tm_sec);
m_strLogName = FormatString("%04d%02d%02d.log", tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday);
}
m_strLogFilePath = m_strLogPath.append(m_strLogName);
InitializeCriticalSection(&m_cs);
}
CLogger::~CLogger()
{
// 释放临界区
DeleteCriticalSection(&m_cs);
// 关闭文件流
if (m_pFileStream)
{
fclose(m_pFileStream);
m_pFileStream = NULL;
}
}
// 写日志
void CLogger::WriteLog(const char *lpcszFormat, ...)
{
string strResult;
if (NULL != lpcszFormat)
{
va_list marker = NULL;
va_start(marker, lpcszFormat); // 初始化变量参数
size_t nLength = _vscprintf(lpcszFormat, marker) + 1; // 获取格式化字符串长度
std::vector<char> vBuffer(nLength, '\0'); // 创建用于存储格式化字符串的字符数组
int nWritten = _vsnprintf_s(&vBuffer[0], vBuffer.size(), nLength, lpcszFormat, marker);
if (nWritten > 0)
{
strResult = &vBuffer[0];
}
va_end(marker); // 重置变量参数
}
if (strResult.empty())
{
return;
}
string strLog = "";
strLog.append(GetTime()).append(strResult);
// 写日志文件
Write(strLog);
}
// 获取系统当前时间
string CLogger::GetTime()
{
time_t curTime;
time(&curTime);
tm tm1;
localtime_s(&tm1, &curTime);
// 21:30:00
string strTime = FormatString("%02d:%02d:%02d ", tm1.tm_hour, tm1.tm_min, tm1.tm_sec);
return strTime;
}
// 写文件操作
void CLogger::Write(const string &strLog)
{
try
{
EnterCriticalSection(&m_cs);
// 若文件流没有打开,则重新打开
if (NULL == m_pFileStream)
{
// 以追加的方式打开文件流
fopen_s(&m_pFileStream, m_strLogFilePath.c_str(), "a+");
if (!m_pFileStream)
{
return;
}
}
// 写日志信息到文件流
fprintf(m_pFileStream, "%s\n", strLog.c_str());
fflush(m_pFileStream);
// 关闭文件流
if (m_pFileStream)
{
fclose(m_pFileStream);
m_pFileStream = NULL;
}
LeaveCriticalSection(&m_cs);
}
// 若发生异常,则先离开临界区,防止死锁
catch (...)
{
LeaveCriticalSection(&m_cs);
}
}
string CLogger::GetAppPathA()
{
char szFilePath[MAX_PATH] = { 0 }, szDrive[MAX_PATH] = { 0 }, szDir[MAX_PATH] = { 0 }, szFileName[MAX_PATH] = { 0 }, szExt[MAX_PATH] = { 0 };
GetModuleFileNameA(NULL, szFilePath, sizeof(szFilePath));
_splitpath_s(szFilePath, szDrive, szDir, szFileName, szExt);
string str(szDrive);
str.append(szDir);
return str;
}
string CLogger::FormatString(const char *lpcszFormat, ...)
{
string strResult;
if (NULL != lpcszFormat)
{
va_list marker = NULL;
va_start(marker, lpcszFormat); // 初始化变量参数
size_t nLength = _vscprintf(lpcszFormat, marker) + 1; // 获取格式化字符串长度
std::vector<char> vBuffer(nLength, '\0'); // 创建用于存储格式化字符串的字符数组
int nWritten = _vsnprintf_s(&vBuffer[0], vBuffer.size(), nLength, lpcszFormat, marker);
if (nWritten > 0)
{
strResult = &vBuffer[0];
}
va_end(marker); // 重置变量参数
}
return strResult;
}
为了更方便地使用CLogger类,我们可以写一个工具类Util,核心代码如下:
class Util {
public:
Util();
virtual ~Util();
public:
// 获取工作路径
static CString GetCurrentPath();
public:
static CLogger logger;
};
// 静态变量
CLogger Util::logger = (CLogger::GetAppPathA().append("log\\"));
这样,我们就可以很方便地使用,示例:
// CLoggerTestDlg 消息处理程序
BOOL CLoggerTestDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
Util::logger.WriteLog("XXX 程序开始运行...");
Util::logger.WriteLog("调试信息 %s %d", __FUNCTION__, __LINE__);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
示例程序截图如下:
日志记录结果:
本文向大家介绍一个C++实战项目:基于Duilib的CEF3中C++与JS函数互相调用示例,主要涉及开发技术点为DuiLib界面开发、谷歌浏览器CEF插件开发、C++与JavaScript的相互调用,具有一定的C++实战价值,感兴趣的朋友可以参考一下。
OpenMP是一种用于共享内存并行系统的多线程程序设计方案,本文向大家介绍C++并行计算之OpenMP快速入门,感兴趣的朋友可以参考一下。
本文向大家介绍一个C++实战项目:C++11实现多线程下载操作类,很多时候我们需要同时在网络上下载文件,那么就需要一个类来管理多线程下载,提高下载速度,具有一定的C++实战价值,感兴趣的朋友可以参考一下。
本文向大家介绍一个C++实战项目:C++实现MD5、DES加密,主要涉及MD5加密、DES加解密算法,具有一定的C++实战价值,感兴趣的朋友可以参考一下。
一般好的软件程序是不应该占用太多CPU时间和内存空间的,一般在自己的学习过程中,我们常常忽略这一点,虽然功能实现没有问题,但是程序占用太多CPU时间,这在实际开发中是不允许的,一个好的程序员也应该注意这个问题。本文就CPU使用率问题做一些简单介绍,为开发稳定健壮的程序提供参考。
本文向大家介绍一个C++实战项目:C++制作一个简单的程序依赖DLL打包工具,有时候我们需要对某个程序进行打包,却不知道该程序依赖哪些动态库,这个工具可实现程序的一键打包功能,具有一定的C++实战价值,感兴趣的朋友可以参考一下。