那谁的技术博客

感兴趣领域:高性能服务器编程,存储,算法,Linux内核
随笔 - 210, 文章 - 0, 评论 - 1183, 引用 - 0
数据加载中……

服务器公共库开发--log系统

实现了简易的log打印,有以下的特点:
1) 支持向标准输出或者文件打印log
2) 向文件打印log时限制log文件的大小, 同时, 如果在文件大小超出范围的时候, 可以选择下一个文件进行打印.在这里, 文件大小和文件的数量都是可以配置的.
3) 支持分级别打印, 目前分的级别有DEBUG,ERROR,WARN,INFO这几种, 在打印时, 也可以通过宏来开启DEBUG,WARN的log.
4) 在向文件打印log时, 支持多线程, 同样的, 这个也是可以配置的.

分为三个文件,log.h是对外的接口, 提供对文件打印log和对标准输出打印log的简易封装, 具体使用哪种方式, 可以通过宏进行配置;logger.h和logger.cpp是对文件进行打印的类封装, 是一个单件类, 因此继承自前面提过的singleton类.

log.h:

/********************************************************************
    created:    2008/08/02
    filename:   log.h    
    author:        Lichuang
                
    purpose:    log模块的对外接口, 可以根据宏将log打印到文件或者标准
                输出
********************************************************************
*/

#ifndef __LOG_H__
#define __LOG_H__

#ifdef __USE_LOG_FILE__

#include 
"logger.h"

#define LOG(runlevel, file, lineno, args)   \
        
do                                  \
        {                                   \
            CLogger::GetInstance()
->Log(    \
                    runlevel, file,         \
                    lineno, args);          \
        } 
while (0)

#define RUN_MSG_LOG()        LOG(INFO, __FILE__, __LINE__, __VA_ARGS__);
#define ERROR_MSG_LOG()      LOG(ERROR, __FILE__, __LINE__, __VA_ARGS__);

#ifdef LOG_DEBUG_MSG
    
#define DEBUG_MSG_LOG()  LOG(DEBUG, __FILE__, __LINE__, __VA_ARGS__);
#else
    
#define DEBUG_MSG_LOG(
#endif

#ifdef LOG_WARN_MSG
    
#define WARN_MSG_LOG()   LOG(WARN, __FILE__, __LINE__, __VA_ARGS__);
#else
    
#define WARN_MSG_LOG(
#endif

#else

#include 
<stdio.h>
#include 
<stdlib.h>
#include 
<time.h>
#include 
<stdarg.h>
#include 
<sys/time.h>

#define LOG(runlevel, file, lineno, args)   \
        
do{ \
                
struct timeval now;\
                gettimeofday(
&now,0); \
                
struct tm *ptm=localtime(&(now.tv_sec)); \
                fprintf(runlevel, 
"[%d:%d] [%s:%d]",ptm->tm_hour,ptm->tm_min, file,lineno); \
                fprintf(runlevel, args); \
                fprintf(runlevel, 
"\n"); \
        }
while(0)

#define RUN_MSG_LOG()        LOG(stdout, __FILE__, __LINE__, __VA_ARGS__);
#define ERROR_MSG_LOG()      LOG(stderr, __FILE__, __LINE__, __VA_ARGS__);

#ifdef LOG_DEBUG_MSG
    
#define DEBUG_MSG_LOG()  LOG(stdout, __FILE__, __LINE__, __VA_ARGS__);
#else
    
#define DEBUG_MSG_LOG(
#endif

#ifdef LOG_WARN_MSG
    
#define WARN_MSG_LOG()   LOG(stdout, __FILE__, __LINE__, __VA_ARGS__);
#else
    
#define WARN_MSG_LOG(
#endif

#endif

#endif /* __LOG_H__ */


logger.h
/********************************************************************
    created:    2008/08/02
    filename:   logger.h    
    author:        Lichuang
                
    purpose:    实现简易的log打印
********************************************************************
*/

#ifndef __LOGGER_H__
#define __LOGGER_H__

#include 
"defines.h"
#include 
"singleton.h"
#include 
"threadmutex.h"
#include 
<string>

using namespace std;

struct LogInfo
{
    
int nFileNum;
    
string strFileSuffix;
    
string strFileName;
};

enum 
{
    DEBUG 
= 0,
    WARN,
    ERROR,
    INFO,
    NUM_OF_LOG_LEVEL
};

class CLogger
    : 
public CSingleton<CLogger>
{
public:

public:
    
int Init(const string& strLogPath, const string& strFileName);
    
int Log(int nLogLevel, const char* pFileName, int nLine, const char *szFormat, );

private:
    DECLARE_SINGLETON_CLASS(CLogger)
    CLogger();
    
virtual ~CLogger();

    
int GetFileName(int nLogLevel);
    
int CheckFileSize(const string& strFileName);
    
void GenerateFileName(string& strFileName, int nFileNum, int nLogLevel);

private:
    
string m_strLogPath;
    
string m_strFileName;
    LogInfo m_szLogInfo[NUM_OF_LOG_LEVEL];
    CThreadMutex m_tThreadMutex;
};

#endif /* __LOGGER_H__ */

logger.cpp
/********************************************************************
    created:    2008/08/02
    filename:   logger.h    
    author:        Lichuang
                
    purpose:    实现简易的log打印
********************************************************************
*/

#include 
"logger.h"
#include 
<sys/stat.h>

static const char* g_szLogSuffix[] = 
{
    
".debug",
    
".warn",
    
".error",
    
".info",
    NULL
};

CLogger::CLogger()
{
}

CLogger::
~CLogger()
{
}

int CLogger::Init(const string& strLogPath, const string& strFileName)
{
    m_strLogPath 
= strLogPath;
    m_strFileName 
= strFileName;

    
for (int i = DEBUG; i < NUM_OF_LOG_LEVEL; ++i)
    {
        m_szLogInfo[i].strFileSuffix 
= g_szLogSuffix[i];
        
if (0 > GetFileName(i))
            
return -1;
    }

    
return 0;
}

int CLogger::GetFileName(int nLogLevel)
{
    
int i;
    
string strFileName;
    
for (i = 0; i < MAX_FILE_NUM; ++i)
    {
        GenerateFileName(strFileName, i, nLogLevel);
        
if (!CheckFileSize(strFileName))
        {
            
break;
        }
    }

    
if (MAX_FILE_NUM == i)
    {
        
return -1;
    }

    m_szLogInfo[nLogLevel].nFileNum 
= i;
    m_szLogInfo[nLogLevel].strFileName 
= strFileName;

    
return 0;
}

int CLogger::CheckFileSize(const string& strFileName)
{
    
struct stat tStat;
    
int nRet = ::stat(strFileName.c_str(), &tStat);
    
if (0 == nRet && tStat.st_size > MAX_FILE_SIZE)
    {
        
return -1;
    }
    
else
    {
        
return 0;
    }
}

void CLogger::GenerateFileName(string& strFileName, int nFileNum, int nLogLevel)
{
    
char szFileNum[10];
    ::snprintf(szFileNum, 
sizeof(szFileNum), "_%02d", nFileNum);
    strFileName 
= m_strLogPath + m_strFileName + m_szLogInfo[nLogLevel].strFileSuffix + szFileNum;
}

int CLogger::Log(int nLogLevel, const char* pFileName, int nLine, const char *szFormat, )
{
    va_list ap;
    FILE 
*pFile;
    
char szTime[32];
    time_t nCurrTime;
    
struct tm stCurrTime;

    THREAD_LOCK(m_tThreadMutex);

    
if (0 > CheckFileSize(m_szLogInfo[nLogLevel].strFileName))
    {
        m_szLogInfo[nLogLevel].nFileNum 
= (m_szLogInfo[nLogLevel].nFileNum + 1% MAX_FILE_NUM;
        GenerateFileName(m_szLogInfo[nLogLevel].strFileName, m_szLogInfo[nLogLevel].nFileNum, nLogLevel);
    }

    
if(NULL == (pFile = ::fopen(m_szLogInfo[nLogLevel].strFileName.c_str(), "a+")))
    {
        
return -1;
    }

    ::time(
&nCurrTime);
    ::localtime_r(
&nCurrTime, &stCurrTime);
    ::snprintf(szTime, 
sizeof(szTime), "%d%02d%02d %02d:%02d:%02d",
            stCurrTime.tm_year
+1900, stCurrTime.tm_mon+1, stCurrTime.tm_mday,
            stCurrTime.tm_hour, stCurrTime.tm_min, stCurrTime.tm_sec);
    ::fprintf(pFile, 
"[%s]:[%s][%d] ", szTime, pFileName, nLine);

    ::va_start(ap, szFormat);
    ::vfprintf(pFile, szFormat, ap);
    ::va_end(ap);

    ::fprintf(pFile, 
"\n");
    ::fclose(pFile);

    THREAD_UNLOCK(m_tThreadMutex);

    
return 0;
}


posted on 2008-08-05 11:37 那谁 阅读(2970) 评论(10)  编辑 收藏 引用 所属分类: 服务器设计Linux/Unix

评论

# re: 服务器公共库开发--log系统  回复  更多评论   

log rotate 最好能加一些可配置的策略,
如允许多少log文件,文件大小超过多少rotate,用户可定义什么时间rotate,用户可定义log可以生存多少时间自动删除。
log 还可以加入模块名的参数,有的时候只想log某些模块。
2008-08-05 13:18 | lotus

# re: 服务器公共库开发--log系统  回复  更多评论   

试试Log4吧,Log不用自己写
2008-08-05 13:36 | 亨德列克

# re: 服务器公共库开发--log系统  回复  更多评论   

@lotus
这几个配置基本都有了,除了那个用户定义生存时间的策略我没有做之外.另外,log模块也没有实现.

2008-08-05 14:27 |

# re: 服务器公共库开发--log系统  回复  更多评论   

@亨德列克
log4cpp吧?感觉有点大....
2008-08-05 14:28 |

# re: 服务器公共库开发--log系统  回复  更多评论   

试试log4cxx,最近出了新版本.
采用log4系列是个很好的选择,不用担心功能不够用,有各种语言的绑定,大不是问题,都是设设INCLUDE路径,LIB路径直接用就行了.
2008-08-05 20:10 | tangxinfa

# re: 服务器公共库开发--log系统  回复  更多评论   

所谓的 log4cxx ,完全是照抄 JAVA 的理念做的不伦不类的东西。
语言的不同核心应该是理念的不同。
C/C++ 语言最好的 log 应该是 syslog ,完全现成,完全满足需要,完全可配置,也完全符合 KISS 标准。
记住,每个程序,只做自己改做的事情,并把事情做好。
2008-08-06 10:37 | tangfl

# re: 服务器公共库开发--log系统  回复  更多评论   

syslog和log4xx都是封装好的东西,如果要知其所以然,还得我们亲手实践。
各个公司可能会需要在自己的业务需求上开发自己的log基盘,所以楼主的实践颇具价值。
2008-08-06 13:00 | 金哥

# re: 服务器公共库开发--log系统  回复  更多评论   

每次写log都去检查size会不会有影响呢?
2008-08-26 11:10 | haha

# re: 服务器公共库开发--log系统[未登录]  回复  更多评论   

言之有理,以后会进行优化,用一个成员变量去存放文件的大小, 每次程序初始化的时候读取文件大小到这个成员中, 每次打印log之后更新, 而不是每次去检查文件大小.
2008-08-26 12:13 |

# re: 服务器公共库开发--log系统  回复  更多评论   

if (0 > CheckFileSize(m_szLogInfo[nLogLevel].strFileName))
{
m_szLogInfo[nLogLevel].nFileNum = (m_szLogInfo[nLogLevel].nFileNum + 1) % MAX_FILE_NUM;
GenerateFileName(m_szLogInfo[nLogLevel].strFileName, m_szLogInfo[nLogLevel].nFileNum, nLogLevel);
}

这里有问题吧,如果(m_szLogInfo[nLogLevel].nFileNum + 1) % MAX_FILE_NUM;这个log文件是回到0,这个文件肯定是已经达到最大存储了,这时候还写?
2009-03-03 20:55 | capable

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理