開源純C日誌函數庫iLOG3快速入門(2、定製遠程日誌服務)

開源純C日誌函數庫iLOG3快速入門(2、定製遠程日誌服務)

前面一篇講了如何使用iLOG3寫日誌,從日誌句柄LOG到日誌句柄集合LOGS實現單一輸出介質到多輸出介質。
iLOG3輸出介質不少,除了文件外還有標準輸出、標準錯誤輸出、syslog或WINDOWS EVENT等,這篇介紹如何利用輸出介質回調函數實現日誌遠程落地,在某些場合下很須要這樣的功能。
首先設計通信過程,一個日誌句柄扮演一個日誌輸出客戶端角色,在輸出日誌前先鏈接上日誌服務器,發送文件名,服務器打開本地日誌文件,客戶端不停的發送格式化好的日誌數據,服務器接收並落地,最後客戶端關閉日誌句柄前斷開與日誌服務器鏈接,服務器關閉本地日誌文件。

看設置輸出類型函數原型

/* 自定義打開、輸出、關閉日誌函數類型 */
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 );





通常咱們輸出到文件,output設置爲LOG_OUTPUT_FILE,這時最後六個回調函數鉤子都設置爲NULL,或者用宏LOG_NO_OUTPUTFUNC。這裏咱們想自定義打開日誌、輸出日誌、關閉日誌邏輯,編寫三個函數掛上去,同時output設置爲LOG_OUTPUT_CALLBACK,iLOG3將用用戶自定義函數替代內部缺省的打開、輸出、關閉日誌函數。由於設計成長鏈接,自定義打開日誌函數掛在pfuncOpenLogFirst上,自定義輸出日誌函數掛在pfuncWriteLog上,自定義關閉日誌函數掛在pfuncCloseLogFinally上。若是你想改爲短鏈接,打開和關閉函數改掛在pfuncOpenLog、pfuncCloseLog上便可。也就是說pfuncOpenLogFirst和pfuncCloseLogFinally只作一次,pfuncOpenLog和pfuncCloseLog在每次輸出日誌先後都作。

三個回調函數代碼以下:
注意:代碼中使用了大量封裝網絡通信的函數庫iSocket

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;
}




 
函數funcOpenLog_ConnectToLogServer掛在只執行一次的打開日誌回調函數鉤子上,當設置輸出類型時最後自動調用,首先申請日誌打開環境所需內存,而後分析文件名和網絡地址,而後建立客戶端socket鏈接上服務端,發送文件名,鏈接協議爲「(文件名長度)(1字節)|(文件名)(n字節,值爲前一字節值)」。
函數funcWriteLog_SendToLogServer掛在輸出日誌回調函數鉤子上,每第二天志輸出都會調用,不停的發送格式化好的日誌數據到遠程服務器。
函數funcCloseLog_DisconnectFromLogServer掛在只執行一次的關閉日誌回調函數鉤子上,當關閉日誌句柄時自動調用,斷開socket鏈接,釋放日誌打開環境所佔內存。

編寫測試代碼以下:

#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();
}





請注意,函數SetLogOutput的日誌文件名參數設置成"$ProgramFiles$/test_iLOG3SERVER.log @127.0.0.1 :7878",是傳遞給自定義打開日誌回調函數解析用的,你也能夠根據喜愛設計其它格式。

想運行?別急,尚未日誌服務器呢,下面簡單寫一個吧,基於select事件模型,一樣使用了大量封裝過的iLibX、iSocket函數庫函數,有註釋呢,應該看得懂
server.h

#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,後續即將放出)
相關文章
相關標籤/搜索