Log4cpp输出日志到Kafka

最近的项目需要做一个生物识别的分布式比对服务,在机器集群上如何做日志汇总和分析?ELK很不错,但是公司原来都是使用Log4cpp,能不能在改动最小的情况下,将日志输出到ELK?当然没有问题,Logstash有可以从kafka输入,只需要实现日志输出到kafka,剩下的ELK配置好就ok!但是Log4cpp并没有提供kafka输出,自定义一个即可!



1. 下载Log4cpp

2. 编码

只需两步:

  1. 添加KafkaAppender类,继承LayoutAppender
  2. 修改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. 编译

  1. 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
  )

  1. 生成config.h文件
./configure
  1. 编译
make
  1. 安装
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的工具查看。

全部代码:https://github.com/BuildSoftwareBetter/Log4cpp-kafka

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值