读书人

C++写日记

发布时间: 2013-05-02 09:39:29 作者: rapoo

C++写日志
在linux下调试比较蛋疼,因此需要打许多的日志。
尝试写了一个写日志文件的类,不知道性能能不能达到项目的需求?各位看看,欢迎指正~


/*
* CPsvrtsLog.h
*
* Created on: 2013-4-19
* Author: lizp
*/

#ifndef CPSVRTSLOG_H_
#define CPSVRTSLOG_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <boost/pool/pool.hpp>
#include <stdarg.h>
#include <time.h>
#include <sys/timeb.h>
#include <queue>

#ifdef WIN32
#include <windows.h>
#include <io.h>
#include <process.h>

#define PUINT64 LONGLONG
#else
#include <unistd.h>
#include <sys/time.h>
#include <pthread.h>
#include <inttypes.h>
#include <string.h>

#define PUINT64 uint64_t
#define CRITICAL_SECTION pthread_mutex_t
#define _vsnprintf vsnprintf
#endif

#define MAXLOGSIZE 20000000
#define ARRSIZE(x) (sizeof(x)/sizeof(x[0]))
#define CACULATE_REALSIZE(nSize) ((0 == (nSize % 128)) ?\
(nSize/128) : ((nSize/128) + 1))

class CPsvrtsLog {
public:
CPsvrtsLog(std::string strLogFileDir=""/*日志文件存储路径*/);
virtual ~CPsvrtsLog();

/* 日志读写接口 */
public:
/*
* DEBUG调试输出
* */
void Log_Debug(const char *pszFmt, ...);

/*
* 事件记录输出
* */
void Log_Info(const char *pszFmt, ...);

/*
* 程序警告输出
* */
void Log_Warning(const char *pszFmt, ...);

/*
* 程序错误记录
* */
void Log_Error(const char *pszFmt, ...);

protected:
CRITICAL_SECTION * m_pCsLog; // 异步读写加锁
std::string m_strFileDir; // 文件存储路径
char m_szLog[512]; // 一次日志文件写入的数据输出
boost::pool<> * m_pMemoryPool; // boost内存池,用于申请小块的数据缓存内存
std::queue<char *> m_queueFileBuffer; // 文件内存写入

// 处理文件写入线程
#ifdef WIN32
HANDLE m_hThreadHandle; // 线程句柄


unsigned m_uThreadID; // 线程ID
#else
pthread_t m_threadID; // 线程句柄
#endif // WIN32

bool m_bIsWorking; // 线程工作标记

protected:

class TSCRITICAL_SECTION
{
friend class CPsvrtsLog;
protected:
#ifdef WIN32
void Lock(CRITICAL_SECTION *l)
{
if (l)
EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l)
{
if (l)
LeaveCriticalSection(l);
}
#else
void Lock(CRITICAL_SECTION *l)
{
if (l)
pthread_mutex_lock(l);
}
void Unlock(CRITICAL_SECTION *l)
{
if (l)
pthread_mutex_unlock(l);
}
#endif
protected:
CRITICAL_SECTION * m_pCsLog;
protected:
TSCRITICAL_SECTION(CRITICAL_SECTION * pCsLog)
{
m_pCsLog = pCsLog;
if (m_pCsLog)
Lock(pCsLog);
}
virtual ~TSCRITICAL_SECTION()
{
if (m_pCsLog)
Unlock(m_pCsLog);
}
};

/*
* 日志输出
* */
void Log_Printf(const char *pszFmt,va_list argp)
{
/* 提取用户构造的字符串 */
if ((NULL == pszFmt) || (0 == pszFmt[0])) { return; }
memset(m_szLog, 0, 512); int n = 1;
m_szLog[0] = '#';
char * pAddrB = m_szLog + 1;
if (-1 != _vsnprintf(pAddrB, ARRSIZE(m_szLog)-n, pszFmt, argp))
{
n = strlen(m_szLog);
pAddrB = m_szLog + n;
}
else
{
m_szLog[1] = '\0';
}

/* 构造时间前缀 */
sprintf(pAddrB, "\t time: ");
n = strlen(m_szLog);
pAddrB = m_szLog + n;

/* 添加时间结点 */
struct tm *now;
struct timeb tb;
ftime(&tb);
now = localtime(&tb.time);
sprintf(pAddrB,"%04d-%02d-%02d | %02d:%02d:%02d:%03d",
now->tm_year+1900, now->tm_mon+1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec, tb.millitm);

/* 输出 */
printf("%s\n", m_szLog);
}

/*
* 日志输出
* */
void Log_pushFile()
{
char * pszBuffCache = (char *)m_pMemoryPool->ordered_malloc(CACULATE_REALSIZE(strlen(m_szLog)));
memcpy(pszBuffCache, m_szLog, strlen(m_szLog)+1);
m_queueFileBuffer.push(pszBuffCache);
}

/*
* 访问文件写入节点
*/
char * Log_popFile()
{
TSCRITICAL_SECTION lock(m_pCsLog);
char * pBufferF = NULL;


if (!m_queueFileBuffer.empty())
{
pBufferF = m_queueFileBuffer.front();
m_queueFileBuffer.pop();
}

return pBufferF;
}

/*
* 文件写线程(每个100条记录,写入一次文件fflush)
*/
#ifdef WIN32
static unsigned int __stdcall _LogFileWriteThrd(void* Param);
#else
static void * _LogFileWriteThrd(void* Param);
#endif // WIN32
};

#endif /* CPSVRTSLOG_H_ */




/*
* CPsvrtsLog.cpp
*
* Created on: 2013-4-19
* Author: lizp
*/

#include "PsvrtsLog.h"
#include <map>

#ifdef WIN32
#include <imagehlp.h>
#include <shlobj.h>
#include <io.h>
#pragma comment(lib, "imagehlp.lib")
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <cstddef>
#endif

#define LOG_PRINTF() {\
va_list argp;\
va_start(argp, pszFmt);\
Log_Printf(pszFmt, argp);\
va_end(argp);}

CPsvrtsLog::CPsvrtsLog(std::string strLogFileDir/*=""*//*日志文件存储路径*/)
: m_strFileDir(strLogFileDir)
{
// TODO Auto-generated constructor stub
/* 检查目录,不存在则新建 */
#ifdef WIN32
/* 将'/'替换成'\' */
std::string::size_type pos = 0;
std::string::size_type srclen = m_strFileDir.size();
while(std::string::npos != (pos = m_strFileDir.find('/', pos)))
{
m_strFileDir.replace(pos, 1, "\\");
pos += 1;
}
MakeSureDirectoryPathExists(m_strFileDir.c_str());
#else
/* 将'\'替换成'/' */
std::string::size_type pos = 0;
std::string::size_type srclen = m_strFileDir.size();
while(std::string::npos != (pos = m_strFileDir.find('\\', pos)))
{
m_strFileDir.replace(pos, 1, "/");
pos += 1;
}
if(NULL == opendir(m_strFileDir.c_str()))
{ mkdir(m_strFileDir.c_str(), 0775); }
#endif

m_pCsLog = new CRITICAL_SECTION;

m_pMemoryPool = new boost::pool<> (128);

#ifdef WIN32
InitializeCriticalSection(m_pCsLog);
#else
pthread_mutex_init(m_pCsLog, NULL);
#endif

memset(m_szLog, 0, 512);

#ifdef WIN32
m_hThreadHandle = NULL;
m_uThreadID = 0;
#else
m_threadID = 0;
#endif // WIN32

m_bIsWorking = false;

#ifdef WIN32
m_hThreadHandle = (HANDLE)_beginthreadex(0, 0, _LogFileWriteThrd, this, 0, &m_uThreadID);
if (NULL == m_hThreadHandle) { abort(); }
#else
if (0 != pthread_create(&m_threadID, NULL, _LogFileWriteThrd, this)) { abort(); }
#endif // WIN32
}

CPsvrtsLog::~CPsvrtsLog() {
// TODO Auto-generated destructor stub
#ifdef WIN32
DeleteCriticalSection(m_pCsLog);


#else
pthread_mutex_destroy(m_pCsLog);
#endif

m_bIsWorking = false;

#ifdef WIN32
if (NULL != m_hThreadHandle)
{
WaitForSingleObject(m_hThreadHandle, INFINITE);
CloseHandle(m_hThreadHandle);
m_hThreadHandle = NULL;
}
#else
if (0 != m_threadID)
{
pthread_join(m_threadID, NULL);
m_threadID = 0;
}
#endif // WIN32

/* 清空所有申请的内存 */
while (!m_queueFileBuffer.empty())
{
m_pMemoryPool->ordered_free(m_queueFileBuffer.front());
m_queueFileBuffer.pop();
}

delete m_pMemoryPool;
}

/*
* 程序错误记录
*/
void CPsvrtsLog::Log_Error(const char *pszFmt, ...)
{
TSCRITICAL_SECTION lock(m_pCsLog);
LOG_PRINTF();
Log_pushFile();
}

/*
* DEBUG调试输出
*/
void CPsvrtsLog::Log_Debug(const char *pszFmt, ...)
{
TSCRITICAL_SECTION lock(m_pCsLog);
LOG_PRINTF();
}

/*
* 程序警告输出
*/
void CPsvrtsLog::Log_Warning(const char *pszFmt, ...)
{
TSCRITICAL_SECTION lock(m_pCsLog);
LOG_PRINTF();
Log_pushFile();
}

/*
* 事件记录输出
*/
void CPsvrtsLog::Log_Info(const char *pszFmt, ...)
{
TSCRITICAL_SECTION lock(m_pCsLog);
LOG_PRINTF();
Log_pushFile();
}

/*
* 文件写入线程(每个100条记录,写入一次文件fflush)
* */
#ifdef WIN32
unsigned int CPsvrtsLog::_LogFileWriteThrd(void* Param)
#else
void * CPsvrtsLog::_LogFileWriteThrd(void* Param)
#endif // WIN32
{
CPsvrtsLog * pLogObj = (CPsvrtsLog *)Param;
pLogObj->m_bIsWorking = true;
char * pFileBuffer = NULL;
FILE * fileLog = NULL; // 文件句柄

/* 文件名初始化 */
struct tm *now;
struct timeb tb;
std::string strFilePathName = "";
int nFlushIndex = 0; // 文件日志记录计数

#define MAKE_FILEPATHNAME() { \
ftime(&tb); now = localtime(&tb.time);\
char szFileName[128] = {0};\
sprintf(szFileName,"%04d_%02d_%02d_%02d_%02d.log", now->tm_year+1900, now->tm_mon+1, now->tm_mday, now->tm_hour, now->tm_min);\
strFilePathName = pLogObj->m_strFileDir + szFileName;\
}

#define FILE_WRITEANDCLOSE() { \
if (fileLog) /* 写入文件 */\
{ fflush(fileLog); fclose(fileLog); fileLog = NULL; } }\

/* 一直查询缓冲队列,写入文件 */
while (pLogObj->m_bIsWorking)
{
if (NULL != (pFileBuffer = pLogObj->Log_popFile()))
{
if (NULL == fileLog) // 如果文件还未打开,则打开文件
{
MAKE_FILEPATHNAME();
fileLog = fopen(strFilePathName.c_str(), "ab"); // 打开文件,文件名字为日期时间组合,精确到分钟
if (NULL == fileLog) // 打开失败,则不写文件


{
#ifdef WIN32
Sleep(10);
#else
usleep(10*1000);
#endif
continue;
}// end ;
}

/* 写入文件,如果文件过大,则新建文件写入 */
fprintf(fileLog, "%s\r\n", pFileBuffer);
if (ftell(fileLog) >= MAXLOGSIZE)
{
FILE_WRITEANDCLOSE();
}
else if (nFlushIndex++ >= 100)
{
nFlushIndex = 0;
fflush(fileLog);
}
} // end: if (NULL != (pFileBuffer = pLogObj->Log_popFile()))
else if (0 < nFlushIndex)// 比较空闲,直接写入文件
{
fflush(fileLog);
#ifdef WIN32
Sleep(10);
#else
usleep(10*1000);
#endif
}
}// end : while ()

// 退出是写入文件
FILE_WRITEANDCLOSE();

return 0;
}


[解决办法]
简单的,用不着自已编写format吧,sprintf就可以了,
[解决办法]
再简单点,fprintf就可以,
自己写format方便控制自定义格式
[解决办法]
仅供参考

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <windows.h>
#include <io.h>
#else
#include <unistd.h>
#include <sys/time.h>
#include <pthread.h>
#define CRITICAL_SECTION pthread_mutex_t
#define _vsnprintf vsnprintf
#endif
//Log{
#define MAXLOGSIZE 20000000
#define MAXLINSIZE 16000
#include <time.h>
#include <sys/timeb.h>
#include <stdarg.h>
char logfilename1[]="MyLog1.log";
char logfilename2[]="MyLog2.log";
static char logstr[MAXLINSIZE+1];
char datestr[16];
char timestr[16];
char mss[4];
CRITICAL_SECTION cs_log;
FILE *flog;
#ifdef WIN32
void Lock(CRITICAL_SECTION *l) {
EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l) {
LeaveCriticalSection(l);
}
#else
void Lock(CRITICAL_SECTION *l) {
pthread_mutex_lock(l);
}
void Unlock(CRITICAL_SECTION *l) {
pthread_mutex_unlock(l);
}
#endif
void LogV(const char *pszFmt,va_list argp) {
struct tm *now;
struct timeb tb;

if (NULL==pszFmt
[解决办法]
0==pszFmt[0]) return;
_vsnprintf(logstr,MAXLINSIZE,pszFmt,argp);
ftime(&tb);
now=localtime(&tb.time);
sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
sprintf(timestr,"%02d:%02d:%02d",now->tm_hour ,now->tm_min ,now->tm_sec );
sprintf(mss,"%03d",tb.millitm);
printf("%s %s.%s %s",datestr,timestr,mss,logstr);
flog=fopen(logfilename1,"a");
if (NULL!=flog) {
fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr);
if (ftell(flog)>MAXLOGSIZE) {
fclose(flog);
if (rename(logfilename1,logfilename2)) {
remove(logfilename2);
rename(logfilename1,logfilename2);
}
} else {
fclose(flog);


}
}
}
void Log(const char *pszFmt,...) {
va_list argp;

Lock(&cs_log);
va_start(argp,pszFmt);
LogV(pszFmt,argp);
va_end(argp);
Unlock(&cs_log);
}
//Log}
int main(int argc,char * argv[]) {
int i;
#ifdef WIN32
InitializeCriticalSection(&cs_log);
#else
pthread_mutex_init(&cs_log,NULL);
#endif
for (i=0;i<10000;i++) {
Log("This is a Log %04d from FILE:%s LINE:%d\n",i, __FILE__, __LINE__);
}
#ifdef WIN32
DeleteCriticalSection(&cs_log);
#else
pthread_mutex_destroy(&cs_log);
#endif
return 0;
}


[解决办法]
产品级别的, 最好保证
1. 可以自由关闭或者调整debug级别, release版本日志应该默认打在Error级别. 一般性能影响应该还好.
2. 如果支持用户自己打开Debug Log, 最好实现磁盘容量控制. 也就是删除过期日志.
3. 支持flush模式(非缓冲), 方便特定Bug的实时观察.
4. 如果用户自己可以打开Debug log, 不能存入敏感信息和关键函数信息.
[解决办法]
glog党飘过
[解决办法]
开始还是用不带缓冲的IO来做吧
另外,既然LZ用的是带缓冲的IO,那就没有必要100次fflush一下,反正又不会阻塞,直接fflush好了

效率方面,看下这个贴子: write+commit与ofstream的比较
[解决办法]
有现成的syslog不用?

读书人网 >C++

热点推荐