C++实现多线程安全日志记录功能

2023-02-19 23:56:02 2486人已围观 29已点赞 5人已收藏

简介在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++日志相关,日志类,日志记录

日志记录结果:

C++日志相关,日志类,日志记录

源码下载
  • 最近更新:   2022-06-21开发环境:   Visual Studio 2015
  • 源码大小:   135.83KB下载次数:  13 

更多为你推荐