1、导语
Singleton(单例模式)是设计模式中最为简单、最为常见、最容易实现的设计模式;也是最应该熟悉和掌握的设计模式之一。
单例:顾名思义就是在程序运行过程中的唯一变量或者是对象;在面向过程或者是面向过程与面向对象相结合的语言中,唯一的变量可以使用全局变量来实现,但是,在纯面向对象的语言中,只能通过单例模式来实现。
2、单例模式的设计
单例模式的经典结构图:
(此图来自网络)
在实现单例模式中通过维护一个静态的成员变量来记录这个唯一的对象实例,并且把构造函数设计为保护或者是私有的,并且通过一个静态的成员函数获取到static维护的唯一实例对象。
3、单例模式的C++实现
以下实例使用单例模式实现一个写日志类,支持多线程、可变形参表函数以及支持日志级别的设置。
主要提供一下几个接口:
1、设置写日志的级别
2、写关键日志信息
3、写错误日志信息
4、写警告日志信息
5、写一般日志信息
通过以上5个接口轻松使用此类进行项目日志的记录。
一些公共变量的定义:
#ifndef COMMAND_DEFINE_H
#define COMMAND_DEFINE_H
//日志级别的提示信息
static const char * KEYINFOPREFIX = " Key: \n";
static const char * ERRORPREFIX = " Error: \n";
static const char * WARNINGPREFIX = " Warning: \n";
static const char * INFOPREFIX = " Info: \n";
static const int MAX_STR_LEN = 1024;
//日志级别枚举
typedef enum EnumLogLevel
{
LogLevelAll = 0, //所有信息都写日志
LogLevelMid, //写错误、警告信息
LogLevelNormal, //只写错误信息
LogLevelStop //不写日志
};
#endif
日志类的设计:
#ifndef LOGGER_H_
#define LOGGER_H_
#include <Windows.h>
#include <stdio.h>
#include "CommandDefine.h"
/*
* 类名:Logger
* 作用:提供写日志功能,支持多线程,支持可变形参数操作,支持写日志级别的设置
* 接口:SetLogLevel:设置写日志级别
TraceKeyInfo:忽略日志级别,写关键信息
TraceError:写错误信息
TraceWarning:写警告信息
TraceInfo:写一般信息
*/
class Logger
{
private:
//默认构造函数
Logger();
//构造函数
Logger(const char * strLogPath, EnumLogLevel nLogLevel = EnumLogLevel::LogLevelNormal);
public:
static Logger* GetInstance();
static Logger instance;
//析构函数
virtual ~Logger();
public:
//写关键信息
void TraceKeyInfo(const char * strInfo, ...);
//写错误信息
void TraceError(const char* strInfo, ...);
//写警告信息
void TraceWarning(const char * strInfo, ...);
//写一般信息
void TraceInfo(const char * strInfo, ...);
//设置写日志级别
void SetLogLevel(EnumLogLevel nLevel);
private:
//写文件操作
void Trace(const char * strInfo);
//获取当前系统时间
char * GetCurrentTime();
//创建日志文件名称
void GenerateLogName();
//创建日志路径
void CreateLogPath();
private:
//写日志文件流
FILE * m_pFileStream;
//写日志级别
EnumLogLevel m_nLogLevel;
//日志的路径
char m_strLogPath[MAX_STR_LEN];
//日志的名称
char m_strCurLogName[MAX_STR_LEN];
//线程同步的临界区变量
CRITICAL_SECTION m_cs;
};
#endif
日志类的实现:
#include "Logger.h"
#include <imagehlp.h>
#include <time.h>
#include <string.h>
#include <stdarg.h>
#pragma comment(lib, "DbgHelp.lib")
Logger Logger::instance;
//默认构造函数
Logger::Logger()
{
//初始化
memset(m_strLogPath, 0, MAX_STR_LEN);
memset(m_strCurLogName, 0, MAX_STR_LEN);
m_pFileStream = NULL;
//设置默认的写日志级别
m_nLogLevel = EnumLogLevel::LogLevelNormal;
//初始化临界区变量
InitializeCriticalSection(&m_cs);
//创建日志文件名
GenerateLogName();
}
//构造函数
Logger::Logger(const char * strLogPath, EnumLogLevel nLogLevel):m_nLogLevel(nLogLevel)
{
//初始化
m_pFileStream = NULL;
strcpy(m_strLogPath, strLogPath);
InitializeCriticalSection(&m_cs);
CreateLogPath();
GenerateLogName();
}
//析构函数
Logger::~Logger()
{
//释放临界区
DeleteCriticalSection(&m_cs);
//关闭文件流
if(m_pFileStream)
fclose(m_pFileStream);
}
//写关键信息接口
void Logger::TraceKeyInfo(const char * strInfo, ...)
{
if(!strInfo)
return;
char pTemp[MAX_STR_LEN] = {0};
strcpy(pTemp, GetCurrentTime());
strcat(pTemp, KEYINFOPREFIX);
//获取可变形参
va_list arg_ptr = NULL;
va_start(arg_ptr, strInfo);
vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr);
va_end(arg_ptr);
//写日志文件
Trace(pTemp);
arg_ptr = NULL;
}
//写错误信息
void Logger::TraceError(const char* strInfo, ...)
{
//判断当前的写日志级别,若设置为不写日志则函数返回
if(m_nLogLevel >= EnumLogLevel::LogLevelStop)
return;
if(!strInfo)
return;
char pTemp[MAX_STR_LEN] = {0};
strcpy(pTemp, GetCurrentTime());
strcat(pTemp, ERRORPREFIX);
va_list arg_ptr = NULL;
va_start(arg_ptr, strInfo);
vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr);
va_end(arg_ptr);
Trace(pTemp);
arg_ptr = NULL;
}
//写警告信息
void Logger::TraceWarning(const char * strInfo, ...)
{
//判断当前的写日志级别,若设置为只写错误信息则函数返回
if(m_nLogLevel >= EnumLogLevel::LogLevelNormal)
return;
if(!strInfo)
return;
char pTemp[MAX_STR_LEN] = {0};
strcpy(pTemp, GetCurrentTime());
strcat(pTemp, WARNINGPREFIX);
va_list arg_ptr = NULL;
va_start(arg_ptr, strInfo);
vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr);
va_end(arg_ptr);
Trace(pTemp);
arg_ptr = NULL;
}
//写一般信息
void Logger::TraceInfo(const char * strInfo, ...)
{
//判断当前的写日志级别,若设置只写错误和警告信息则函数返回
if(m_nLogLevel >= EnumLogLevel::LogLevelMid)
return;
if(!strInfo)
return;
char pTemp[MAX_STR_LEN] = {0};
strcpy(pTemp, GetCurrentTime());
strcat(pTemp,INFOPREFIX);
va_list arg_ptr = NULL;
va_start(arg_ptr, strInfo);
vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr);
va_end(arg_ptr);
Trace(pTemp);
arg_ptr = NULL;
}
//获取系统当前时间
char * Logger::GetCurrentTime()
{
time_t curTime;
struct tm * pTimeInfo = NULL;
time(&curTime);
pTimeInfo = localtime(&curTime);
char temp[MAX_STR_LEN] = {0};
sprintf(temp, "%02d:%02d:%02d", pTimeInfo->tm_hour, pTimeInfo->tm_min, pTimeInfo->tm_sec);
char * pTemp = temp;
return pTemp;
}
//设置写日志级别
void Logger::SetLogLevel(EnumLogLevel nLevel)
{
m_nLogLevel = nLevel;
}
//写文件操作
void Logger::Trace(const char * strInfo)
{
if(!strInfo)
return;
try
{
//进入临界区
EnterCriticalSection(&m_cs);
//若文件流没有打开,则重新打开
if(!m_pFileStream)
{
char temp[1024] = {0};
strcat(temp, m_strLogPath);
strcat(temp, m_strCurLogName);
m_pFileStream = fopen(temp, "a+");
if(!m_pFileStream)
{
return;
}
}
//写日志信息到文件流
fprintf(m_pFileStream, "%s\n", strInfo);
fflush(m_pFileStream);
//离开临界区
LeaveCriticalSection(&m_cs);
}
//若发生异常,则先离开临界区,防止死锁
catch(...)
{
LeaveCriticalSection(&m_cs);
}
}
//创建日志文件的名称
void Logger::GenerateLogName()
{
time_t curTime;
struct tm * pTimeInfo = NULL;
time(&curTime);
pTimeInfo = localtime(&curTime);
char temp[1024] = {0};
//日志的名称如:2013-01-01.log
sprintf(temp, "%04d-%02d-%02d.log", pTimeInfo->tm_year+1900, pTimeInfo->tm_mon + 1, pTimeInfo->tm_mday);
if(0 != strcmp(m_strCurLogName, temp))
{
strcpy(m_strCurLogName,temp);
if(m_pFileStream)
fclose(m_pFileStream);
char temp[1024] = {0};
strcat(temp, m_strLogPath);
strcat(temp, m_strCurLogName);
//以追加的方式打开文件流
m_pFileStream = fopen(temp, "a+");
}
}
//创建日志文件的路径
void Logger::CreateLogPath()
{
if(0 != strlen(m_strLogPath))
{
strcat(m_strLogPath, "\\");
}
MakeSureDirectoryPathExists(m_strLogPath);
}
Logger* Logger::GetInstance()
{
return &instance;
}
由于单例模式比较简单,就不再作更多阐述,并且次日志类的实现也比较简单。
附:此日志类的一般实现(非单例模式)
http://www.oschina.net/code/snippet_861360_25275