基于单例模式的日志输出(C++)

描述如下:

  在软件运行中,系统一般会采用一个持久化的日志系统来记录运行情况。
  本题你需要实现一个简易的 logger 日志类,这个类主要负责辅助调试,将日志信息输出到一个日志文件中。

比赛预期的输出为同一目录下的 shiyanloulogger.log日志文件,文件的内容如下:

shiyanlou logger test start[2017.07.20 16:13:02 Thursday]
info info info[2017.07.20 16:13:02 Thursday]
shiyanlou logger test end[2017.07.20 16:13:02 Thursday]

程序的主要目标为:
  1. 实现 Logger 简单的单例实现(自动垃圾回收)
  2. 日志文件需要使用追加的方式添加日志

C++单例模式也称为单件模式、单子模式。使用单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。有很多地方需要这样的功能模块,如系统的日志输出等。[参考1]

在具体实现的时候需要考虑两个问题:
  1. 单例模式的实现问题
  这样的例子有很多,[参考1],单例模式的好处就是保证这个类的实例是唯一的,从而不会产生冲突。
  2. 日志文件流的生命周期
  在进行日志输出的时候,当日志对象创建的时候,日志文件流就应该已经创建。当日志对象结束的时候,文件流也应当关闭。这样不用每次写一行日志的时候就要重新生成打开关闭文件,效率较高。
  3. 数据释放问题
  可以在日志类里面创建一个私有类,在程序运行结束时,自动调用私有类的析构函数,在析构函数中进行释放当前日志对象和关闭文件等操作。这样,不需要在外部函数中进行释放,不用关心日志对象的释放问题。
  最终日志对象的头文件easyLogger.h如下:

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <memory>
#include <ctime>
#include <iostream>
#include <fstream>

using namespace std;

class easyLogger
{
public:
	static easyLogger *myInst()
	{
		if (NULL == _instance)
		{
			_instance = new  easyLogger();
			// write to easyLogger.log
			_instance->ofs.open("logger.log",ios::app);
		}
		return _instance;
	}

	void Log(const string& logInfo);

private:
	easyLogger(void) {}
	virtual ~easyLogger(void) {}
	friend class auto_ptr<easyLogger>;
	static easyLogger *_instance;
	char tmp[100];
	ofstream ofs;// 输出文件流
	class CGarbo // 它的唯一工作就是在析构函数中删除easyLogger的实例
	{
	public:
		~CGarbo()
		{
			if(easyLogger::_instance)
			{
				easyLogger::_instance->ofs.close();//关闭文件流
				delete easyLogger::_instance;
				easyLogger::_instance = NULL;
			}
		}
	};
	static CGarbo Garbo;
};
easyLogger *easyLogger::_instance = NULL;
void easyLogger::Log(const string& logInfo)
{
	time_t t = time(0);
	strftime(tmp, sizeof(tmp), "[%Y.%m.%d %X %A]", localtime(&t));
	ofs << logInfo.c_str()<<tmp<<endl;
}

接下来我们进行日志写入的测试,代码如下:

#include "stdafx.h"
#include <iostream>
#include <ctime>
#include <cstdlib>
#include "easyLogger.h"

#define NUM 100000  // 单次写入的日志条数

using namespace std;

char logdata[NUM][100];

double random(double start,double end)
{
	return start+(end-start)*rand()/(RAND_MAX+1.0);
}

// 测试日志写入的效率
int main()
{
    // 以当前的时间作为种子,这样每次生成的随机数都是不一样的,否则每次随机数都是一样的
	// srand(unsigned(time(0)));
	clock_t t1,t2;
	int sum_time =0;
	// 测试10次
	for (int k=0;k<10;k++)
	{
		// 生成随机的数据
		t1 = clock();
		for(int i = 0;i<NUM;++i)
		{
			for(int j =0;j<100-1;++j)
				logdata[i][j] = rand()%74+48;
			logdata[i][99] = '\0';
		}
		t2 = clock();
		cout<<"生成日志数据"<<NUM<<"条,耗费时间:"<<(t2-t1)<<"ms"<<endl;

		// 进行日志写入
		t1 = clock();
		for(int i = 0;i<NUM;++i)
			easyLogger::myInst()->Log(logdata[i]);
		t2 = clock();
		cout<<"写入日志数据"<<NUM<<"条,耗费时间:"<<(t2-t1)<<"ms"<<endl;
		sum_time += t2-t1;
	}
	cout<<"平均每次花费时间为:"<<sum_time/10<<"ms"<<endl;
	system("pause");
	return 0;
}

最终在本平台(windows 10 64位+VS2010 +win32 debug模式下)运行结果如下:
log_test_resulut
  程序其实还有需要改进的地方,比如说多线程环境下是否还能保证类的实例是唯一的、如何指定输出路径、以及如何能够进一步提升大量数据情况下的写入性能等等。
[楼赛]:https://www.shiyanlou.com/contests/lou12/challenges
[参考1]:http://www.cnblogs.com/my_life/articles/2356709.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值