You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by mm...@apache.org on 2018/05/22 18:59:42 UTC

[incubator-pulsar] branch master updated: Added option for custom loggers in C++ and C APIs (#1827)

This is an automated email from the ASF dual-hosted git repository.

mmerli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pulsar.git


The following commit(s) were added to refs/heads/master by this push:
     new 26b181e  Added option for custom loggers in C++ and C APIs (#1827)
26b181e is described below

commit 26b181e4fcfb9da0827db5f78d29e15c026d20ab
Author: Matteo Merli <mm...@apache.org>
AuthorDate: Tue May 22 11:59:39 2018 -0700

    Added option for custom loggers in C++ and C APIs (#1827)
---
 pulsar-client-cpp/CMakeLists.txt                   |  38 +++++---
 .../include/pulsar/ClientConfiguration.h           |  13 +++
 .../{tests/main.cc => include/pulsar/Logger.h}     |  41 +++++++--
 .../include/pulsar/c/client_configuration.h        |  17 +---
 pulsar-client-cpp/lib/ClientConfiguration.cc       |   7 ++
 pulsar-client-cpp/lib/ClientConfigurationImpl.h    |   2 +
 pulsar-client-cpp/lib/ClientImpl.cc                |  21 ++++-
 .../{tests/main.cc => lib/Log4CxxLogger.h}         |  31 +++++--
 pulsar-client-cpp/lib/Log4cxxLogger.cc             |  96 ++++++++++++++++++++
 pulsar-client-cpp/lib/LogUtils.cc                  |  55 ++++++-----
 pulsar-client-cpp/lib/LogUtils.h                   | 101 ++++++++++-----------
 pulsar-client-cpp/lib/MessageBuilder.cc            |   2 +-
 pulsar-client-cpp/lib/MessageCrypto.cc             |  14 +--
 pulsar-client-cpp/lib/SimpleLoggerImpl.cc          |  90 ++++++++++++++++++
 .../{tests/main.cc => lib/SimpleLoggerImpl.h}      |  21 +++--
 pulsar-client-cpp/lib/c/c_ClientConfiguration.cc   |  30 ++++--
 pulsar-client-cpp/perf/CMakeLists.txt              |   4 +-
 pulsar-client-cpp/perf/PerfConsumer.cc             |   2 -
 pulsar-client-cpp/perf/PerfProducer.cc             |  10 +-
 pulsar-client-cpp/tests/main.cc                    |   1 -
 20 files changed, 445 insertions(+), 151 deletions(-)

diff --git a/pulsar-client-cpp/CMakeLists.txt b/pulsar-client-cpp/CMakeLists.txt
index e5fd716..38a4774 100644
--- a/pulsar-client-cpp/CMakeLists.txt
+++ b/pulsar-client-cpp/CMakeLists.txt
@@ -27,6 +27,9 @@ MESSAGE(STATUS "BUILD_TESTS:  " ${BUILD_TESTS})
 option(LINK_STATIC "Link against static libraries" OFF)
 MESSAGE(STATUS "LINK_STATIC:  " ${LINK_STATIC})
 
+option(USE_LOG4CXX "Build with Log4cxx support" OFF)
+MESSAGE(STATUS "USE_LOG4CXX:  " ${USE_LOG4CXX})
+
 IF (CMAKE_BUILD_TYPE STREQUAL "")
     set(CMAKE_BUILD_TYPE RelWithDebInfo)
 ENDIF ()
@@ -65,17 +68,20 @@ if (LINK_STATIC)
     find_library(PROTOBUF_LIBRARIES NAMES libprotobuf.a)
     find_library(CURL_LIBRARY_PATH NAMES libcurl.a curl)
     find_library(LIB_JSON NAMES libjsoncpp.a libjsoncpp_static.a)
-    find_library(LOG4CXX_LIBRARY_PATH NAMES liblog4cxx.a)
-
-    # Libraries needed by log4cxx to link statically with
-    find_library(APR_LIBRARY_PATH NAMES libapr-1.a PATHS /usr/lib /usr/local/apr/lib /usr/local/opt/apr/libexec/lib/)
-    find_library(APR_UTIL_LIBRARY_PATH NAMES libaprutil-1.a PATHS /usr/lib /usr/local/apr/lib /usr/local/opt/apr-util/libexec/lib/)
-    find_library(EXPAT_LIBRARY_PATH NAMES libexpat.a expat)
-    if (APPLE)
-        find_library(ICONV_LIBRARY_PATH NAMES libiconv.a iconv)
-    else ()
-        set(ICONV_LIBRARY_PATH )
-    endif ()
+
+    if (USE_LOG4CXX)
+        find_library(LOG4CXX_LIBRARY_PATH NAMES liblog4cxx.a)
+
+        # Libraries needed by log4cxx to link statically with
+        find_library(APR_LIBRARY_PATH NAMES libapr-1.a PATHS /usr/lib /usr/local/apr/lib /usr/local/opt/apr/libexec/lib/)
+        find_library(APR_UTIL_LIBRARY_PATH NAMES libaprutil-1.a PATHS /usr/lib /usr/local/apr/lib /usr/local/opt/apr-util/libexec/lib/)
+        find_library(EXPAT_LIBRARY_PATH NAMES libexpat.a expat)
+        if (APPLE)
+            find_library(ICONV_LIBRARY_PATH NAMES libiconv.a iconv)
+        else ()
+            set(ICONV_LIBRARY_PATH )
+        endif (APPLE)
+    endif (USE_LOG4CXX)
 else()
     # Link to shared libraries
     find_package(ZLIB REQUIRED)
@@ -90,7 +96,9 @@ else()
     find_library(LIB_JSON jsoncpp)
     find_library(LOG4CXX_LIBRARY_PATH log4cxx)
     find_library(CURL_LIBRARY_PATH curl)
-    find_path(LOG4CXX_INCLUDE_PATH log4cxx/logger.h)
+    if (USE_LOG4CXX)
+        find_path(LOG4CXX_INCLUDE_PATH log4cxx/logger.h)
+    endif (USE_LOG4CXX)
 endif (LINK_STATIC)
 
 
@@ -140,7 +148,11 @@ if (BUILD_TESTS)
 endif ()
 
 find_path(JSON_INCLUDE_PATH jsoncpp)
-find_path(LOG4CXX_INCLUDE_PATH log4cxx/logger.h)
+
+if (USE_LOG4CXX)
+    set(CMAKE_CXX_FLAGS " -DUSE_LOG4CXX ${CMAKE_CXX_FLAGS}")
+    find_path(LOG4CXX_INCLUDE_PATH log4cxx/logger.h)
+endif (USE_LOG4CXX)
 
 if (NOT LIB_JSON)
     find_library(LIB_JSON json_cpp)
diff --git a/pulsar-client-cpp/include/pulsar/ClientConfiguration.h b/pulsar-client-cpp/include/pulsar/ClientConfiguration.h
index 137d528..eedf716 100644
--- a/pulsar-client-cpp/include/pulsar/ClientConfiguration.h
+++ b/pulsar-client-cpp/include/pulsar/ClientConfiguration.h
@@ -20,6 +20,8 @@
 #define PULSAR_CLIENTCONFIGURATION_H_
 
 #include <pulsar/Authentication.h>
+#include <pulsar/Logger.h>
+
 #pragma GCC visibility push(default)
 namespace pulsar {
 class PulsarWrapper;
@@ -105,6 +107,7 @@ class ClientConfiguration {
      * Initialize the log configuration
      *
      * @param logConfFilePath  path of the configuration file
+     * @deprecated
      */
     ClientConfiguration& setLogConfFilePath(const std::string& logConfFilePath);
 
@@ -113,6 +116,16 @@ class ClientConfiguration {
      */
     const std::string& getLogConfFilePath() const;
 
+    /**
+     * Configure a custom logger backend to route of Pulsar client library
+     * to a different logger implementation.
+     *
+     * By default, log messages are printed on standard output.
+     */
+    ClientConfiguration& setLogger(LoggerFactoryPtr loggerFactory);
+
+    LoggerFactoryPtr getLogger() const;
+
     ClientConfiguration& setUseTls(bool useTls);
     bool isUseTls() const;
 
diff --git a/pulsar-client-cpp/tests/main.cc b/pulsar-client-cpp/include/pulsar/Logger.h
similarity index 57%
copy from pulsar-client-cpp/tests/main.cc
copy to pulsar-client-cpp/include/pulsar/Logger.h
index a600af3..e319a04 100644
--- a/pulsar-client-cpp/tests/main.cc
+++ b/pulsar-client-cpp/include/pulsar/Logger.h
@@ -16,11 +16,36 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-#include <LogUtils.h>
-#include <gmock/gmock.h>
-
-int main(int argc, char **argv) {
-    LogUtils::init("log4cxx.conf");
-    ::testing::InitGoogleMock(&argc, argv);
-    return RUN_ALL_TESTS();
-}
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#pragma GCC visibility push(default)
+
+namespace pulsar {
+
+class Logger {
+   public:
+    enum Level
+    {
+        DEBUG = 0,
+        INFO = 1,
+        WARN = 2,
+        ERROR = 3
+    };
+
+    virtual bool isEnabled(Level level) = 0;
+
+    virtual void log(Level level, int line, const std::string& message) = 0;
+};
+
+class LoggerFactory {
+   public:
+    virtual ~LoggerFactory() {}
+
+    virtual Logger* getLogger(const std::string& fileName) = 0;
+};
+
+typedef boost::shared_ptr<LoggerFactory> LoggerFactoryPtr;
+}  // namespace pulsar
+#pragma GCC visibility pop
diff --git a/pulsar-client-cpp/include/pulsar/c/client_configuration.h b/pulsar-client-cpp/include/pulsar/c/client_configuration.h
index 7725e7c..b04c21d 100644
--- a/pulsar-client-cpp/include/pulsar/c/client_configuration.h
+++ b/pulsar-client-cpp/include/pulsar/c/client_configuration.h
@@ -25,6 +25,10 @@ extern "C" {
 
 #pragma GCC visibility push(default)
 
+typedef enum { pulsar_DEBUG = 0, pulsar_INFO = 1, pulsar_WARN = 2, pulsar_ERROR = 3 } pulsar_logger_level_t;
+
+typedef void (*pulsar_logger)(pulsar_logger_level_t level, const char *file, int line, const char *message);
+
 typedef struct _pulsar_client_configuration pulsar_client_configuration_t;
 typedef struct _pulsar_authentication pulsar_authentication_t;
 
@@ -101,18 +105,7 @@ void pulsar_client_configuration_set_concurrent_lookup_request(pulsar_client_con
  */
 int pulsar_client_configuration_get_concurrent_lookup_request(pulsar_client_configuration_t *conf);
 
-/**
- * Initialize the log configuration
- *
- * @param logConfFilePath  path of the configuration file
- */
-void pulsar_client_configuration_set_log_conf_file_path(pulsar_client_configuration_t *conf,
-                                                        const char *logConfFilePath);
-
-/**
- * Get the path of log configuration file (log4cpp)
- */
-const char *pulsar_client_configuration_get_log_conf_file_path(pulsar_client_configuration_t *conf);
+void pulsar_client_configuration_logger(pulsar_client_configuration_t *conf, pulsar_logger logger);
 
 void pulsar_client_configuration_set_use_tls(pulsar_client_configuration_t *conf, int useTls);
 
diff --git a/pulsar-client-cpp/lib/ClientConfiguration.cc b/pulsar-client-cpp/lib/ClientConfiguration.cc
index 6f011e3..246c7af 100644
--- a/pulsar-client-cpp/lib/ClientConfiguration.cc
+++ b/pulsar-client-cpp/lib/ClientConfiguration.cc
@@ -96,6 +96,13 @@ ClientConfiguration& ClientConfiguration::setLogConfFilePath(const std::string&
 
 const std::string& ClientConfiguration::getLogConfFilePath() const { return impl_->logConfFilePath; }
 
+ClientConfiguration& ClientConfiguration::setLogger(LoggerFactoryPtr loggerFactory) {
+    impl_->loggerFactory = loggerFactory;
+    return *this;
+}
+
+LoggerFactoryPtr ClientConfiguration::getLogger() const { return impl_->loggerFactory; }
+
 ClientConfiguration& ClientConfiguration::setStatsIntervalInSeconds(
     const unsigned int& statsIntervalInSeconds) {
     impl_->statsIntervalInSeconds = statsIntervalInSeconds;
diff --git a/pulsar-client-cpp/lib/ClientConfigurationImpl.h b/pulsar-client-cpp/lib/ClientConfigurationImpl.h
index 4ea77cf..b5da790 100644
--- a/pulsar-client-cpp/lib/ClientConfigurationImpl.h
+++ b/pulsar-client-cpp/lib/ClientConfigurationImpl.h
@@ -34,6 +34,8 @@ struct ClientConfigurationImpl {
     std::string tlsTrustCertsFilePath;
     bool tlsAllowInsecureConnection;
     unsigned int statsIntervalInSeconds;
+    LoggerFactoryPtr loggerFactory;
+
     ClientConfigurationImpl()
         : authenticationPtr(AuthFactory::Disabled()),
           ioThreads(1),
diff --git a/pulsar-client-cpp/lib/ClientImpl.cc b/pulsar-client-cpp/lib/ClientImpl.cc
index 1390336..80f947b 100644
--- a/pulsar-client-cpp/lib/ClientImpl.cc
+++ b/pulsar-client-cpp/lib/ClientImpl.cc
@@ -24,6 +24,8 @@
 #include "ReaderImpl.h"
 #include "PartitionedProducerImpl.h"
 #include "PartitionedConsumerImpl.h"
+#include "SimpleLoggerImpl.h"
+#include "Log4CxxLogger.h"
 #include <boost/bind.hpp>
 #include <boost/algorithm/string/predicate.hpp>
 #include <sstream>
@@ -73,7 +75,24 @@ ClientImpl::ClientImpl(const std::string& serviceUrl, const ClientConfiguration&
       producerIdGenerator_(0),
       consumerIdGenerator_(0),
       requestIdGenerator_(0) {
-    LogUtils::init(clientConfiguration.getLogConfFilePath());
+    if (clientConfiguration.getLogger()) {
+        // A logger factory was explicitely configured. Let's just use that
+        LogUtils::setLoggerFactory(clientConfiguration.getLogger());
+    } else {
+#ifdef USE_LOG4CXX
+        if (!clientConfiguration.getLogConfFilePath().empty()) {
+            // A log4cxx log file was passed through deprecated parameter. Use that to configure Log4CXX
+            LogUtils::setLoggerFactory(Log4CxxLogger::create(clientConfiguration.getLogConfFilePath()));
+        } else {
+            // Use default simple console logger
+            LogUtils::setLoggerFactory(SimpleLoggerFactory::create());
+        }
+#else
+        // Use default simple console logger
+        LogUtils::setLoggerFactory(SimpleLoggerFactory::create());
+#endif
+    }
+
     if (serviceUrl_.compare(0, 4, "http") == 0) {
         LOG_DEBUG("Using HTTP Lookup");
         lookupServicePtr_ =
diff --git a/pulsar-client-cpp/tests/main.cc b/pulsar-client-cpp/lib/Log4CxxLogger.h
similarity index 66%
copy from pulsar-client-cpp/tests/main.cc
copy to pulsar-client-cpp/lib/Log4CxxLogger.h
index a600af3..184c336 100644
--- a/pulsar-client-cpp/tests/main.cc
+++ b/pulsar-client-cpp/lib/Log4CxxLogger.h
@@ -16,11 +16,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-#include <LogUtils.h>
-#include <gmock/gmock.h>
-
-int main(int argc, char **argv) {
-    LogUtils::init("log4cxx.conf");
-    ::testing::InitGoogleMock(&argc, argv);
-    return RUN_ALL_TESTS();
-}
+
+#pragma once
+
+#include <pulsar/Logger.h>
+
+#ifdef USE_LOG4CXX
+
+#pragma GCC visibility push(default)
+
+namespace pulsar {
+
+class Log4CxxLoggerFactory : public LoggerFactory {
+   public:
+    static LoggerFactoryPtr create();
+    static LoggerFactoryPtr create(const std::string& log4cxxConfFile);
+
+    Logger* getLogger(const std::string& fileName);
+};
+}  // namespace pulsar
+
+#pragma GCC visibility pop
+
+#endif
diff --git a/pulsar-client-cpp/lib/Log4cxxLogger.cc b/pulsar-client-cpp/lib/Log4cxxLogger.cc
new file mode 100644
index 0000000..99cc614
--- /dev/null
+++ b/pulsar-client-cpp/lib/Log4cxxLogger.cc
@@ -0,0 +1,96 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 "Log4CxxLogger.h"
+#include <iostream>
+
+#ifdef USE_LOG4CXX
+
+#include <log4cxx/logger.h>
+#include <log4cxx/logmanager.h>
+#include <log4cxx/consoleappender.h>
+#include <log4cxx/propertyconfigurator.h>
+#include <log4cxx/patternlayout.h>
+
+using namespace log4cxx;
+
+namespace pulsar {
+
+class Log4CxxLogger : public Logger {
+    std::string _fileName;
+    LoggerPtr _logger;
+
+   public:
+    Log4CxxLogger(const std::string &fileName)
+        : _fileName(fileName), _logger(log4cxx::Logger::getLogger(LOG_CATEGORY_NAME + fileName)) {}
+
+    bool isEnabled(Level level) { return _logger->isEnabledFor(getLevel(level)); }
+
+    void log(Level level, int line, const std::string &message) {
+        spi::LocationInfo location(_fileName.c_str(), "", line);
+        _logger->forcedLogLS(getLevel(level), message, location);
+    }
+
+   private:
+    static log4cxx::LevelPtr getLevel(Level level) {
+        switch (level) {
+            case DEBUG:
+                return log4cxx::Level::getDebug();
+            case INFO:
+                return log4cxx::Level::getInfo();
+            case WARN:
+                return log4cxx::Level::getWarn();
+            case ERROR:
+                return log4cxx::Level::getError();
+        }
+    }
+};
+
+LoggerFactoryPtr Log4CxxLoggerFactory::create() {
+    if (!LogManager::getLoggerRepository()->isConfigured()) {
+        LogManager::getLoggerRepository()->setConfigured(true);
+        LoggerPtr root = log4cxx::Logger::getRootLogger();
+        static const LogString TTCC_CONVERSION_PATTERN(LOG4CXX_STR("%d{HH:mm:ss.SSS} [%t] %-5p %l - %m%n"));
+        LayoutPtr layout(new PatternLayout(TTCC_CONVERSION_PATTERN));
+        AppenderPtr appender(new ConsoleAppender(layout));
+        root->setLevel(log4cxx::Level::getInfo());
+        root->addAppender(appender);
+    }
+
+    return LoggerFactoryPtr(new Log4CxxLoggerFactory());
+}
+
+LoggerFactoryPtr Log4CxxLoggerFactory::create(const std::string &log4cxxConfFile) {
+    try {
+        log4cxx::PropertyConfigurator::configure(log4cxxConfFile);
+    } catch (const std::exception &e) {
+        std::cerr << "exception caught while configuring log4cpp via '" << log4cxxConfFile
+                  << "': " << e.what() << std::endl;
+    } catch (...) {
+        std::cerr << "unknown exception while configuring log4cpp via '" << log4cxxConfFile << "'."
+                  << std::endl;
+    }
+
+    return LoggerFactoryPtr(new Log4CxxLoggerFactory());
+}
+
+Logger *Log4CxxLoggerFactory::getLogger(const std::string &fileName) { return new Log4CxxLogger(fileName); }
+}  // namespace pulsar
+
+#endif  // USE_LOG4CXX
diff --git a/pulsar-client-cpp/lib/LogUtils.cc b/pulsar-client-cpp/lib/LogUtils.cc
index 70507d9..2192327 100644
--- a/pulsar-client-cpp/lib/LogUtils.cc
+++ b/pulsar-client-cpp/lib/LogUtils.cc
@@ -18,35 +18,34 @@
  */
 #include "LogUtils.h"
 
-#include <log4cxx/logger.h>
-#include <log4cxx/logmanager.h>
-#include <log4cxx/consoleappender.h>
-#include <log4cxx/propertyconfigurator.h>
-#include <log4cxx/patternlayout.h>
 #include <iostream>
 
-using namespace log4cxx;
-
-void LogUtils::init(const std::string& logfilePath) {
-    try {
-        if (logfilePath.empty()) {
-            if (!LogManager::getLoggerRepository()->isConfigured()) {
-                LogManager::getLoggerRepository()->setConfigured(true);
-                LoggerPtr root = Logger::getRootLogger();
-                static const LogString TTCC_CONVERSION_PATTERN(
-                    LOG4CXX_STR("%d{HH:mm:ss.SSS} [%t] %-5p %l - %m%n"));
-                LayoutPtr layout(new PatternLayout(TTCC_CONVERSION_PATTERN));
-                AppenderPtr appender(new ConsoleAppender(layout));
-                root->setLevel(log4cxx::Level::getInfo());
-                root->addAppender(appender);
-            }
-        } else {
-            log4cxx::PropertyConfigurator::configure(logfilePath);
-        }
-    } catch (const std::exception& e) {
-        std::cerr << "exception caught while configuring log4cpp via '" << logfilePath << "': " << e.what()
-                  << std::endl;
-    } catch (...) {
-        std::cerr << "unknown exception while configuring log4cpp via '" << logfilePath << "'." << std::endl;
+#include "SimpleLoggerImpl.h"
+#include "Log4CxxLogger.h"
+
+namespace pulsar {
+
+void LogUtils::init(const std::string &logfilePath) {
+// If this is called explicitely, we fallback to Log4cxx config, if enabled
+
+#ifdef USE_LOG4CXX
+    if (!logfilePath.empty()) {
+        setLoggerFactory(Log4CxxLoggerFactory::create(logfilePath));
+    } else {
+        setLoggerFactory(Log4CxxLoggerFactory::create());
     }
+#endif  // USE_LOG4CXX
 }
+
+static LoggerFactoryPtr s_loggerFactory;
+
+void LogUtils::setLoggerFactory(LoggerFactoryPtr loggerFactory) { s_loggerFactory = loggerFactory; }
+
+LoggerFactoryPtr LogUtils::getLoggerFactory() {
+    if (!s_loggerFactory) {
+        s_loggerFactory.reset(new SimpleLoggerFactory());
+    }
+    return s_loggerFactory;
+}
+
+}  // namespace pulsar
\ No newline at end of file
diff --git a/pulsar-client-cpp/lib/LogUtils.h b/pulsar-client-cpp/lib/LogUtils.h
index 6371efe..12a5b06 100644
--- a/pulsar-client-cpp/lib/LogUtils.h
+++ b/pulsar-client-cpp/lib/LogUtils.h
@@ -16,68 +16,64 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-#ifndef LOG_UTIL_H
-#define LOG_UTIL_H
+
+#pragma once
 
 #include <boost/thread/tss.hpp>
-#include <log4cxx/logger.h>
 #include <string>
+#include <sstream>
 
-#define DECLARE_LOG_OBJECT()                                                                     \
-    static log4cxx::LoggerPtr& logger() {                                                        \
-        static boost::thread_specific_ptr<log4cxx::LoggerPtr> threadSpecificLogPtr;              \
-        log4cxx::LoggerPtr* ptr = threadSpecificLogPtr.get();                                    \
-        if (!ptr) {                                                                              \
-            threadSpecificLogPtr.reset(                                                          \
-                new log4cxx::LoggerPtr(log4cxx::Logger::getLogger(LOG_CATEGORY_NAME __FILE__))); \
-            ptr = threadSpecificLogPtr.get();                                                    \
-        }                                                                                        \
-        return *ptr;                                                                             \
-    }
+#include <pulsar/Logger.h>
+
+namespace pulsar {
+
+#define PULSAR_UNLIKELY(expr) __builtin_expect(expr, 0)
 
-#define LOG_DEBUG(message)                                                                                \
-    {                                                                                                     \
-        if (LOG4CXX_UNLIKELY(logger()->isDebugEnabled())) {                                               \
-            ::log4cxx::helpers::MessageBuffer oss_;                                                       \
-            logger()->forcedLog(::log4cxx::Level::getDebug(), oss_.str(((std::ostream&)oss_) << message), \
-                                LOG4CXX_LOCATION);                                                        \
-        }                                                                                                 \
+#define DECLARE_LOG_OBJECT()                                                                       \
+    static pulsar::Logger* logger() {                                                              \
+        static boost::thread_specific_ptr<pulsar::Logger> threadSpecificLogPtr;                    \
+        pulsar::Logger* ptr = threadSpecificLogPtr.get();                                          \
+        if (PULSAR_UNLIKELY(!ptr)) {                                                               \
+            threadSpecificLogPtr.reset(pulsar::LogUtils::getLoggerFactory()->getLogger(__FILE__)); \
+            ptr = threadSpecificLogPtr.get();                                                      \
+        }                                                                                          \
+        return ptr;                                                                                \
     }
 
-#define LOG_INFO(message)                                                                                \
-    {                                                                                                    \
-        if (logger()->isInfoEnabled()) {                                                                 \
-            ::log4cxx::helpers::MessageBuffer oss_;                                                      \
-            logger()->forcedLog(::log4cxx::Level::getInfo(), oss_.str(((std::ostream&)oss_) << message), \
-                                LOG4CXX_LOCATION);                                                       \
-        }                                                                                                \
+#define LOG_DEBUG(message)                                                 \
+    {                                                                      \
+        if (PULSAR_UNLIKELY(logger()->isEnabled(pulsar::Logger::DEBUG))) { \
+            std::stringstream ss;                                          \
+            ss << message;                                                 \
+            logger()->log(pulsar::Logger::DEBUG, __LINE__, ss.str());      \
+        }                                                                  \
     }
 
-#define LOG_WARN(message)                                                                                \
-    {                                                                                                    \
-        if (logger()->isWarnEnabled()) {                                                                 \
-            ::log4cxx::helpers::MessageBuffer oss_;                                                      \
-            logger()->forcedLog(::log4cxx::Level::getWarn(), oss_.str(((std::ostream&)oss_) << message), \
-                                LOG4CXX_LOCATION);                                                       \
-        }                                                                                                \
+#define LOG_INFO(message)                                            \
+    {                                                                \
+        if (logger()->isEnabled(pulsar::Logger::INFO)) {             \
+            std::stringstream ss;                                    \
+            ss << message;                                           \
+            logger()->log(pulsar::Logger::INFO, __LINE__, ss.str()); \
+        }                                                            \
     }
 
-#define LOG_ERROR(message)                                                                                \
-    {                                                                                                     \
-        if (logger()->isErrorEnabled()) {                                                                 \
-            ::log4cxx::helpers::MessageBuffer oss_;                                                       \
-            logger()->forcedLog(::log4cxx::Level::getError(), oss_.str(((std::ostream&)oss_) << message), \
-                                LOG4CXX_LOCATION);                                                        \
-        }                                                                                                 \
+#define LOG_WARN(message)                                            \
+    {                                                                \
+        if (logger()->isEnabled(pulsar::Logger::WARN)) {             \
+            std::stringstream ss;                                    \
+            ss << message;                                           \
+            logger()->log(pulsar::Logger::WARN, __LINE__, ss.str()); \
+        }                                                            \
     }
 
-#define LOG_FATAL(message)                                                                                \
-    {                                                                                                     \
-        if (logger()->isFatalEnabled()) {                                                                 \
-            ::log4cxx::helpers::MessageBuffer oss_;                                                       \
-            logger()->forcedLog(::log4cxx::Level::getFatal(), oss_.str(((std::ostream&)oss_) << message), \
-                                LOG4CXX_LOCATION);                                                        \
-        }                                                                                                 \
+#define LOG_ERROR(message)                                            \
+    {                                                                 \
+        if (logger()->isEnabled(pulsar::Logger::ERROR)) {             \
+            std::stringstream ss;                                     \
+            ss << message;                                            \
+            logger()->log(pulsar::Logger::ERROR, __LINE__, ss.str()); \
+        }                                                             \
     }
 
 #pragma GCC visibility push(default)
@@ -85,8 +81,11 @@
 class LogUtils {
    public:
     static void init(const std::string& logConfFilePath);
+
+    static void setLoggerFactory(LoggerFactoryPtr loggerFactory);
+
+    static LoggerFactoryPtr getLoggerFactory();
 };
 
 #pragma GCC visibility pop
-
-#endif
+}
diff --git a/pulsar-client-cpp/lib/MessageBuilder.cc b/pulsar-client-cpp/lib/MessageBuilder.cc
index 4ea21b2..5dd9df1 100644
--- a/pulsar-client-cpp/lib/MessageBuilder.cc
+++ b/pulsar-client-cpp/lib/MessageBuilder.cc
@@ -48,7 +48,7 @@ Message MessageBuilder::build() { return Message(impl_); }
 
 void MessageBuilder::checkMetadata() {
     if (!impl_.get()) {
-        LOG_FATAL("Cannot reuse the same message builder to build a message");
+        LOG_ERROR("Cannot reuse the same message builder to build a message");
         abort();
     }
 }
diff --git a/pulsar-client-cpp/lib/MessageCrypto.cc b/pulsar-client-cpp/lib/MessageCrypto.cc
index c5f41d8..0203660 100644
--- a/pulsar-client-cpp/lib/MessageCrypto.cc
+++ b/pulsar-client-cpp/lib/MessageCrypto.cc
@@ -147,7 +147,7 @@ Result MessageCrypto::addPublicKeyCipher(std::set<std::string>& keyNames,
 
     // Generate data key
     RAND_bytes(dataKey_.get(), dataKeyLen_);
-    if (LOG4CXX_UNLIKELY(logger()->isDebugEnabled())) {
+    if (PULSAR_UNLIKELY(logger()->isEnabled(Logger::DEBUG))) {
         std::string dataKeyStr(reinterpret_cast<char*>(dataKey_.get()), dataKeyLen_);
         std::string strHex = stringToHex(dataKeyStr, dataKeyStr.size());
         LOG_DEBUG(logCtx_ << "Generated Data key " << strHex);
@@ -203,7 +203,7 @@ Result MessageCrypto::addPublicKeyCipher(const std::string& keyName, const Crypt
     // Add a new entry or replace existing entry, if one is present.
     encryptedDataKeyMap_[keyName] = eki;
 
-    if (LOG4CXX_UNLIKELY(logger()->isDebugEnabled())) {
+    if (PULSAR_UNLIKELY(logger()->isEnabled(Logger::DEBUG))) {
         std::string strHex = stringToHex(encryptedKeyStr, encryptedKeyStr.size());
         LOG_DEBUG(logCtx_ << " Data key encrypted for key " << keyName
                           << ". Encrypted key size = " << encryptedKeyStr.size() << ", value = " << strHex);
@@ -253,7 +253,7 @@ bool MessageCrypto::encrypt(std::set<std::string>& encKeys, const CryptoKeyReade
         proto::EncryptionKeys* encKeys = proto::EncryptionKeys().New();
         encKeys->set_key(keyName);
         encKeys->set_value(keyInfo->getKey());
-        if (LOG4CXX_UNLIKELY(logger()->isDebugEnabled())) {
+        if (PULSAR_UNLIKELY(logger()->isEnabled(Logger::DEBUG))) {
             std::string strHex = stringToHex(keyInfo->getKey(), keyInfo->getKey().size());
             LOG_DEBUG(logCtx_ << " Encrypted data key added for key " << keyName << ". Encrypted key size = "
                               << keyInfo->getKey().size() << ", value = " << strHex);
@@ -323,7 +323,7 @@ bool MessageCrypto::encrypt(std::set<std::string>& encKeys, const CryptoKeyReade
         return false;
     }
     encryptedPayload.bytesWritten(tagLen_);
-    if (LOG4CXX_UNLIKELY(logger()->isDebugEnabled())) {
+    if (PULSAR_UNLIKELY(logger()->isEnabled(Logger::DEBUG))) {
         std::string strPayloadHex = stringToHex(payload.data(), payload.readableBytes());
         std::string strHex = stringToHex(encryptedPayload.data(), encryptedPayload.readableBytes());
         LOG_DEBUG(logCtx_ << " Original size = " << payload.readableBytes() << ", value = " << strPayloadHex
@@ -376,7 +376,7 @@ bool MessageCrypto::decryptDataKey(const std::string& keyName, const std::string
     std::string keyDigestStr(reinterpret_cast<char*>(keyDigest), digestLen);
     std::string dataKeyStr(reinterpret_cast<char*>(dataKey_.get()), dataKeyLen_);
     dataKeyCache_[keyDigestStr] = make_pair(dataKeyStr, boost::posix_time::second_clock::universal_time());
-    if (LOG4CXX_UNLIKELY(logger()->isDebugEnabled())) {
+    if (PULSAR_UNLIKELY(logger()->isEnabled(Logger::DEBUG))) {
         std::string strHex = stringToHex(dataKeyStr, dataKeyStr.size());
         LOG_DEBUG(logCtx_ << "Data key for key " << keyName << " decrypted. Decrypted data key is "
                           << strHex);
@@ -395,7 +395,7 @@ bool MessageCrypto::decryptData(const std::string& dataKeySecret, const proto::M
 
     EVP_CIPHER_CTX* cipherCtx = NULL;
     decryptedPayload = SharedBuffer::allocate(payload.readableBytes() + EVP_MAX_BLOCK_LENGTH + tagLen_);
-    if (LOG4CXX_UNLIKELY(logger()->isDebugEnabled())) {
+    if (PULSAR_UNLIKELY(logger()->isEnabled(Logger::DEBUG))) {
         std::string strHex = stringToHex(payload.data(), payload.readableBytes());
         LOG_DEBUG(logCtx_ << "Attempting to decrypt data with encrypted size " << payload.readableBytes()
                           << ", data = " << strHex);
@@ -443,7 +443,7 @@ bool MessageCrypto::decryptData(const std::string& dataKeySecret, const proto::M
         return false;
     }
     decryptedPayload.bytesWritten(decLen);
-    if (LOG4CXX_UNLIKELY(logger()->isDebugEnabled())) {
+    if (PULSAR_UNLIKELY(logger()->isEnabled(Logger::DEBUG))) {
         std::string strHex = stringToHex(decryptedPayload.data(), decryptedPayload.readableBytes());
         LOG_DEBUG(logCtx_ << "Data decrypted. Decrypted size = " << decryptedPayload.readableBytes()
                           << ", data = " << strHex);
diff --git a/pulsar-client-cpp/lib/SimpleLoggerImpl.cc b/pulsar-client-cpp/lib/SimpleLoggerImpl.cc
new file mode 100644
index 0000000..309eb33
--- /dev/null
+++ b/pulsar-client-cpp/lib/SimpleLoggerImpl.cc
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 "SimpleLoggerImpl.h"
+
+#include <iostream>
+#include <sstream>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/format.hpp>
+
+namespace pulsar {
+
+inline std::ostream &operator<<(std::ostream &s, Logger::Level level) {
+    switch (level) {
+        case Logger::DEBUG:
+            s << "DEBUG";
+            break;
+        case Logger::INFO:
+            s << "INFO ";
+            break;
+        case Logger::WARN:
+            s << "WARN ";
+            break;
+        case Logger::ERROR:
+            s << "ERROR";
+            break;
+    }
+
+    return s;
+}
+
+class SimpleLogger : public Logger {
+    std::string _logger;
+
+   public:
+    SimpleLogger(const std::string &logger) : _logger(logger) {}
+
+    bool isEnabled(Level level) { return level >= Logger::INFO; }
+
+    void log(Level level, int line, const std::string &message) {
+        std::stringstream ss;
+
+        printTimestamp(ss);
+        ss << " " << level << " " << _logger << ":" << line << " | " << message << "\n";
+
+        std::cout << ss.str();
+        std::cout.flush();
+    }
+
+   private:
+    static std::ostream &printTimestamp(std::ostream &s) {
+        boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time();
+
+        const boost::format f =
+            boost::format("%04d-%02d-%02d %02d:%02d:%02d.%03d") % now.date().year_month_day().year %
+            now.date().year_month_day().month.as_number() % now.date().year_month_day().day.as_number() %
+            now.time_of_day().hours() % now.time_of_day().minutes() % now.time_of_day().seconds() %
+            (now.time_of_day().fractional_seconds() / 1000);
+
+        s << f.str();
+        return s;
+    }
+};
+
+Logger *SimpleLoggerFactory::getLogger(const std::string &path) {
+    // Remove all directories from filename
+    int startIdx = path.find_last_of("/");
+    int endIdx = path.find_last_of(".");
+    std::string fileName = path.substr(startIdx + 1, endIdx - startIdx - 1);
+    return new SimpleLogger(fileName);
+}
+
+LoggerFactoryPtr SimpleLoggerFactory::create() { return LoggerFactoryPtr(new SimpleLoggerFactory); }
+}  // namespace pulsar
diff --git a/pulsar-client-cpp/tests/main.cc b/pulsar-client-cpp/lib/SimpleLoggerImpl.h
similarity index 77%
copy from pulsar-client-cpp/tests/main.cc
copy to pulsar-client-cpp/lib/SimpleLoggerImpl.h
index a600af3..2b81060 100644
--- a/pulsar-client-cpp/tests/main.cc
+++ b/pulsar-client-cpp/lib/SimpleLoggerImpl.h
@@ -16,11 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-#include <LogUtils.h>
-#include <gmock/gmock.h>
 
-int main(int argc, char **argv) {
-    LogUtils::init("log4cxx.conf");
-    ::testing::InitGoogleMock(&argc, argv);
-    return RUN_ALL_TESTS();
-}
+#pragma once
+
+#include <pulsar/Logger.h>
+
+namespace pulsar {
+
+class SimpleLoggerFactory : public LoggerFactory {
+   public:
+    Logger* getLogger(const std::string& fileName);
+
+    static LoggerFactoryPtr create();
+};
+
+}  // namespace pulsar
\ No newline at end of file
diff --git a/pulsar-client-cpp/lib/c/c_ClientConfiguration.cc b/pulsar-client-cpp/lib/c/c_ClientConfiguration.cc
index 8caf8e8..6ceaf6b 100644
--- a/pulsar-client-cpp/lib/c/c_ClientConfiguration.cc
+++ b/pulsar-client-cpp/lib/c/c_ClientConfiguration.cc
@@ -69,13 +69,31 @@ int pulsar_client_configuration_get_concurrent_lookup_request(pulsar_client_conf
     return conf->conf.getConcurrentLookupRequest();
 }
 
-void pulsar_client_configuration_set_log_conf_file_path(pulsar_client_configuration_t *conf,
-                                                        const char *logConfFilePath) {
-    conf->conf.setLogConfFilePath(logConfFilePath);
-}
+class PulsarCLogger : public pulsar::Logger {
+    std::string file_;
+    pulsar_logger logger_;
+
+   public:
+    PulsarCLogger(const std::string &file, pulsar_logger logger) : file_(file), logger_(logger) {}
+
+    bool isEnabled(Level level) { return level >= pulsar::Logger::INFO; }
+
+    void log(Level level, int line, const std::string &message) {
+        logger_((pulsar_logger_level_t)level, file_.c_str(), line, message.c_str());
+    }
+};
+
+class PulsarCLoggerFactory : public pulsar::LoggerFactory {
+    pulsar_logger logger_;
+
+   public:
+    PulsarCLoggerFactory(pulsar_logger logger) : logger_(logger) {}
+
+    pulsar::Logger *getLogger(const std::string &fileName) { return new PulsarCLogger(fileName, logger_); }
+};
 
-const char *pulsar_client_configuration_get_log_conf_file_path(pulsar_client_configuration_t *conf) {
-    return conf->conf.getLogConfFilePath().c_str();
+void pulsar_client_configuration_set_logger(pulsar_client_configuration_t *conf, pulsar_logger logger) {
+    conf->conf.setLogger(pulsar::LoggerFactoryPtr(new PulsarCLoggerFactory(logger)));
 }
 
 void pulsar_client_configuration_set_use_tls(pulsar_client_configuration_t *conf, int useTls) {
diff --git a/pulsar-client-cpp/perf/CMakeLists.txt b/pulsar-client-cpp/perf/CMakeLists.txt
index b298871..b3c8dc8 100644
--- a/pulsar-client-cpp/perf/CMakeLists.txt
+++ b/pulsar-client-cpp/perf/CMakeLists.txt
@@ -31,5 +31,5 @@ set(PERF_CONSUMER_SOURCES
 add_executable(perfProducer ${PERF_PRODUCER_SOURCES})
 add_executable(perfConsumer ${PERF_CONSUMER_SOURCES})
 
-target_link_libraries(perfProducer ${CLIENT_LIBS})
-target_link_libraries(perfConsumer ${CLIENT_LIBS})
+target_link_libraries(perfProducer pulsarShared ${CLIENT_LIBS})
+target_link_libraries(perfConsumer pulsarShared ${CLIENT_LIBS})
diff --git a/pulsar-client-cpp/perf/PerfConsumer.cc b/pulsar-client-cpp/perf/PerfConsumer.cc
index 337035e..a263c57 100644
--- a/pulsar-client-cpp/perf/PerfConsumer.cc
+++ b/pulsar-client-cpp/perf/PerfConsumer.cc
@@ -237,8 +237,6 @@ void startPerfConsumer(const Arguments& args) {
 }
 
 int main(int argc, char** argv) {
-    LogUtils::init("conf/log4cxx.conf");
-
     // First try to read default values from config file if present
     const std::string confFile = "conf/client.conf";
     std::string defaultServiceUrl;
diff --git a/pulsar-client-cpp/perf/PerfProducer.cc b/pulsar-client-cpp/perf/PerfProducer.cc
index d591d34..859d4bd 100644
--- a/pulsar-client-cpp/perf/PerfProducer.cc
+++ b/pulsar-client-cpp/perf/PerfProducer.cc
@@ -30,6 +30,7 @@ DECLARE_LOG_OBJECT()
 #include <boost/accumulators/statistics/p_square_quantile.hpp>
 #include <boost/program_options/variables_map.hpp>
 #include <boost/program_options.hpp>
+#include <boost/thread.hpp>
 namespace po = boost::program_options;
 
 #include <iostream>
@@ -151,7 +152,7 @@ void runProducer(const Arguments& args, std::string topicName, int threadIndex,
             producer.sendAsync(msg, boost::bind(sendCallback, _1, _2, Clock::now()));
             boost::this_thread::interruption_point();
         }
-    } catch(const boost::thread_interrupted&) {
+    } catch(const boost::thread_interrupted& e) {
         // Thread interruption request received, break the loop
         LOG_INFO("Thread interrupted. Exiting thread.");
     }
@@ -187,8 +188,6 @@ void startPerfProducer(const Arguments& args, pulsar::ProducerConfiguration &pro
 }
 
 int main(int argc, char** argv) {
-    LogUtils::init("conf/log4cxx.conf");
-
     std::string defaultServiceUrl;
 
     // First try to read default values from config file if present
@@ -305,6 +304,8 @@ int main(int argc, char** argv) {
     for (po::variables_map::iterator it = map.begin(); it != map.end(); ++it) {
         if (it->second.value().type() == typeid(std::string)) {
             LOG_INFO(it->first << ": " << it->second.as<std::string>());
+        } else if (it->second.value().type() == typeid(bool)) {
+            LOG_INFO(it->first << ": " << it->second.as<bool>());
         } else if (it->second.value().type() == typeid(int)) {
             LOG_INFO(it->first << ": " << it->second.as<int>());
         } else if (it->second.value().type() == typeid(double)) {
@@ -355,7 +356,8 @@ int main(int argc, char** argv) {
 
     Clock::time_point oldTime = Clock::now();
     unsigned long totalMessagesProduced = 0;
-    while (args.numberOfSamples--) {
+    long messagesToSend = args.numberOfSamples;
+    while (args.numberOfSamples == 0 || --messagesToSend > 0) {
         std::this_thread::sleep_for(std::chrono::seconds(args.samplingPeriod));
 
         Clock::time_point now = Clock::now();
diff --git a/pulsar-client-cpp/tests/main.cc b/pulsar-client-cpp/tests/main.cc
index a600af3..0659925 100644
--- a/pulsar-client-cpp/tests/main.cc
+++ b/pulsar-client-cpp/tests/main.cc
@@ -20,7 +20,6 @@
 #include <gmock/gmock.h>
 
 int main(int argc, char **argv) {
-    LogUtils::init("log4cxx.conf");
     ::testing::InitGoogleMock(&argc, argv);
     return RUN_ALL_TESTS();
 }

-- 
To stop receiving notification emails like this one, please contact
mmerli@apache.org.