最近的项目需要做一个生物识别的分布式比对服务,在机器集群上如何做日志汇总和分析?ELK很不错,但是公司原来都是使用Log4cpp,能不能在改动最小的情况下,将日志输出到ELK?当然没有问题,Logstash有可以从kafka输入,只需要实现日志输出到kafka,剩下的ELK配置好就ok!但是Log4cpp并没有提供kafka输出,自定义一个即可!
文章目录
1. 下载Log4cpp
- 下载 Log library for C++
- 解压
2. 编码
只需两步:
- 添加KafkaAppender类,继承LayoutAppender
- 修改PropertyConfiguratorImpl配置类
2.1 添加KafkaAppender实现
2.1.1 添加KafkaAppender.hh
将KafkaAppender.hh添加到include/log4cpp下
后缀.hh是c++的头文件,为了和log4cpp统一而没用.h;.hh是为了区分c语言的.h头文件
// include/log4cpp/KafkaAppender.hh
#ifndef _LOG4CPP_KAFKAAPPENDER_HH
#define _LOG4CPP_KAFKAAPPENDER_HH
#include <log4cpp/Portability.hh>
#include <log4cpp/LayoutAppender.hh>
#include <string>
#include <stdarg.h>
#include <librdkafka/rdkafkacpp.h>
#include <librdkafka/rdkafka.h>
namespace log4cpp
{
// 数据异步发送结果回调
class ExampleDeliveryReportCb : public RdKafka::DeliveryReportCb
{
public:
void dr_cb(RdKafka::Message &message);
};
class LOG4CPP_EXPORT KafkaAppender : public LayoutAppender
{
public:
KafkaAppender(const std::string &name, const std::string &brokers, const std::string &topic, int timeout);
virtual ~KafkaAppender();
virtual bool reopen();
virtual void close();
protected:
virtual void _append(const LoggingEvent &event);
const std::string _brokers;
const std::string _topic;
const int _timeout;
RdKafka::Producer *_producer = nullptr;
int32_t _partition = RdKafka::Topic::PARTITION_UA;
ExampleDeliveryReportCb *ex_dr_cb = nullptr;
};
} // namespace log4cpp
#endif
2.1.2 添加KafkaAppender.cpp
在src下添加KafkaAppender.cpp文件
// src/KafkaAppender.cpp
#include "PortabilityImpl.hh"
#ifdef LOG4CPP_HAVE_IO_H
#include <io.h>
#endif
#ifdef LOG4CPP_HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <memory>
#include <stdio.h>
#include <time.h>
#include <log4cpp/KafkaAppender.hh>
#include <log4cpp/Category.hh>
#include <log4cpp/FactoryParams.hh>
namespace log4cpp
{
// 数据异步发送结果回调
void ExampleDeliveryReportCb::dr_cb(RdKafka::Message &message)
{
std::cout << "ExampleDeliveryReportCb dr_cb" << std::endl;
std::string status_name;
switch (message.status())
{
case RdKafka::Message::MSG_STATUS_NOT_PERSISTED:
status_name = "NotPersisted";
break;
case RdKafka::Message::MSG_STATUS_POSSIBLY_PERSISTED:
status_name = "PossiblyPersisted";
break;
case RdKafka::Message::MSG_STATUS_PERSISTED:
status_name = "Persisted";
break;
default:
status_name = "Unknown?";
break;
}
std::cout << "Message delivery for (" << message.len() << " bytes): " << status_name << ": " << message.errstr() << std::endl;
if (message.key())
std::cout << "Key: " << *(message.key()) << ";" << std::endl;
}
KafkaAppender::KafkaAppender(const std::string &name, const std::string &brokers, const std::string &topic, int timeout)
: LayoutAppender(name), _brokers(brokers), _topic(topic), _timeout(timeout)
{
std::cout << "kafka appender name : " << name << ", brokers : " << brokers << ",topic " << _topic << ",timeout " << _timeout << std::endl;
// 连接kafka
std::string errstr;
int64_t start_offset = RdKafka::Topic::OFFSET_BEGINNING;
/*
* Create configuration objects
*/
RdKafka::Conf *conf = RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL);
RdKafka::Conf *tconf = RdKafka::Conf::create(RdKafka::Conf::CONF_TOPIC);
// 设置brokers
conf->set("metadata.broker.list", brokers, errstr);
/* Set delivery report callback 异步数据发送结果回调 */
ex_dr_cb = new ExampleDeliveryReportCb;
conf->set("dr_cb", ex_dr_cb, errstr);
// 设置topic
conf->set("default_topic_conf", tconf, errstr);
// 创建producer
_producer = RdKafka::Producer::create(conf, errstr);
if (!_producer)
{
std::cerr << "Failed to create producer: " << errstr << std::endl;
exit(1);
}
}
KafkaAppender::~KafkaAppender()
{
// 析构,释放kafka
close();
}
bool KafkaAppender::reopen()
{
// 重新打开kafka
}
void KafkaAppender::close()
{
// 关闭kafka连接
while (_producer->outq_len() > 0)
{
std::cerr << "Waiting for " << _producer->outq_len() << std::endl;
_producer->poll(1000);
}
delete _producer;
_producer = nullptr;
RdKafka::wait_destroyed(5000);
delete ex_dr_cb;
ex_dr_cb = nullptr;
}
void KafkaAppender::_append(const LoggingEvent &event)
{
std::string message(_getLayout().format(event));
std::cout << "kafka appender " << message << std::endl;
// 写入kafka
RdKafka::Headers *headers = RdKafka::Headers::create();
headers->add("my header", "header value");
headers->add("other header", "yes");
RdKafka::ErrorCode resp =
_producer->produce(_topic, _partition,
RdKafka::Producer::RK_MSG_COPY /* Copy payload */,
/* Value */
const_cast<char *>(message.c_str()), message.size(),
/* Key */
NULL, 0,
/* Timestamp (defaults to now) */
0,
/* Message headers, if any */
headers,
/* Per-message opaque value passed to
* delivery report */
NULL);
if (resp != RdKafka::ERR_NO_ERROR)
{
std::cerr << "% Produce failed: " << RdKafka::err2str(resp) << std::endl;
delete headers; /* Headers are automatically deleted on produce()
* success. */
}
else
{
std::cerr << "% Produced message (" << message.size() << " bytes)" << std::endl;
}
_producer->poll(_timeout);
}
} // namespace log4cpp
2.2 修改PropertyConfiguratorImpl.cpp,添加Kafka配置
添加kafka配置,修改PropertyConfiguratorImpl.cpp
/*
* PropertyConfiguratorImpl.cpp
*
* Copyright 2002, Log4cpp Project. All rights reserved.
*
* See the COPYING file for the terms of usage and distribution.
*/
#include "PortabilityImpl.hh"
#ifdef LOG4CPP_HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef LOG4CPP_HAVE_IO_H
#include <io.h>
#endif
#include <iostream>
#include <string>
#include <fstream>
#include <log4cpp/Category.hh>
// appenders
#include <log4cpp/Appender.hh>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/RollingFileAppender.hh>
#include <log4cpp/DailyRollingFileAppender.hh>
#include <log4cpp/AbortAppender.hh>
#include <log4cpp/KafkaAppender.hh>
#ifdef WIN32
#include <log4cpp/Win32DebugAppender.hh>
#include <log4cpp/NTEventLogAppender.hh>
#endif
#ifndef LOG4CPP_DISABLE_REMOTE_SYSLOG
#include <log4cpp/RemoteSyslogAppender.hh>
#endif // LOG4CPP_DISABLE_REMOTE_SYSLOG
#ifdef LOG4CPP_HAVE_LIBIDSA
#include <log4cpp/IdsaAppender.hh>
#endif // LOG4CPP_HAVE_LIBIDSA
#ifdef LOG4CPP_HAVE_SYSLOG
#include <log4cpp/SyslogAppender.hh>
#endif
// layouts
#include <log4cpp/Layout.hh>
#include <log4cpp/BasicLayout.hh>
#include <log4cpp/SimpleLayout.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/Priority.hh>
#include <log4cpp/NDC.hh>
#include <list>
#include <vector>
#include <iterator>
#include <algorithm>
#include "PropertyConfiguratorImpl.hh"
#include "StringUtil.hh"
namespace log4cpp
{
PropertyConfiguratorImpl::PropertyConfiguratorImpl()
{
}
PropertyConfiguratorImpl::~PropertyConfiguratorImpl()
{
}
// 根据配置文件配置log4cpp
void PropertyConfiguratorImpl::doConfigure(const std::string &initFileName)
{
std::ifstream initFile(initFileName.c_str());
if (!initFile)
{
throw ConfigureFailure(std::string("File ") + initFileName + " does not exist");
}
doConfigure(initFile);
}
// 使用输入流配置log4cpp
void PropertyConfiguratorImpl::doConfigure(std::istream &in)
{
// parse the file to get all of the configuration
// 解析配置
_properties.load(in);
instantiateAllAppenders();
// get categories
std::vector<std::string> catList;
getCategories(catList);
// configure each category
for (std::vector<std::string>::const_iterator iter = catList.begin();
iter != catList.end(); ++iter)
{
configureCategory(*iter);
}
}
// 初始化全部appender
void PropertyConfiguratorImpl::instantiateAllAppenders()
{
std::string currentAppender;
std::string prefix("appender");
Properties::const_iterator from = _properties.lower_bound(prefix + '.');
Properties::const_iterator to = _properties.lower_bound(prefix + '/');
for (Properties::const_iterator i = from; i != to; ++i)
{
const std::string &key = (*i).first;
const std::string &value = (*i).second;
std::list<std::string> propNameParts;
std::back_insert_iterator<std::list<std::string>> pnpIt(propNameParts);
StringUtil::split(pnpIt, key, '.');
std::list<std::string>::const_iterator i2 = propNameParts.begin();
std::list<std::string>::const_iterator iEnd = propNameParts.end();
if (++i2 == iEnd)
{
throw ConfigureFailure(std::string("missing appender name"));
}
const std::string appenderName = *i2++;
/* WARNING, approaching lame code section:
skipping of the Appenders properties only to get them
again in instantiateAppender.
*/
if (appenderName == currentAppender)
{
// simply skip properties for the current appender
}
else
{
if (i2 == iEnd)
{
// a new appender
currentAppender = appenderName;
_allAppenders[currentAppender] =
instantiateAppender(currentAppender);
}
else
{
throw ConfigureFailure(std::string("partial appender definition : ") + key);
}
}
}
}
// 配置category
void PropertyConfiguratorImpl::configureCategory(const std::string &categoryName)
{
// start by reading the "rootCategory" key
std::string tempCatName =
(categoryName == "rootCategory") ? categoryName : "category." + categoryName;
Properties::iterator iter = _properties.find(tempCatName);
if (iter == _properties.end())
throw ConfigureFailure(std::string("Unable to find category: ") + tempCatName);
// need to get the root instance of the category
Category &category = (categoryName == "rootCategory") ? Category::getRoot() : Category::getInstance(categoryName);
std::list<std::string> tokens;
std::back_insert_iterator<std::list<std::string>> tokIt(tokens);
StringUtil::split(tokIt, (*iter).second, ',');
std::list<std::string>::const_iterator i = tokens.begin();
std::list<std::string>::const_iterator iEnd = tokens.end();
Priority::Value priority = Priority::NOTSET;
if (i != iEnd)
{
std::string priorityName = StringUtil::trim(*i++);
try
{
if (priorityName != "")
{
priority = Priority::getPriorityValue(priorityName);
}
}
catch (std::invalid_argument &e)
{
throw ConfigureFailure(std::string(e.what()) +
" for category '" + categoryName + "'");
}
}
try
{
category.setPriority(priority);
}
catch (std::invalid_argument &e)
{
throw ConfigureFailure(std::string(e.what()) +
" for category '" + categoryName + "'");
}
bool additive = _properties.getBool("additivity." + categoryName, true);
category.setAdditivity(additive);
category.removeAllAppenders();
for (/**/; i != iEnd; ++i)
{
std::string appenderName = StringUtil::trim(*i);
AppenderMap::const_iterator appIt =
_allAppenders.find(appenderName);
if (appIt == _allAppenders.end())
{
// appender not found;
throw ConfigureFailure(std::string("Appender '") +
appenderName + "' not found for category '" + categoryName + "'");
}
else
{
/* pass by reference, i.e. don't transfer ownership
*/
category.addAppender(*((*appIt).second));
}
}
}
Appender *PropertyConfiguratorImpl::instantiateAppender(const std::string &appenderName)
{
Appender *appender = NULL;
std::string appenderPrefix = std::string("appender.") + appenderName;
// determine the type by the appenderName
Properties::iterator key = _properties.find(appenderPrefix);
if (key == _properties.end())
throw ConfigureFailure(std::string("Appender '") + appenderName + "' not defined");
std::string::size_type length = (*key).second.find_last_of(".");
std::string appenderType = (length == std::string::npos) ? (*key).second : (*key).second.substr(length + 1);
// and instantiate the appropriate object ConsoleAppender使用OstreamAppender
if (appenderType == "ConsoleAppender")
{
std::string target = _properties.getString(appenderPrefix + ".target", "stdout");
std::transform(target.begin(), target.end(), target.begin(), ::tolower);
if (target.compare("stdout") == 0)
{
appender = new OstreamAppender(appenderName, &std::cout);
}
else if (target.compare("stderr") == 0)
{
appender = new OstreamAppender(appenderName, &std::cerr);
}
else
{
throw ConfigureFailure(appenderName + "' has invalid target '" + target + "'");
}
}
else if (appenderType == "FileAppender")
{
std::string fileName = _properties.getString(appenderPrefix + ".fileName", "foobar");
bool append = _properties.getBool(appenderPrefix + ".append", true);
appender = new FileAppender(appenderName, fileName, append);
}
else if (appenderType == "RollingFileAppender")
{
std::string fileName = _properties.getString(appenderPrefix + ".fileName", "foobar");
size_t maxFileSize = _properties.getInt(appenderPrefix + ".maxFileSize", 10 * 1024 * 1024);
int maxBackupIndex = _properties.getInt(appenderPrefix + ".maxBackupIndex", 1);
bool append = _properties.getBool(appenderPrefix + ".append", true);
appender = new RollingFileAppender(appenderName, fileName, maxFileSize, maxBackupIndex,
append);
}
else if (appenderType == "DailyRollingFileAppender")
{
std::string fileName = _properties.getString(appenderPrefix + ".fileName", "foobar");
unsigned int maxDaysKeep = _properties.getInt(appenderPrefix + ".maxDaysKeep", 0);
bool append = _properties.getBool(appenderPrefix + ".append", true);
appender = new DailyRollingFileAppender(appenderName, fileName, maxDaysKeep, append);
}
else if (appenderType == "KafkaAppender")
{
// 配置输入参数brokers、topic、timeout
std::string brokers = _properties.getString(appenderPrefix + ".brokers", "localhost:9092");
std::string topic = _properties.getString(appenderPrefix + ".topic", "log4cpp_kafka");
unsigned int timeout = _properties.getInt(appenderPrefix + ".timeout", 0);
// bool append = _properties.getBool(appenderPrefix + ".append", true);
appender = new KafkaAppender(appenderName, brokers, topic, timeout);
}
#ifndef LOG4CPP_DISABLE_REMOTE_SYSLOG
else if (appenderType == "SyslogAppender")
{
std::string syslogName = _properties.getString(appenderPrefix + ".syslogName", "syslog");
std::string syslogHost = _properties.getString(appenderPrefix + ".syslogHost", "localhost");
int facility = _properties.getInt(appenderPrefix + ".facility", -1) * 8; // * 8 to get LOG_KERN, etc. compatible values.
int portNumber = _properties.getInt(appenderPrefix + ".portNumber", -1);
appender = new RemoteSyslogAppender(appenderName, syslogName,
syslogHost, facility, portNumber);
}
#endif // LOG4CPP_DISABLE_REMOTE_SYSLOG
#ifdef LOG4CPP_HAVE_SYSLOG
else if (appenderType == "LocalSyslogAppender")
{
std::string syslogName = _properties.getString(appenderPrefix + ".syslogName", "syslog");
int facility = _properties.getInt(appenderPrefix + ".facility", -1) * 8; // * 8 to get LOG_KERN, etc. compatible values.
appender = new SyslogAppender(appenderName, syslogName, facility);
}
#endif // LOG4CPP_HAVE_SYSLOG
else if (appenderType == "AbortAppender")
{
appender = new AbortAppender(appenderName);
}
#ifdef LOG4CPP_HAVE_LIBIDSA
else if (appenderType == "IdsaAppender")
{
// default idsa name ???
std::string idsaName = _properties.getString(appenderPrefix + ".idsaName", "foobar");
appender = new IdsaAppender(appenderName, idsaname);
}
#endif // LOG4CPP_HAVE_LIBIDSA
#ifdef WIN32
// win32 debug appender
else if (appenderType == "Win32DebugAppender")
{
appender = new Win32DebugAppender(appenderName);
}
// win32 NT event log appender
else if (appenderType == "NTEventLogAppender")
{
std::string source = _properties.getString(appenderPrefix + ".source", "foobar");
appender = new NTEventLogAppender(appenderName, source);
}
#endif // WIN32
else
{
throw ConfigureFailure(std::string("Appender '") + appenderName +
"' has unknown type '" + appenderType + "'");
}
if (appender->requiresLayout())
{
setLayout(appender, appenderName);
}
// set threshold
std::string thresholdName = _properties.getString(appenderPrefix + ".threshold", "");
try
{
if (thresholdName != "")
{
appender->setThreshold(Priority::getPriorityValue(thresholdName));
}
}
catch (std::invalid_argument &e)
{
delete appender; // fix for #3109495
throw ConfigureFailure(std::string(e.what()) +
" for threshold of appender '" + appenderName + "'");
}
return appender;
}
void PropertyConfiguratorImpl::setLayout(Appender *appender, const std::string &appenderName)
{
// determine the type by appenderName
std::string tempString;
Properties::iterator key =
_properties.find(std::string("appender.") + appenderName + ".layout");
if (key == _properties.end())
throw ConfigureFailure(std::string("Missing layout property for appender '") +
appenderName + "'");
std::string::size_type length = (*key).second.find_last_of(".");
std::string layoutType = (length == std::string::npos) ? (*key).second : (*key).second.substr(length + 1);
Layout *layout;
// and instantiate the appropriate object
if (layoutType == "BasicLayout")
{
layout = new BasicLayout();
}
else if (layoutType == "SimpleLayout")
{
layout = new SimpleLayout();
}
else if (layoutType == "PatternLayout")
{
// need to read the properties to configure this one
PatternLayout *patternLayout = new PatternLayout();
key = _properties.find(std::string("appender.") + appenderName + ".layout.ConversionPattern");
if (key == _properties.end())
{
// leave default pattern
}
else
{
// set pattern
patternLayout->setConversionPattern((*key).second);
}
layout = patternLayout;
}
else
{
throw ConfigureFailure(std::string("Unknown layout type '" + layoutType +
"' for appender '") +
appenderName + "'");
}
appender->setLayout(layout);
}
/**
* Get the categories contained within the map of properties. Since
* the category looks something like "category.xxxxx.yyy.zzz", we need
* to search the entire map to figure out which properties are category
* listings. Seems like there might be a more elegant solution.
*/
void PropertyConfiguratorImpl::getCategories(std::vector<std::string> &categories) const
{
categories.clear();
// add the root category first
categories.push_back(std::string("rootCategory"));
// then look for "category."
std::string prefix("category");
Properties::const_iterator from = _properties.lower_bound(prefix + '.');
Properties::const_iterator to = _properties.lower_bound(prefix + (char)('.' + 1));
for (Properties::const_iterator iter = from; iter != to; iter++)
{
categories.push_back((*iter).first.substr(prefix.size() + 1));
}
}
} // namespace log4cpp
3. 编译
- CMakeLists.txt 添加src/KafkaAppender.c
#
# Nicholas Yue nicholas_yue@users.sourceforge.net
#
# Note:
# (1) A dummy file include/config.h is required (remance from configure)
# (2) Default installation directory is /usr/local, override with -DCMAKE_INSTALL_PREFIX="" during cmake
# invocation
# (3) Do the usual "make clean all" to build the library
# (4) To install either "make install" or "make install DESTDIR=<your directory>"
# (5) Need to include changes in include/log4cpp/Portability.hh for OSX to build
cmake_minimum_required (VERSION 2.8)
PROJECT ( LOG4CPP )
INCLUDE_DIRECTORIES ( include )
INCLUDE_DIRECTORIES ( . )
IF (WIN32)
ADD_DEFINITIONS ( -D_CRT_SECURE_NO_WARNINGS )
ELSE (WIN32)
IF (APPLE)
ADD_DEFINITIONS ( -DNDEBUG -DLOG4CPP_HAVE_SSTREAM )
ELSE (APPLE)
ADD_DEFINITIONS ( -pthread -DNDEBUG -DLOG4CPP_HAVE_SSTREAM )
ENDIF (APPLE)
ENDIF (WIN32)
IF ( CMAKE_BUILD_TYPE MATCHES "Debug" )
SET ( LOG4CPP_LIBRARY_NAME "log4cppD" )
ELSE ( CMAKE_BUILD_TYPE MATCHES "Debug" )
SET ( LOG4CPP_LIBRARY_NAME "log4cpp" )
ENDIF ( CMAKE_BUILD_TYPE MATCHES "Debug" )
ADD_LIBRARY ( ${LOG4CPP_LIBRARY_NAME}
src/Appender.cpp
src/AppenderSkeleton.cpp
src/AppendersFactory.cpp
src/BufferingAppender.cpp
src/FactoryParams.cpp
src/LayoutsFactory.cpp
src/LevelEvaluator.cpp
src/Localtime.cpp
src/PassThroughLayout.cpp
src/TriggeringEventEvaluatorFactory.cpp
src/LayoutAppender.cpp
src/FileAppender.cpp
src/DailyRollingFileAppender.cpp
src/RollingFileAppender.cpp
src/FixedContextCategory.cpp
src/IdsaAppender.cpp
src/OstreamAppender.cpp
src/StringQueueAppender.cpp
src/SyslogAppender.cpp
src/RemoteSyslogAppender.cpp
src/KafkaAppender.cpp
src/SimpleLayout.cpp
src/BasicLayout.cpp
src/PatternLayout.cpp
src/Category.cpp
src/CategoryStream.cpp
src/HierarchyMaintainer.cpp
src/Configurator.cpp
src/BasicConfigurator.cpp
src/SimpleConfigurator.cpp
src/PropertyConfigurator.cpp
src/PropertyConfiguratorImpl.cpp
src/LoggingEvent.cpp
src/Priority.cpp
src/NDC.cpp
src/Filter.cpp
src/TimeStamp.cpp
src/StringUtil.cpp
src/Properties.cpp
src/Win32DebugAppender.cpp
src/NTEventLogAppender.cpp
src/DllMain.cpp
src/DummyThreads.cpp
src/MSThreads.cpp
src/OmniThreads.cpp
src/PThreads.cpp
src/PortabilityImpl.cpp
src/AbortAppender.cpp
)
IF (WIN32)
TARGET_LINK_LIBRARIES (${LOG4CPP_LIBRARY_NAME} kernel32 user32 ws2_32 advapi32 )
SET_TARGET_PROPERTIES(${LOG4CPP_LIBRARY_NAME} PROPERTIES LINK_FLAGS /NODEFAULTLIB:msvcrt )
ENDIF (WIN32)
TARGET_LINK_LIBRARIES(${LOG4CPP_LIBRARY_NAME} rdkafka++)
INSTALL (
DIRECTORY include/log4cpp
DESTINATION include
PATTERN "config.h" EXCLUDE
PATTERN ".svn" EXCLUDE
PATTERN "*.am" EXCLUDE
PATTERN "*.in" EXCLUDE
)
INSTALL (
TARGETS ${LOG4CPP_LIBRARY_NAME}
ARCHIVE DESTINATION lib
)
- 生成config.h文件
./configure
- 编译
make
- 安装
sudo make install
4. 测试
在tests文件夹下有很多个测试文件,我们用testPropertyConfig.cpp来测试,只需要修改testConfig.log4cpp.properties配置文件即可:
# a simple test config
# log4j和log4cpp及其之前的数据都会被忽略,以${}引用环境变量和之前的kv值
log4j.rootCategory=DEBUG, rootAppender
log4j.category.sub1=,A1
log4j.category.sub2=INFO
log4j.category.sub1.sub2=ERROR, A2
log4j.category.sub1.sub2.sub3=INFO, A4, A5, A6, A7, A8
log4j.additivity.sub1.sub2=false
log4j.additivity.sub1.sub2.sub3=false
# 设置为KafkaAppender,设置brokers、topic、timeout
log4j.appender.rootAppender=org.apache.log4j.KafkaAppender
log4j.appender.rootAppender.brokers=localhost:9092
log4j.appender.rootAppender.topic=pt
log4j.appender.rootAppender.timeout=0
log4j.appender.rootAppender.layout=org.apache.log4j.BasicLayout
log4j.appender.A1=org.apache.log4j.AbortAppender
log4j.appender.A1.fileName=A1.log
log4j.appender.A1.layout=org.apache.log4j.SimpleLayout
log4j.appender.A2=org.apache.log4j.ConsoleAppender
log4j.appender.A2.layout=org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern=The message '%m' at time %d thread %t%n
# tests for looong strings within the config file
log4cpp.appender.A4=RollingFileAppender
log4cpp.appender.A4.fileName=A4.log
log4cpp.appender.A4.maxFileSize=10240
log4cpp.appender.A4.maxBackupIndex=1
log4cpp.appender.A4.layout=PatternLayout
log4cpp.appender.A4.layout.ConversionPattern=The message %m at time %d{%H:%M} thread %t %n is going to be very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong%n
log4cpp.appender.A5=RollingFileAppender
log4cpp.appender.A5.fileName=A4.log
log4cpp.appender.A5.maxFileSize=10240
log4cpp.appender.A5.maxBackupIndex=1
log4cpp.appender.A5.layout=PatternLayout
log4cpp.appender.A5.layout.ConversionPattern=The message %m at time %d{%H:%M} thread %t %n is going to be very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongg%n
log4cpp.appender.A6=RollingFileAppender
log4cpp.appender.A6.fileName=A4.log
log4cpp.appender.A6.maxFileSize=10240
log4cpp.appender.A6.maxBackupIndex=1
log4cpp.appender.A6.layout=PatternLayout
log4cpp.appender.A6.layout.ConversionPattern=The message %m at time %d{%H:%M} thread %t %n is going to be very loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooonggg%n
log4cpp.appender.A7=RollingFileAppender
log4cpp.appender.A7.fileName=A4.log
log4cpp.appender.A7.maxFileSize=10240
log4cpp.appender.A7.maxBackupIndex=1
log4cpp.appender.A7.layout=PatternLayout
log4cpp.appender.A7.layout.ConversionPattern=The message %m at time %d{%H:%M}%n is going to be very loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongggeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeer%n
log4cpp.appender.A8=RollingFileAppender
log4cpp.appender.A8.fileName=A4.log
log4cpp.appender.A8.maxFileSize=10240
log4cpp.appender.A8.maxBackupIndex=1
log4cpp.appender.A8.layout=PatternLayout
log4cpp.appender.A8.layout.ConversionPattern=The message %m at time %d{%H:%M}%n is going to be very loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongggeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeer%n
编译程序:
注意:需将/usr/local/lib添加至LD_LIBRARY_PATH环境变量
g++ testPropertyConfig.cpp -I../src -I../include -o testPropertyConfig -llog4cpp -lpthread -lrdkafka++
运行:
./testPropertyConfig
ok!需要查看结果可使用kafka的工具查看。