使用C++实现一个简单的日志系统

一、概述

    本文讲述使用C++在windows平台实现一个简单的日志系统的方法。该日志系统的特点如下:

1.支持类似c语言printf风格的输出方式,支持不定参数。

2.支持将日志输出到屏幕和文件中。

3.支持打印系统时间。

4.线程安全,各个线程都能同时写出日志。

二、实现方式

实现代码如下:

AfMutex.h

#ifndef _OSAPI_MUTEX_H
#define _OSAPI_MUTEX_H

struct AfMutex_Priv;

//互斥锁相关的类
class AfMutex
{
public:
	AfMutex();
	~AfMutex();

	int Lock();
	int TryLock();
	void Unlock();

private:
	int Init(); 

private:
	AfMutex_Priv *m_Priv;

};

AfMutex_Win32.cpp

#include "AfMutex.h"

#ifdef _WIN32
#include <windows.h>

struct AfMutex_Priv
{
	HANDLE hMutex;
};

AfMutex::AfMutex()
:m_Priv(NULL)
{
	Init();
}

AfMutex::~AfMutex()
{
	if(m_Priv) 
	{
		CloseHandle(m_Priv->hMutex);
		delete m_Priv;
	}
}

int AfMutex::Init()
{
	m_Priv = new AfMutex_Priv;
	m_Priv->hMutex = CreateMutex(NULL, true, NULL);
	if(m_Priv->hMutex == NULL)
	{
		delete m_Priv;
		m_Priv = NULL;
		return -1;
	}

	ReleaseMutex(m_Priv->hMutex);
	return 0;
}

int AfMutex::Lock()
{
	if(!m_Priv) return -1;

	WaitForSingleObject(m_Priv->hMutex, INFINITE);
	return 0;
}

int AfMutex::TryLock()
{
	if(!m_Priv) return -1;

	DWORD  ret = WaitForSingleObject(m_Priv->hMutex, 1);	
	if( ret == WAIT_OBJECT_0)
	{
		return 0; // success
	}
	if( ret == WAIT_TIMEOUT)
	{
		return -1; // timeout
	}
	return -1;
}

void AfMutex::Unlock()
{
	if(!m_Priv) return;
	ReleaseMutex(m_Priv->hMutex);
}

#endif

Clog.h

#ifndef CLOG_H
#define CLOG_H

#include <stdio.h>
#include <ctime>
#include <stdarg.h>
#include "AfMutex.h"

//日志相关的类
class CLog
{
public:
	CLog(void)
	{
		;
	}
public:
	static CLog *i()                    //单例模式
	{
		static CLog c_log;
		return &c_log;
	}
	void InitLog(const char *log_dir)   //打开log_dir目录下的日志文件,如果该文件不存在,则创建它
	{
		char filePath[100] = { 0 };
		time_t now = time(0);
		tm *ltm = localtime(&now);
		sprintf(filePath, "%s/%04d-%02d-%02d_%02d%02d%02d.log", log_dir, 1900 + (ltm->tm_year), 1 + (ltm->tm_mon), ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);

		m_fp = fopen(filePath, "a");     //以附加的方式打开只写文件filePath。使用fopen打开的文件是共享的,即使不关闭文件指针,其它文件也可以访问该文件
		if (NULL == m_fp)
		{
			printf("can not open log file: %s/n", filePath);
		}
	}
	void WriteLog(const char *msg ...)   //将信息msg写入到文件指针为m_fp的日志文件中,并打印到控制台上(支持ANSI,格式化输出)
	{
		m_mutex.Lock();

		char buf[1024] = { 0 };
		char t_buf[50] = { 0 };
		time_t now = time(0);
		tm *ltm = localtime(&now);

		sprintf(t_buf, "<%d年%d月%d日%d时%d分%d秒>:", 1900 + (ltm->tm_year), 1 + (ltm->tm_mon), ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec, msg);
		printf("%s", t_buf);
		fprintf(m_fp, "%s", t_buf);

		va_list vp;
		va_start(vp, msg);
		vfprintf(stdout, msg, vp);
		vfprintf(m_fp, msg, vp);
		va_end(vp);
		fflush(m_fp);
		
		m_mutex.Unlock();
	}
private:
	FILE *m_fp;               //日志文件的文件指针
	AfMutex m_mutex;          //互斥锁
};

#endif

main.cpp

#include "Clog.h"
#include <stdio.h>

using namespace std;


int main()
{
	CLog::i()->InitLog("log");
	char *buf = "xiaoming";
	CLog::i()->WriteLog("name:%s\n", buf);
	return 0;
}

三、运行效果

我们在vs(比如vs2015)中创建一个控制台项目,添加上述代码,在项目目录下新建一个log目录,如下图所示:

然后编译、运行,我们可以发现在控制台中已经可以将日志的信息打印出来了。

在log目录下也根据启动程序的时间生成对应的日志文件了。

四、总结

    本文演示的日志只是一个具有最基本功能的日志。实际在生产环境中使用日志还要考虑很多问题,比如日志的性能,日志是否支持程序故障异常退出时及时保存,是否支持准确的源码溯源,日志输出的级别;如果是在服务器中,还要考虑日志是否支持轮替(服务器不出故障是不重启的,半年一年的日志放到一个文件会导致文件过大)等等。所以在项目中,我们还是优先考虑使用现成的开源库(比如log4cpp、boost.log、spdlog、glog、zlog等),尤其是是公司项目,而不是自己实现日志,自己造的轮子可能不但不完善,还不好用。

五、参考链接

本文使用的互斥锁的代码部分参考了邵发老师的代码,链接如下:

https://download.csdn.net/download/iamshaofa/4303875

发布了54 篇原创文章 · 获赞 55 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/u014552102/article/details/103517018