/* 自定义打开、输出、关闭日志函数类型 */ typedef int funcOpenLog( LOG *g , char *log_pathfilename , void **open_handle ); typedef int funcWriteLog( LOG *g , void **open_handle , int log_level , char *buf , long len , long *writelen ); typedef int funcChangeTest( LOG *g , void **test_handle ); typedef int funcCloseLog( LOG *g , void **open_handle ); #define LOG_NO_OUTPUTFUNC NULL , NULL , NULL , NULL , NULL , NULL /* 设置输出类型 */ _WINDLL_FUNC int SetLogOutput( LOG *g , int output , char *log_pathfilename , funcOpenLog *pfuncOpenLogFirst , funcOpenLog *pfuncOpenLog , funcWriteLog *pfuncWriteLog , funcChangeTest *pfuncChangeTest , funcCloseLog *pfuncCloseLog , funcCloseLog *pfuncCloseLogFinally );
funcOpenLog funcOpenLog_ConnectToLogServer ; int funcOpenLog_ConnectToLogServer( LOG *g , char *log_pathfilename , void **open_handle ) { char *ptr = NULL ; char *ptr2 = NULL ; long log_pathfilename_len ; char ch_len ; char ip[ MAXLEN_IP + 1 ] ; long port ; long timeout ; int nret = 0 ; if( IsLogOpened(g) == 1 ) return 0; /* 申请日志打开环境句柄内存 */ (*open_handle) = (int*)malloc( sizeof(int) ) ; if( (*open_handle) == NULL ) { return -11; } /* 分析文件名和网络地址 */ ptr = strchr( log_pathfilename , '@' ) ; if( ptr == NULL ) return -21; log_pathfilename_len = ptr - log_pathfilename ; ch_len = (char)log_pathfilename_len ; ptr++; ptr2 = strchr( ptr , ':' ) ; memset( ip , 0x00 , sizeof(ip) ); strncpy( ip , ptr , ptr2 - ptr ); ptr2++; port = atol(ptr2) ; /* 建立客户端socket */ nret = TCPCreateClient( (int*)(*open_handle) ) ; if( nret ) return -31; /* 链接服务端socket */ nret = TCPConnectToServer( (int*)(*open_handle) , ip , port ) ; if( nret ) { CloseSocket( (int*)(*open_handle) ); return -32; } /* 发送文件名 */ timeout = 10 * 1000 ; nret = TCPSendData( *(int*)(*open_handle) , & ch_len , 1 , & timeout ) ; if( nret ) { CloseSocket( (int*)(*open_handle) ); return -33; } nret = TCPSendData( *(int*)(*open_handle) , log_pathfilename , log_pathfilename_len , & timeout ) ; if( nret ) { CloseSocket( (int*)(*open_handle) ); return -34; } SetOpenFlag(g,1); return 0; } funcWriteLog funcWriteLog_SendToLogServer ; int funcWriteLog_SendToLogServer( LOG *g , void **open_handle , int log_level , char *buf , long len , long *writelen ) { long timeout ; int nret = 0 ; if( IsLogOpened(g) == 0 ) return 0; /* 发送日志数据 */ timeout = 10 * 1000 ; (*writelen) = TCPSendData( *(int*)(*open_handle) , buf , len , & timeout ) ; if( (*writelen) ) { /* 若是发送失败,重连服务端,而后再发送,若是还失败,报错返回 */ funcCloseLog_DisconnectFromLogServer( g , open_handle ); nret = funcOpenLog_ConnectToLogServer( g , g->log_pathfilename , & (g->open_handle) ) ; if( nret ) return -41; timeout = 10 * 1000 ; (*writelen) = TCPSendData( *(int*)(*open_handle) , buf , len , & timeout ) ; if( (*writelen) <= 0 ) return -42; } return 0; } funcCloseLog funcCloseLog_DisconnectFromLogServer ; int funcCloseLog_DisconnectFromLogServer( LOG *g , void **open_handle ) { if( IsLogOpened(g) == 0 ) return 0; /* 断开socket链接 */ CloseSocket( (int*)(*open_handle) ); /* 释放日志打开环境内存 */ free( (*open_handle) ); SetOpenFlag(g,0); return 0; }
#include <stdio.h> #include "LOGSERVER.h" #define LOG_STYLES_TEST ( LOG_STYLE_DATETIME | LOG_STYLE_LOGLEVEL | LOG_STYLE_PID | LOG_STYLE_TID | LOG_STYLE_SOURCE | LOG_STYLE_FORMAT | LOG_STYLE_NEWLINE ) int test_iLOG3SERVER() { LOG *g = NULL ; char buffer[ 64 + 1 ] = "" ; long buflen = sizeof(buffer) - 1 ; int nret = 0 ; /* 建立日志句柄,带后缀'G'的函数还会自动设置到线程安全的全局缺省日志句柄 */ if( CreateLogHandleG() == NULL ) { printf( "CreateLogHandleG failed\n" ); return -1; } else { printf( "CreateLogHandleG ok\n" ); } /* 设置日志属性 */ nret = SetLogOutputG( LOG_OUTPUT_CALLBACK , "$ProgramFiles$/test_iLOG3SERVER.log@127.0.0.1 :7878" , & funcOpenLog_ConnectToLogServer , NULL , & funcWriteLog_SendToLogServer , NULL , NULL , & funcCloseLog_DisconnectFromLogServer ) ; /* 设置输出类型时挂上那三个回调函数,实现自定义的链接远程日志服务器、输出日志数据到远程日志服务器、关闭通信链接功能 */ if( nret ) { printf( "SetLogOutputG failed\n" ); return -1; } SetLogLevelG( LOG_LEVEL_INFO ); SetLogStylesG( LOG_STYLES_TEST , LOG_NO_STYLEFUNC ); /* 输出日志 */ DebugLogG( __FILE__ , __LINE__ , "hello DEBUG" ); InfoLogG( __FILE__ , __LINE__ , "hello INFO" ); WarnLogG( __FILE__ , __LINE__ , "hello WARN" ); ErrorLogG( __FILE__ , __LINE__ , "hello ERROR" ); FatalLogG( __FILE__ , __LINE__ , "hello FATAL" ); DebugHexLogG( __FILE__ , __LINE__ , buffer , buflen , "hello DEBUG" ); InfoHexLogG( __FILE__ , __LINE__ , buffer , buflen , "hello INFO" ); WarnHexLogG( __FILE__ , __LINE__ , buffer , buflen , "hello WARN" ); ErrorHexLogG( __FILE__ , __LINE__ , buffer , buflen , "hello ERROR" ); FatalHexLogG( __FILE__ , __LINE__ , buffer , buflen , "hello FATAL" ); /* 释放日志句柄 */ DestroyLogHandleG(); return 0; } int main() { /* windows上须要作初始化 */ SocketSystemInitial(); return -test_iLOG3SERVER(); }
#ifndef _H_SERVER_ #define _H_SERVER_ /* * iLOG3SERVER - iLOG3简单远程服务器 * author : calvin * email : * history : 2014-02-11 v1.0.0 建立 */ #include <stdio.h> #include "LibX.h" #include "StringX.h" #include "Socket.h" #include "SocketTCP.h" #include "LOGSERVER.h" #define IP_LOGSERVER "0" #define PORT_LOGSERVER 7878 #define MAXCNT_LOGCLIENT 100 #define CLIENTSTATUS_UNUSED 0 #define CLIENTSTATUS_LOGGING 2 #define RECV_BUFFER_SIZE 1024 #ifndef MAXLEN_FILENAME #define MAXLEN_FILENAME 256 #endif struct SocketAddressExp { char ip [ MAXLEN_IP + 1 ] ; long port ; } ; struct LogClientAccept { char status ; /* 链接状态 */ int clisock ; /* 已接受socket描述字 */ SOCKADDR cliaddr ; /* 已接受socket地址信息 */ struct SocketAddressExp cliaddr_exp ; /* 已接受socket地址信息(转换为易读格式) */ char log_pathfilename[ MAXLEN_FILENAME + 1 ] ; FILE *fp ; } ; struct LogServerEnv { int lsnsock ; /* 侦听socket描述字 */ SOCKADDR lsnaddr ; /* 侦听socket地址信息 */ struct LogClientAccept client[ MAXCNT_LOGCLIENT ] ; /* 客户端socket环境单元集 */ } ; int LogServer( char *server_ip , long server_port ); int SendSocketData( struct LogServerEnv *penv , struct LogClientAccept *pselclient ); int ProcessSocketData( struct LogServerEnv *penv , struct LogClientAccept *pselclient ); int ReceiveSocketData( struct LogServerEnv *penv , struct LogClientAccept *pselclient ); #endif [/code] server.c [code=c] static int AcceptClientSocket( struct LogServerEnv *penv ) { int clisock ; SOCKADDR cliaddr ; struct SocketAddressExp cliaddr_exp ; long timeout ; char ch ; long ch_len ; char log_pathfilename[ MAXLEN_FILENAME + 1 ] ; long log_pathfilename_len ; char env_key[ MAXLEN_FILENAME + 1 ] ; long env_key_len ; char *env_val = NULL ; long env_val_len ; char *p1 = NULL , *p2 = NULL ; FILE *fp = NULL ; int i ; struct LogClientAccept *pclient = NULL ; int nret ; /* 接受新socket链接 */ nret = TCPAcceptFromClient( & (penv->lsnsock) , & clisock , & cliaddr ) ; if( nret ) { printf( "TCPAcceptFromClient failed[%d]errno[%d]\n" , nret , errno ); return 0; } else { memset( & cliaddr_exp , 0x00 , sizeof(cliaddr_exp.ip) ); GetSocketAddressIP( & cliaddr , cliaddr_exp.ip ); GetSocketAddressPort( & cliaddr , & (cliaddr_exp.port) ); printf( "TCPAcceptFromClient ok , ip[%s]port[%ld]\n" , cliaddr_exp.ip , cliaddr_exp.port ); } /* 接收文件名长度 */ timeout = 10 * 1000 ; ch = 0 ; ch_len = 1 ; nret = TCPReceiveData( clisock , & ch , & ch_len , & timeout ) ; if( nret ) { printf( "TCPReceiveData failed[%d]errno[%d]\n" , nret , errno ); CloseSocket( & clisock ); return 0; } memset( log_pathfilename , 0x00 , sizeof(log_pathfilename) ); log_pathfilename_len = (long)ch ; nret = TCPReceiveData( clisock , log_pathfilename , & log_pathfilename_len , & timeout ) ; if( nret ) { printf( "TCPReceiveData failed[%d]errno[%d]\n" , nret , errno ); CloseSocket( & clisock ); return 0; } p1 = strchr( log_pathfilename , '$' ); while( p1 ) { /* 展开环境变量 */ p2 = strchr( p1 + 1 , '$' ) ; if( p2 == NULL ) return LOG_RETURN_ERROR_PARAMETER; memset( env_key , 0x00 , sizeof(env_key) ); env_key_len = p2 - p1 + 1 ; strncpy( env_key , p1 + 1 , env_key_len - 2 ); env_val = getenv( env_key ) ; if( env_val == NULL ) { printf( "environment [%s] not found\n" , env_key ); CloseSocket( & clisock ); return 0; } env_val_len = strlen(env_val) ; if( log_pathfilename_len + ( env_val_len - env_key_len ) > sizeof(log_pathfilename)-1 ) { printf( "filename overflow\n" ); CloseSocket( & clisock ); return 0; } memmove( p2+1 + ( env_val_len - env_key_len ) , p2+1 , strlen(p2+1) + 1 ); memcpy( p1 , env_val , env_val_len ); log_pathfilename_len += env_val_len - env_key_len ; p1 = strchr( p1 + ( env_val_len - env_key_len ) , '$' ); } fp = fopen( log_pathfilename , "a" ) ; if( fp == NULL ) { printf( "fopen failed errno[%d]\n" , nret , errno ); CloseSocket( & clisock ); return 0; } /* 查询未使用客户端socket环境单元 */ for( i = 0 , pclient = & (penv->client[0]) ; i < MAXCNT_LOGCLIENT ; i++ , pclient++ ) { if( pclient->status == CLIENTSTATUS_UNUSED ) { break; } } if( i >= MAXCNT_LOGCLIENT ) { printf( "太多的客户端\n" ); CloseSocket( & clisock ); return 0; } /* 填充客户端socket环境单元 */ pclient->clisock = clisock ; memcpy( & (pclient->cliaddr) , & cliaddr , sizeof(SOCKADDR) ); memcpy( & (pclient->cliaddr_exp) , & cliaddr_exp , sizeof(struct SocketAddressExp) ); strcpy( pclient->log_pathfilename , log_pathfilename ); pclient->fp = fp ; pclient->status = CLIENTSTATUS_LOGGING ; return 0; } int ReceiveSocketData( struct LogServerEnv *penv , struct LogClientAccept *pselclient ) { char recv_buffer[ RECV_BUFFER_SIZE + 1 ] ; int recv_len ; int nret = 0 ; /* 接收通信数据到临时接收缓冲区 */ memset( recv_buffer , 0x00 , sizeof(recv_buffer) ); recv_len = sizeof(recv_buffer)-1 ; recv_len = recv( pselclient->clisock , recv_buffer , recv_len , 0 ) ; if( recv_len == 0 ) { printf( "socket closed by remote[%s:%ld]\n" , pselclient->cliaddr_exp.ip , pselclient->cliaddr_exp.port ); CloseSocket( & (pselclient->clisock) ); fclose( pselclient->fp ); pselclient->status = CLIENTSTATUS_UNUSED ; return 0; } else if( recv_len < 0 ) { printf( "read socket failed from remote[%s:%ld] , errno[%d]\n" , pselclient->cliaddr_exp.ip , pselclient->cliaddr_exp.port , errno ); CloseSocket( & (pselclient->clisock) ); fclose( pselclient->fp ); pselclient->status = CLIENTSTATUS_UNUSED ; return 0; } fprintf( pselclient->fp , "%s" , recv_buffer ); return 0; } int LogServer( char *server_ip , long server_port ) { struct LogServerEnv env , *penv = & env ; int selsocks[ 1 + MAXCNT_LOGCLIENT ] ; struct LogClientAccept *pselclient[ 1 + MAXCNT_LOGCLIENT ] ; int selsocks_count ; struct LogClientAccept *pclient = NULL ; int i ; int selsock_index ; int nret = 0 ; /* 初始化聊天环境 */ memset( penv , 0x00 , sizeof(struct LogServerEnv) ); /* 建立服务端侦听socket */ nret = TCPCreateServer( & (penv->lsnsock) , & (penv->lsnaddr) , server_ip , server_port ) ; if( nret ) { printf( "TCPCreateServer failed[%d]errno[%d]\n" , nret , errno ); return -1; } else { printf( "TCPCreateServer ok , ip[%s]port[%ld]\n" , server_ip , server_port ); } /* 主循环 */ while(1) { /* 准备socket描述字集合 */ selsocks[0] = penv->lsnsock ; selsocks_count = 1 ; for( i = 0 , pclient = & (penv->client[0]) ; i < MAXCNT_LOGCLIENT ; i++ , pclient++ ) { if( pclient->status != CLIENTSTATUS_UNUSED ) { selsocks[selsocks_count] = pclient->clisock ; pselclient[selsocks_count] = pclient ; selsocks_count++; } } /* 等待socket描述字事件 */ selsock_index = SelectSocket( selsocks , selsocks_count , SELECTSOCKET_READ , NULL ) ; if( selsock_index < 0 ) { printf( "SelectSocket failed[%d]errno[%d]\n" , selsock_index , errno ); return -1; } /* 处理socket描述字事件 */ if( selsock_index == 0 ) { /* 接受新socket链接 */ nret = AcceptClientSocket( penv ) ; if( nret ) { printf( "AcceptClientSocket failed[%d]errno[%d]\n" , nret , errno ); return -1; } } else { /* 接收已链接socket数据 */ nret = ReceiveSocketData( penv , pselclient[selsock_index] ) ; if( nret ) { printf( "ReceiveSocketData failed[%d]errno[%d]\n" , nret , errno ); return -1; } } } /* 关闭全部已链接socket */ for( i = 0 , pclient = & (penv->client[0]) ; i < MAXCNT_LOGCLIENT ; i++ , pclient++ ) { if( pclient->status != CLIENTSTATUS_UNUSED ) { CloseSocket( & (pclient->clisock) ); fclose( pclient->fp ); } } /* 关闭服务端侦听socket */ CloseSocket( & (penv->lsnsock) ); return 0; } static void usage( char *i ) { printf( "USAGE : %s server_ip server_port\n" , i ); return; } int main( int argc , char *argv[] ) { int nret = 0 ; if( argc != 1 + 2 ) { usage( argv[0] ); exit(7); } /* 初始化网络通信环境(windows上须要) */ SocketSystemInitial(); /* 启动聊天服务器 */ nret = -LogServer( argv[1] , atol(argv[2]) ) ; /* 销毁网络通信环境 */ SocketSystemDestroy(); return nret; }编译连接经过,运行 客户端显示 CreateLogHandleG ok DestroyLogHandleG ok 服务端显示 TCPCreateServer ok , ip[127.0.0.1]port[7878] TCPAcceptFromClient ok , ip[127.0.0.1]port[1823] socket closed by remote[127.0.0.1:1823] ...(服务端是并发迭代服务器) 打开"$ProgramFiles$/test_iLOG3SERVER.log",看到日志成功远程落地了 2014-02-13 20:53:38 | INFO | 776:2740:test_iLOG3SERVER.c:38 | hello INFO 2014-02-13 20:53:38 | WARN | 776:2740:test_iLOG3SERVER.c:39 | hello WARN 2014-02-13 20:53:38 | ERROR | 776:2740:test_iLOG3SERVER.c:40 | hello ERROR 2014-02-13 20:53:38 | FATAL | 776:2740:test_iLOG3SERVER.c:41 | hello FATAL 2014-02-13 20:53:38 | INFO | 776:2740:test_iLOG3SERVER.c:44 | hello INFO 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 2014-02-13 20:53:38 | WARN | 776:2740:test_iLOG3SERVER.c:45 | hello WARN 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 2014-02-13 20:53:38 | ERROR | 776:2740:test_iLOG3SERVER.c:46 | hello ERROR 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 2014-02-13 20:53:38 | FATAL | 776:2740:test_iLOG3SERVER.c:47 | hello FATAL 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 设置日志输出类型函数SetLogOutput的回调机制,能够帮助咱们扩展实现不少有意思的自定义功能,这里只是演示了日志远程落地,还能把日志输出到IBMMQ、本地数据库等,开源日志函数库iLOG3实际上是实现了一个日志控制框架,经过大量回调函数钩子,你彻底能够编写本身的函数来替代内部默认实现的功能。 还有不少其它回调函数钩子能实现更有趣的功能,你慢慢探索吧 ^_^ 是否是越看越心动了?那就赶忙下载来玩玩吧 首页传送门 : [url]http://git.oschina.net/calvinwilliams/iLOG3[/url] 源代码包doc目录中包含了用户指南和参考手册,里面有更详尽的说明 (本文中的源代码取自iLOG3姐妹库iLOG3SERVER,后续即将放出)