C++日志库log4cplus:SocketAppender记录日志到log Server

转载请注明出处:http://blog.csdn.net/jmppok/article/details/17375057

1.问题

C++程序在后台运行时,可通过log4cplus记录日志。当C++程序运行在远程服务器上时,我们就需要远程登陆到该服务器才能查看日志。进一步,如果该C++程序一个并行程序或者分布式程序,为了查看程序的运行状态,我们就需要登陆到N台服务器上,tail -f xx.log.这种情形听起来就很令人不爽,而实际上,很多服务端开发者都遇到过或正在遭受这个问题的困扰。


2.LoggingServer

作为Log4J的翻版,Log4cplus也提供了SockeAppender,可以通过SocketAppender将日志输出到一个指定的log server上,从而解决上述问题。

关于Log4cplus的介绍,请参考C++开源日志库log4cplus


在Log4cplus的源码包中,有一个loggingServer目录,该目录中实现了一个LoggingServer。

在编译Log4cplus时,会自动编译该目录,在目录中生成loggingServer可执行文件,当然可以自己make(需要依赖log4cplus库)。

loggingServer使用方式如下:

 ./loggingserver 9000 log4cplus.properties

9000表示监听的端口号(不需要地址,默认监听本机地址)

log4cplus.properties是一个log4cplus的配置文件,和普通的log4cplus配置文件相同,loggingserver收到各个socket发来的日志后,根据配置文件信息,将其写入文件。

下面是一个简单的配置文件示例:

log4cplus.rootLogger=DEBUG, STDOUT, ALL_MSGS


log4cplus.appender.STDOUT=log4cplus::ConsoleAppender
log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout
#log4cplus.appender.STDOUT.layout.ConversionPattern=%d{%m/%d/%y %H:%M:%S} [%t] %-5p %c{2} %%%x%% - %m [%l]%n
log4cplus.appender.STDOUT.layout.ConversionPattern=[%-5p %d{%y-%m-%d %H:%M:%S}] [%l]%n%m%n%n


#设置日志追加到文件尾
log4cplus.appender.ALL_MSGS=log4cplus::RollingFileAppender  

#设置日志文件大小
log4cplus.appender.ALL_MSGS.MaxFileSize=100MB

#设置生成日志最大个数
log4cplus.appender.ALL_MSGS.MaxBackupIndex=10

#设置输出日志路径
log4cplus.appender.ALL_MSGS.File=log/test.log
log4cplus.appender.ALL_MSGS.layout=log4cplus::PatternLayout
#设置日志打印格式
#log4cplus.appender.ALL_MSGS.layout.ConversionPattern=|%D:%d{%Q}|%p|%t|%l|%m|%n
log4cplus.appender.ALL_MSGS.layout.ConversionPattern=[%-5p %d{%y-%m-%d %H:%M:%S}] [%l]%n%m%n%n
#匹配相同日志级别,只有debug日志才输入到该文件中
#log4cplus.appender.ALL_MSGS.filters.1=log4cplus::spi::LogLevelMatchFilter
#log4cplus.appender.DEBUG_MSGS.filters.1.LogLevelToMatch=DEBUG
#log4cplus.appender.DEBUG_MSGS.filters.1.AcceptOnMatch=true
#log4cplus.appender.DEBUG_MSGS.filters.2=log4cplus::spi::DenyAllFilter

loggingserver本身十分简单,其代码如下(当然如果觉得它不爽,你也可以自己实现一个更cool的):

// Module:  LOG4CPLUS
// File:    loggingserver.cxx
// Created: 5/2003
// Author:  Tad E. Smith
//
//
// Copyright 2003-2010 Tad E. Smith
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cstdlib>
#include <iostream>
#include <log4cplus/configurator.h>
#include <log4cplus/socketappender.h>
#include <log4cplus/helpers/socket.h>
#include <log4cplus/thread/threads.h>
#include <log4cplus/spi/loggingevent.h>


namespace loggingserver
{

class ClientThread : public log4cplus::thread::AbstractThread
{
public:
    ClientThread(log4cplus::helpers::Socket clientsock)
    : clientsock(clientsock) 
    {
        std::cout << "Received a client connection!!!!" << std::endl;
    }

    ~ClientThread()
    {
        std::cout << "Client connection closed." << std::endl;
    }

    virtual void run();

private:
    log4cplus::helpers::Socket clientsock;
};

}




int
main(int argc, char** argv)
{
    if(argc < 3) {
        std::cout << "Usage: port config_file" << std::endl;
        return 1;
    }
    int port = std::atoi(argv[1]);
    const log4cplus::tstring configFile = LOG4CPLUS_C_STR_TO_TSTRING(argv[2]);

    log4cplus::PropertyConfigurator config(configFile);
    config.configure();

    log4cplus::helpers::ServerSocket serverSocket(port);
    if (!serverSocket.isOpen()) {
        std::cout << "Could not open server socket, maybe port "
            << port << " is already in use." << std::endl;
        return 2;
    }

    while(1) {
        loggingserver::ClientThread *thr = 
            new loggingserver::ClientThread(serverSocket.accept());
        thr->start();
    }

    return 0;
}


////////////////////////////////////////////////////////////////////////////////
// loggingserver::ClientThread implementation
////////////////////////////////////////////////////////////////////////////////


void
loggingserver::ClientThread::run()
{
    while(1) {
        if(!clientsock.isOpen()) {
            return;
        }
        log4cplus::helpers::SocketBuffer msgSizeBuffer(sizeof(unsigned int));
        if(!clientsock.read(msgSizeBuffer)) {
            return;
        }

        unsigned int msgSize = msgSizeBuffer.readInt();

        log4cplus::helpers::SocketBuffer buffer(msgSize);
        if(!clientsock.read(buffer)) {
            return;
        }
        
        log4cplus::spi::InternalLoggingEvent event
            = log4cplus::helpers::readFromBuffer(buffer);
        log4cplus::Logger logger
            = log4cplus::Logger::getInstance(event.getLoggerName());
        logger.callAppenders(event);   
    }
}

3.应用程序中SocketAppender配置

前面启动了LoggingServer,下面说一下需要收集其日志的各个应用程序中配置。

说白了,就是在原来的基础上加一个SocketAppender,SocketAppender会自动将日志发送给loggingserver。这样直接在loggingserver上就可以查看所有服务器上的日志信息啦。哥终于不用再担心你们的运行状态啦!

log4cplus.rootLogger=DEBUG, STDOUT, ALL_MSGS,RemoteServer


log4cplus.appender.STDOUT=log4cplus::ConsoleAppender
log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout
#log4cplus.appender.STDOUT.layout.ConversionPattern=%d{%m/%d/%y %H:%M:%S} [%t] %-5p %c{2} %%%x%% - %m [%l]%n
log4cplus.appender.STDOUT.layout.ConversionPattern=[%-5p %d{%y-%m-%d %H:%M:%S}] [%l]%n%m%n%n


#设置日志追加到文件尾
log4cplus.appender.ALL_MSGS=log4cplus::RollingFileAppender  

#设置日志文件大小
log4cplus.appender.ALL_MSGS.MaxFileSize=100MB

#设置生成日志最大个数
log4cplus.appender.ALL_MSGS.MaxBackupIndex=10

#设置输出日志路径
log4cplus.appender.ALL_MSGS.File=log/test.log
log4cplus.appender.ALL_MSGS.layout=log4cplus::PatternLayout
#设置日志打印格式
#log4cplus.appender.ALL_MSGS.layout.ConversionPattern=|%D:%d{%Q}|%p|%t|%l|%m|%n
log4cplus.appender.ALL_MSGS.layout.ConversionPattern=[%-5p %d{%y-%m-%d %H:%M:%S}] [%l]%n%m%n%n
#匹配相同日志级别,只有debug日志才输入到该文件中
#log4cplus.appender.ALL_MSGS.filters.1=log4cplus::spi::LogLevelMatchFilter
#log4cplus.appender.DEBUG_MSGS.filters.1.LogLevelToMatch=DEBUG
#log4cplus.appender.DEBUG_MSGS.filters.1.AcceptOnMatch=true
#log4cplus.appender.DEBUG_MSGS.filters.2=log4cplus::spi::DenyAllFilter

log4cplus.appender.RemoteServer=log4cplus::SocketAppender
log4cplus.appender.RemoteServer.host=localhost
log4cplus.appender.RemoteServer.port=9000



上面第一行的RemoteServer和最后三行即为添加一个SocketAppender。将日志发送到远端的loggingserver。

当然对本地的日志记录没有影响,本地仍正常记录。

同时我们还可以设置多个RemoetServer....,不过貌似也没这个必要...


4.更加高端大气上档次

当然我们可以实现自己的loggingserver,更加的高端大气上档次。

        a.将来自不同客户端的日志分别存储;

        b.不同级别的日志单独存储;

        c.在发现错误时,邮件通知;

        d.实现一个B/S的日志浏览工具;

        e.日志入库;

        f.存入HDFS,用Hadoop Map/Reduce挖掘;

        g.接入Storm(实时流处理框架)对其进行分析;

        ...


你可能感兴趣的:(log4cplus,SocketAppender,loggingserver)