1. 程式人生 > >使用log4cplus實現的分目錄存放日誌伺服器完整工程+關鍵客戶端程式碼

使用log4cplus實現的分目錄存放日誌伺服器完整工程+關鍵客戶端程式碼

因為專案原因需要集中記錄各終端的日誌,而且需要將各終端的日誌分目錄存放。

各終端記錄日誌的問題大家可以百度,這裡是用的配置檔案實現的檔案,格式,過濾器配置。本文主要說明服務端的功能,但是附件的程式碼包中的log4cplus庫+標頭檔案可以編譯出客戶端程式碼。

為實現上述功能,首先需要解決server端識別客戶端的問題,但是log4cpus原始碼中沒有實現客戶端的ip獲取,所以需要自己實現。

關鍵程式碼如下(本文基於log4Cplus1.0.3-rc9,程式碼包請到csdn下載資源中搜索資源:“使用log4cplus實現的分目錄存放日誌伺服器完整工程”):


1 修改socket.h暴露原本保護的成員,並新增一個介面acceptSocket2以獲取客戶端的ip。

// Module:  Log4CPLUS
// File:    socket.h
// Created: 4/2003
// Author:  Tad E. Smith
//
//
// Copyright (C) Tad E. Smith  All rights reserved.
//
// This software is published under the terms of the Apache Software
// License version 1.1, a copy of which has been included with this
// distribution in the LICENSE.APL file.
//


/** @file */


#ifndef LOG4CPLUS_HELPERS_SOCKET_HEADER_
#define LOG4CPLUS_HELPERS_SOCKET_HEADER_


#include <log4cplus/config.hxx>
#include <log4cplus/tstring.h>
#include <log4cplus/helpers/socketbuffer.h>
#if defined(_WIN32)
#include <winsock.h>
#endif


namespace log4cplus {
    namespace helpers {


        enum SocketState { ok,
                           not_opened,
                           bad_address,
                           connection_failed,
                           broken_pipe, 
                           invalid_access_mode,
                           message_truncated
                         };


#if !defined(_WIN32)
        typedef int SOCKET_TYPE;
#define INVALID_SOCKET -1
#else
        typedef SOCKET SOCKET_TYPE;
#endif


        class LOG4CPLUS_EXPORT AbstractSocket {
        public:
          // ctor and dtor
            AbstractSocket();
            AbstractSocket(SOCKET_TYPE sock, SocketState state, int err);
            AbstractSocket(const AbstractSocket&);
            virtual ~AbstractSocket() = 0;


          // methods
            /// Close socket
            virtual void close();
            virtual bool isOpen() const;


            AbstractSocket& operator=(const AbstractSocket& rhs);


SOCKET_TYPE sock;//


        protected:
          // Methods
            virtual void copy(const AbstractSocket& rhs);


          // Data
            //SOCKET_TYPE sock; before modify code
            SocketState state;
            int err;
        };






        /**
         * This class implements client sockets (also called just "sockets").
         * A socket is an endpoint for communication between two machines.
         */
        class LOG4CPLUS_EXPORT Socket : public AbstractSocket {
        public:
          // ctor and dtor
            Socket();
            Socket(SOCKET_TYPE sock, SocketState state, int err);
            Socket(const tstring& address, int port);
            virtual ~Socket();


          // methods
            virtual bool read(SocketBuffer& buffer);
            virtual bool write(const SocketBuffer& buffer);
        };






        /**
         * This class implements server sockets. A server socket waits for
         * requests to come in over the network. It performs some operation
         * based on that request, and then possibly returns a result to the
         * requester.
         */
        class LOG4CPLUS_EXPORT ServerSocket : public AbstractSocket {
        public:
          // ctor and dtor
            ServerSocket(int port);
            virtual ~ServerSocket();


            Socket accept();
char m_peerAddr[14];  // wganghy add
        };




        LOG4CPLUS_EXPORT SOCKET_TYPE openSocket(unsigned short port, SocketState& state);
        LOG4CPLUS_EXPORT SOCKET_TYPE connectSocket(const log4cplus::tstring& hostn,
                                                   unsigned short port, SocketState& state);
        LOG4CPLUS_EXPORT SOCKET_TYPE acceptSocket(SOCKET_TYPE sock, SocketState& state);
LOG4CPLUS_EXPORT SOCKET_TYPE acceptSocket2(SOCKET_TYPE sock, char *peerAddr,SocketState& state);//wganghy add
        LOG4CPLUS_EXPORT int closeSocket(SOCKET_TYPE sock);


        LOG4CPLUS_EXPORT long read(SOCKET_TYPE sock, SocketBuffer& buffer);
        LOG4CPLUS_EXPORT long write(SOCKET_TYPE sock, const SocketBuffer& buffer);


    } // end namespace helpers
} // end namespace log4cplus


#endif // LOG4CPLUS_HELPERS_SOCKET_HEADER_

對應的cpp關鍵修改:

SOCKET_TYPE
log4cplus::helpers::acceptSocket2(SOCKET_TYPE sock,  char *peerAddr,SocketState&/*state*/)
{
init_winsock ();


struct sockaddr_in sa;
int len = sizeof(sa);
SOCKET_TYPE tType = ::accept(sock, (struct sockaddr *)&sa, &len); //改前是 ::accept(sock, NULL, NULL);
strncpy(peerAddr, inet_ntoa(sa.sin_addr),len);
//printf("addr: %s, %d\n",  inet_ntoa(sa.sin_addr), len);
return tType;
}

2  server端啟動監聽:

int  main() 
{  
IniFile *pIniFile = new IniFile;
if(false == pIniFile->ReadIniFile("CarIp.txt")){
std::cout<<"not found CarIP file"<<endl;
return 0;
}


if( !IsDirExist("comLog\\")){  system("mkdir comLog");  }


int carIdx = 1;
string strCarNo = "";
while(carIdx < 21){
char carNo[4] = {0};
itoa(carIdx, carNo, 10);
strCarNo = "car";
strCarNo.append(carNo);
string strCarIP = pIniFile->GetString("車號IP", strCarNo, "");

string sinCarLogDir = string("comLog\\") + strCarNo;
string sinCarLogDirWithT  = sinCarLogDir + string("\\");
string crtSinCarLogDirCmd = string("mkdir ") + sinCarLogDirWithT;
if( !IsDirExist(sinCarLogDir)){  system(crtSinCarLogDirCmd.c_str());  }


g_IpCarInfoMap[strCarIP] = CarLogInfo(strCarNo, sinCarLogDirWithT);


carIdx++;
}


PropertyConfigurator::doConfigure("ComDataLog.cfg");
log4cplus::helpers::ServerSocket serverSocket(9000);
if (!serverSocket.isOpen()) {
std::cout << "Could not listen socket:"<< 9000<< std::endl;
return 2;  
}  


while(1) {  
log4cplus::helpers::Socket clSocket = serverSocket.accept();//阻塞程序  
loggingserver::ClientThread *thr = new loggingserver::ClientThread(clSocket,serverSocket.m_peerAddr);  
thr->start(); 
}  


return 0;  
}  

3 附件包含客戶端的日誌配置檔案client-log.cfg,注意其中要向server傳送日誌必須的下列配置一定是server電腦的計算機名:

#掛接器-----------------------------------------------------------------------------------遠端輸出
log4cplus.appender.RmServer=log4cplus::SocketAppender
log4cplus.appender.RmServer.layout=log4cplus::PatternLayout
log4cplus.appender.RmServer.layout.ConversionPattern=%D{%Y%m%d|%H:%M:%S%q}|%t|%m%n
log4cplus.appender.RmServer.filters.1=log4cplus::spi::LogLevelRangeFilter
log4cplus.appender.RmServer.filters.1.LogLevelMin=TRACE
log4cplus.appender.RmServer.filters.1.LogLevelMax=WARN
log4cplus.appender.RmServer.filters.1.AcceptOnMatch=true
log4cplus.appender.RmServer.filters.2=log4cplus::spi::DenyAllFilter
log4cplus.appender.RmServer.host=logserver-pc-name
log4cplus.appender.RmServer.port=9000

4  客戶端關鍵程式碼:初始化日誌模組

//日誌類相關------------begin
#include <iomanip>
#include <log4cplus/logger.h>
#include <log4cplus/configurator.h>
#include <log4cplus/helpers/loglog.h>
#include <log4cplus/helpers/stringhelper.h>

using namespace log4cplus;
using namespace log4cplus::helpers;


#if ((defined DEBUG)||(defined _DEBUG)) 
#pragma comment(lib,"log4cplusSD.lib")
#else
#pragma comment(lib,"log4cplusS.lib")
#endif


#define  COM_LOG_NAME ("comLog")
//日誌類相關-----------end

void InitGlbLog()
{
if( !IsDirExist("comLog"))        {  system("md comLog");       }
if( !IsDirExist("ComPlaybackLog")){ system("md ComPlaybackLog");}

Logger rootlog = Logger::getRoot();
Logger comLog  = Logger::getInstance(COM_LOG_NAME );

PropertyConfigurator::doConfigure(COM_LOG_CFG_PATH);// COM_LOG_CFG_PATH自定義的日誌路徑
}

5 注意客戶端,server端的工程中都需要增加預編譯巨集:LOG4CPLUS_STATIC