转载请注明出处:http://blog.csdn.net/jmppok/article/details/17375057
C++程序在后台运行时,可通过log4cplus记录日志。当C++程序运行在远程服务器上时,我们就需要远程登陆到该服务器才能查看日志。进一步,如果该C++程序一个并行程序或者分布式程序,为了查看程序的运行状态,我们就需要登陆到N台服务器上,tail -f xx.log.这种情形听起来就很令人不爽,而实际上,很多服务端开发者都遇到过或正在遭受这个问题的困扰。
作为Log4J的翻版,Log4cplus也提供了SockeAppender,可以通过SocketAppender将日志输出到一个指定的log server上,从而解决上述问题。
关于Log4cplus的介绍,请参考C++开源日志库log4cplus
在Log4cplus的源码包中,有一个loggingServer目录,该目录中实现了一个LoggingServer。
在编译Log4cplus时,会自动编译该目录,在目录中生成loggingServer可执行文件,当然可以自己make(需要依赖log4cplus库)。
loggingServer使用方式如下:
./loggingserver 9000 log4cplus.properties
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
// 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); } }
前面启动了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....,不过貌似也没这个必要...
当然我们可以实现自己的loggingserver,更加的高端大气上档次。
a.将来自不同客户端的日志分别存储;
b.不同级别的日志单独存储;
c.在发现错误时,邮件通知;
d.实现一个B/S的日志浏览工具;
e.日志入库;
f.存入HDFS,用Hadoop Map/Reduce挖掘;
g.接入Storm(实时流处理框架)对其进行分析;
...