前言

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

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

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

正文

目标

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

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

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

动手编写TCP服务器系列之一:日志文件的更多相关文章

  1. lnmp vps服务器删除mysql日志文件三种方法

    我在上一篇文章介绍了著名的LNMP主机一键安装工具,对比了军哥lnmp和AMH主机的差别,由于AMH拥有用户后台界面,易于新手操作,值得推荐. 但是,上周末我网站宕机,收到DNSPOD发来了宕机提醒, ...

  2. Golang 编写 Tcp 服务器

    Golang 作为广泛用于服务端和云计算领域的编程语言,tcp socket 是其中至关重要的功能.无论是 WEB 服务器还是各类中间件都离不开 tcp socket 的支持. Echo 服务器 拆包 ...

  3. windows服务器自动删除日志文件

    https://blog.csdn.net/u010050174/article/details/72510367 步骤: 1.新建 一个bat脚本 2.添加到window执行计划中,进行每日执行. ...

  4. php编写tcp服务器和客户端程序

    这是我从别的地方看到的. 1.修改php.ini,打开extension=php_sockets.dll 2.客户端程序 SocketClient.php <?php set_time_limi ...

  5. 【实操日记】使用 PyQt5 设计下载远程服务器日志文件程序

    最近通过 PyQt5 设计了一个下载服务器指定日期日志文件的程序,里面有些有意思的技术点,现在做一些分享. PyQt5 是一套 Python 绑定 Digia Qt5 应用的框架,是最强大的 GUI ...

  6. TCP服务器程序

    Linux下编写TCP服务器调用的函数顺序为:socket -> bind -> listen -> accept -> recv/send socket 参见:http:// ...

  7. 记一次log4j日志文件输出错误的解决

    log4j错误信息:log4j:ERROR Failed to rename [D:/logs/wmts_] to [D:/logs/wmts_2015-12-21.log ]. 起因:部门网站使用B ...

  8. Java实时读取日志文件

    古怪的需求 在实习的公司碰到一个古怪的需求:在一台服务器上写日志文件,每当日志文件写到一定大小时,比如是1G,会将这个日志文件改名成另一个名字,并新建一个与原文件名相同的日志文件,再往这个新建的日志文 ...

  9. Windows 2003 Server C盘空间被IIS日志文件消耗殆尽案例

    今天突然收到手头一台数据库服务器的磁盘空间告警邮件,C盘空间只剩下5.41GB大小(当系统磁盘剩余空间小于总大小的10%时,发出告警邮件),如下图所示: 由于还有一些微弱印象:前阵子这台服务器的C盘剩 ...

随机推荐

  1. c++字符串排序

    在主函数中输入10个等长的字符串,用另一函数对它们排序.然后在主函数输出这10个已排好序的字符串. 用两种方法完成. 方法一:用二维数组做函数参数: 方法二:用指向一维数组的指针做函数参数. 方法一: ...

  2. 开放接口/RESTful/Api服务的设计和安全方案

    总体思路 这个涉及到两个方面问题:一个是接口访问认证问题,主要解决谁可以使用接口(用户登录验证.来路验证)一个是数据数据传输安全,主要解决接口数据被监听(HTTPS安全传输.敏感内容加密.数字签名) ...

  3. JS中如何使用EL表达式中的对象

    JS中如何使用EL表达式中的对象 2017年09月25日 15:33:09 lhpnba 阅读数:4859   1.js中使用el表达式要加双引号或单引号:'${list}' 2.js变量获取el表达 ...

  4. array_udiff、array_udiff_assoc、array_udiff_uassoc 使用方法

      <?php // array_udiff 用自定义函数比较数组的差值(array_diff 使用内置函数) // 使用该函数我们通过进行更复杂的比较 class Rectangle { pu ...

  5. gdb打印STL和boost容器

    http://note.youdao.com/noteshare?id=b581e0db0084b6ba3011d9d27d372c91

  6. js 生成二维码

    $(".good_info").on('click',function () { var id = $(this).data('id'); var string = 'http:/ ...

  7. java rmi远程方法调用实例

    RMI的概念 RMI(Remote Method Invocation)远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制.使用这种机制,某一台计算机上的对象可以调用另外一台计 ...

  8. nodejs文件压缩-使用gulp命令(安装过程)

    为了代码安全问题,一般发布程序的时候需要将js代码进行压缩,记录一下安装流程.避免忘记. 安装插件

  9. iOS数据存取---iOS-Apple苹果官方文档翻译

    CHENYILONG Blog iOS数据存取---iOS-Apple苹果官方文档翻译 数据存取/*技术博客http://www.cnblogs.com/ChenYilong/ 新浪微博http:// ...

  10. MySql 快速去重方法

    1.复制需要去重的表 CREATE TABLE 新表 LIKE 旧表 ; 2.将需要去重的字段 设置为唯一union 索引 ALTER TABLE 表名 ADD UNIQUE(`字段`); 3.复制旧 ...