本文讲述使用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
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
#include
#include
#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
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