首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

C++写日记

2013-05-02 
C++写日志在linux下调试比较蛋疼,因此需要打许多的日志。尝试写了一个写日志文件的类,不知道性能能不能达到

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不用?

热点排行