动手编写TCP服务器系列之一:日志文件

前言

   在几个月之前,笔者想自己实现一个性能比较良好的基于tcp的服务器。于是断断续续写了个把月,因为还需要找工,还有论文什么的。拖了这么久。现在开辟这样的一个博客,我想记录下自己的思路,也和大家分享自己的代码。个人觉得一个优秀的服务器,不能考虑各种通用性,平台无关性。我写的这个服务器只针对linux平台。

   在写这个系列文章之前,我想把功能都独立出来。每一篇文章专注于一个功能。

    本系列文章均系笔者所写,难免有一些错误或者纰漏,如果小伙伴们有好的建议或者更好的算法,请不吝赐教。

正文

目标

   本篇文章主要是讲TCP Server的日志功能。那么在一个服务器当中日志功能是必不可少的。无论是定位错误或者打印一些信息等等。日志的预期目标如下图所示。

8

   Time是记录日志时间;Level是日志级别;PID是进程ID;TID是线程ID;Function是日志所在函数;Line是文件行;File是日志文件;Message是所打印的信息。

#ifndef LOG_HPP_
#define LOG_HPP_

#include <string>
#include <cstdio>
#include <cstring>

#include <stdarg.h>

#include <syscall.h>
#include <sys/time.h>
#include <linux/limits.h>

#define LOG_STRING_MAX 4096

#define NEWLINE           "\n"
#define MAX_PATHSIZE      PATH_MAX
#define RETURN_OK             0

#define LOG(level,fmt,...)                                     \
do {                                                                  \
 if( _curPDLevel >= level )                                           \
 {                                                                    \
     log(level,__func__,__FILE__,__LINE__,fmt,##__VA_ARGS__);\
 }                                                                    \
}while( 0 )                                                           \

#define CHECK( cond, retCode, gotoLabel, level, fmt, ... )         \
do {                                                                  \
 if( !( cond ) )                                                      \
 {                                                                    \
     rc = ( retCode );                                                \
     LOG( (level), fmt, ##__VA_ARGS__ );                           \
     goto gotoLabel;                                                  \
 }                                                                    \
}while( 0 )                                                           \

enum PDLEVEL
{
 PDSERVER=0,
 PDERROR,
 PDEVENT,
 PDWARNING,
 PDINFO,
 PDDEBUG
};

extern PDLEVEL _curPDLevel;
const char * getPDLevelDesp( PDLEVEL level );

#define PD_DFT_DIAGLEVEL PDWARNING

void log( PDLEVEL level, const char * func, const char * file,
     unsigned int line, const char * format, ... );

void log( PDLEVEL level, const char * func, const char * file,
     unsigned int lne, std::string message );


#endif
#include "log.hpp"
#include "latch.hpp"
#include "FileOp.hpp"

const static char * PDLEVELSTRING[] =
{
    "SEVERE",
    "ERROR",
    "EVENT",
    "WARNING",
    "INFO",
    "DEBUG"
};

const char * getLevelDesp( PDLEVEL level )
{
    if( (unsigned int)level > (unsigned int)PDDEBUG )
    {
        return "Unknown level";
    }
    return PDLEVELSTRING[(unsigned int)level];
}

const static char * LOG_HEADER_FORMAT = \
        "[Time]     %04d-%02d-%02d-%02d.%02d.%02d           [Level] %s"OSS_NEWLINE \
        "[PID]      %-30d[TID]   %d"OSS_NEWLINE \
        "[Function] %-30s[Line]  %d"OSS_NEWLINE \
        "[File]     %s"OSS_NEWLINE\
        "[Message]  %s"OSS_NEWLINE OSS_NEWLINE;

LEVEL _curPDLevel = DFT_DIAGLEVEL;

static char _diaglogPath[ OSS_MAX_PATHSIZE+1 ] = { 0 };

Latch _logMutex;
FileOperation _logFile;

// open log file
static int _pdLogFileReopen()
{
    int rc = RETURN_OK;
    _logFile.Close();
    rc = _logFile.Open( _pdDiaglogPath );
    if( rc )
    {
        printf( "Failed to open log file, errono = %d", OSS_NEWLINE, rc );
        goto error;
    }

    _logFile.SeekToEnd();
done:
    return rc;
error:
    goto done;
}

// write log file
static int _logFileWrite( const char * pData )
{
    int rc = RETURN_OK;
    size_t dataSize = strlen( pData );
    _logMutex.get();
    if( !_logFile.IsValid() )
    {
        // open the file
        rc = _logFileReopen();
        if( rc )
        {
            printf( "Failed to open log file, erroro = %d"OSS_NEWLINE, rc);
            goto error;
        }
    }
    rc = _logFile.Write( pData, dataSize );
    if( rc )
    {
        printf( "Failed to write into log file, errno = %d"OSS_NEWLINE, rc );
        goto error;
    }
done:
    _logMutex.release();
    return rc;
error:
    goto done;
}

void log( PDLEVEL level, const char * func, const char * file, unsigned int line, const char * format, ... )
{
    int rc = RETURN_OK;
    if( _curPDLevel < level )
    {
        return;
    }
    va_list ap;
    char userInfo[ PD_LOG_STRING_MAX ];
    char sysInfo[PD_LOG_STRING_MAX];
    struct tm otm;
    struct timeval tv;
    struct timezone tz;
    time_t tt;

    gettimeofday( &tv, &tz );
    tt = tv.tv_sec;
    localtime_r( &tt, &otm );

    // create user information
    va_start( ap, format );
    vsnprintf( userInfo, PD_LOG_STRING_MAX, format, ap );
    va_end( ap );

    snprintf( sysInfo, LOG_STRING_MAX, LOG_HEADER_FORMAT,
            otm.tm_year + 1900,
            otm.tm_mon + 1,
            otm.tm_mday,
            otm.tm_hour,
            otm.tm_min,
            otm.tm_sec,
            PDLEVELSTRING[level],
            getpid(),
            syscall(SYS_gettid),
            func,
            line,
            file,
            userInfo );

    printf( "%s"NEWLINE, sysInfo);
/*
   if( _pdDiaglogPath[0] != '\0' )
    {
        rc = _logFileWrite( sysInfo );
        if( rc )
        {
            printf( "Failed to write into log file, errno = %d"NEWLINE, rc );
            printf( "%s"NEWLINE, sysInfo );
        }
    }
*/
    return;
}

/*
int main( int argc, char ** argv )
{
    PD_LOG( PDERROR, "%d", 1 );
    return 0;
}
*/

   代码仅供参考。可能中间用到了一些锁的机制或者一些写文件的类。比如FileOp在后续的文章中会慢慢呈现出来。

作者

   出处:http://www.cnblogs.com/gina

   本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

posted @ 2013-08-12 16:39  进击的程序员  阅读(1159)  评论(0编辑  收藏  举报