使用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