util: Generalize logger backend support
This commit introduces a public API change for changing log
destinations.
Refs: #3782
Change-Id: I329c60f510d9493af5959b34eea93b81e15fe698
diff --git a/ndn-cxx/util/logging.cpp b/ndn-cxx/util/logging.cpp
index 45f4ba7..ca6d584 100644
--- a/ndn-cxx/util/logging.cpp
+++ b/ndn-cxx/util/logging.cpp
@@ -90,7 +90,9 @@
Logging::Logging()
{
- this->setDestinationImpl(shared_ptr<std::ostream>(&std::clog, [] (auto) {}));
+ // cannot call the static setDestination that uses the singleton Logging object that is not yet constructed
+ auto destination = makeDefaultStreamDestination(shared_ptr<std::ostream>(&std::clog, [] (auto) {}));
+ this->setDestinationImpl(std::move(destination));
const char* environ = std::getenv("NDN_LOG");
if (environ != nullptr) {
@@ -242,38 +244,64 @@
void
Logging::setDestination(std::ostream& os)
{
- setDestination(shared_ptr<std::ostream>(&os, [] (auto) {}));
+ auto destination = makeDefaultStreamDestination(shared_ptr<std::ostream>(&os, [] (auto) {}));
+ setDestination(std::move(destination));
+}
+
+class TextOstreamBackend : public boost::log::sinks::text_ostream_backend
+{
+public:
+ TextOstreamBackend(std::shared_ptr<std::ostream> os)
+ : m_stdPtr(std::move(os))
+ {
+ auto_flush(true);
+ add_stream(boost::shared_ptr<std::ostream>(m_stdPtr.get(), [] (auto) {}));
+ }
+
+private:
+ // Quite a mess right now because Boost.Log uses boost::shared_ptr and we are using
+ // std::shared_ptr. When it is finally fixed, we can remove this mess.
+ std::shared_ptr<std::ostream> m_stdPtr;
+};
+
+boost::shared_ptr<boost::log::sinks::sink>
+Logging::makeDefaultStreamDestination(shared_ptr<std::ostream> os)
+{
+ auto backend = boost::make_shared<TextOstreamBackend>(std::move(os));
+ auto destination = boost::make_shared<boost::log::sinks::asynchronous_sink<TextOstreamBackend>>(backend);
+
+ namespace expr = boost::log::expressions;
+ destination->set_formatter(expr::stream
+ << expr::attr<std::string>(log::timestamp.get_name())
+ << " " << std::setw(5) << expr::attr<LogLevel>(log::severity.get_name()) << ": "
+ << "[" << expr::attr<std::string>(log::module.get_name()) << "] "
+ << expr::smessage);
+ return destination;
}
void
-Logging::setDestinationImpl(shared_ptr<std::ostream> os)
+Logging::setDestinationImpl(boost::shared_ptr<boost::log::sinks::sink> destination)
{
std::lock_guard<std::mutex> lock(m_mutex);
- m_destination = std::move(os);
-
- auto backend = boost::make_shared<boost::log::sinks::text_ostream_backend>();
- backend->auto_flush(true);
- backend->add_stream(boost::shared_ptr<std::ostream>(m_destination.get(), [] (auto) {}));
-
- if (m_sink != nullptr) {
- boost::log::core::get()->remove_sink(m_sink);
- m_sink->flush();
- m_sink.reset();
+ if (destination == m_destination) {
+ return;
}
- namespace expr = boost::log::expressions;
- m_sink = boost::make_shared<Sink>(backend);
- m_sink->set_formatter(expr::stream
- << expr::attr<std::string>(log::timestamp.get_name())
- << " " << std::setw(5) << expr::attr<LogLevel>(log::severity.get_name()) << ": "
- << "[" << expr::attr<std::string>(log::module.get_name()) << "] "
- << expr::smessage);
- boost::log::core::get()->add_sink(m_sink);
+ if (m_destination != nullptr) {
+ boost::log::core::get()->remove_sink(m_destination);
+ m_destination->flush();
+ }
+
+ m_destination = std::move(destination);
+
+ if (m_destination != nullptr) {
+ boost::log::core::get()->add_sink(m_destination);
+ }
}
#ifdef NDN_CXX_HAVE_TESTS
-shared_ptr<std::ostream>
+boost::shared_ptr<boost::log::sinks::sink>
Logging::getDestination() const
{
return m_destination;
@@ -298,7 +326,11 @@
void
Logging::flushImpl()
{
- m_sink->flush();
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ if (m_destination != nullptr) {
+ m_destination->flush();
+ }
}
} // namespace util
diff --git a/ndn-cxx/util/logging.hpp b/ndn-cxx/util/logging.hpp
index 532ee73..8162358 100644
--- a/ndn-cxx/util/logging.hpp
+++ b/ndn-cxx/util/logging.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -80,19 +80,26 @@
static void
setLevel(const std::string& config);
- /** \brief Set log destination.
- * \param os a stream for log output
+ /** \brief Set or replace log destination.
+ * \param destination log backend, e.g., returned by `makeDefaultStreamDestination`
*
* The initial destination is `std::clog`.
+ *
+ * Note that if \p destination is nullptr, the destination will be removed and the
+ * application is expected to add its own. If the application does not set a custom
+ * destination (using this function or directly using Boost.Log routines), the default
+ * Boost.Log destination will be used. Refer to Boost.Log documentation and source code
+ * for details.
*/
static void
- setDestination(shared_ptr<std::ostream> os);
+ setDestination(boost::shared_ptr<boost::log::sinks::sink> destination);
- /** \brief Set log destination.
+ /** \brief Helper method to set stream log destination.
* \param os a stream for log output; caller must ensure it remains valid
* until setDestination() is invoked again or program exits
+ *`
+ * This is equivalent to `setDestination(makeDefaultStreamDestination(shared_ptr<std::ostream>(&os, nullDeleter)))`.
*
- * This is equivalent to `setDestination(shared_ptr<std::ostream>(&os, nullDeleter))`.
*/
static void
setDestination(std::ostream& os);
@@ -104,6 +111,11 @@
static void
flush();
+ /** \brief Create stream log destination using default formatting
+ */
+ static boost::shared_ptr<boost::log::sinks::sink>
+ makeDefaultStreamDestination(shared_ptr<std::ostream> os);
+
private:
Logging();
@@ -138,7 +150,7 @@
setLevelImpl(const std::string& config);
void
- setDestinationImpl(shared_ptr<std::ostream> os);
+ setDestinationImpl(boost::shared_ptr<boost::log::sinks::sink> sink);
void
flushImpl();
@@ -154,7 +166,7 @@
void
resetLevels();
- shared_ptr<std::ostream>
+ boost::shared_ptr<boost::log::sinks::sink>
getDestination() const;
void
@@ -171,9 +183,7 @@
std::unordered_map<std::string, LogLevel> m_enabledLevel; ///< module prefix => minimum level
std::unordered_multimap<std::string, Logger*> m_loggers; ///< module name => logger instance
- using Sink = boost::log::sinks::asynchronous_sink<boost::log::sinks::text_ostream_backend>;
- boost::shared_ptr<Sink> m_sink;
- shared_ptr<std::ostream> m_destination;
+ boost::shared_ptr<boost::log::sinks::sink> m_destination;
};
inline std::set<std::string>
@@ -195,9 +205,9 @@
}
inline void
-Logging::setDestination(shared_ptr<std::ostream> os)
+Logging::setDestination(boost::shared_ptr<boost::log::sinks::sink> destination)
{
- get().setDestinationImpl(std::move(os));
+ get().setDestinationImpl(std::move(destination));
}
inline void
diff --git a/tests/unit/util/logging.t.cpp b/tests/unit/util/logging.t.cpp
index 5be163a..e051cce 100644
--- a/tests/unit/util/logging.t.cpp
+++ b/tests/unit/util/logging.t.cpp
@@ -156,7 +156,7 @@
private:
std::unordered_map<std::string, LogLevel> m_oldEnabledLevel;
- shared_ptr<std::ostream> m_oldDestination;
+ boost::shared_ptr<boost::log::sinks::sink> m_oldDestination;
};
BOOST_AUTO_TEST_SUITE(Util)
@@ -624,7 +624,7 @@
logFromModule1();
auto os2 = make_shared<output_test_stream>();
- Logging::setDestination(os2);
+ Logging::setDestination(Logging::makeDefaultStreamDestination(os2));
weak_ptr<output_test_stream> os2weak(os2);
os2.reset();
@@ -646,6 +646,16 @@
BOOST_CHECK(os2weak.expired());
}
+BOOST_AUTO_TEST_CASE(SetNullptrDestination)
+{
+ Logging::setDestination(nullptr);
+ logFromModule1();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(""));
+ // The default Boost.Log output is still expected
+}
+
BOOST_AUTO_TEST_SUITE_END() // TestLogging
BOOST_AUTO_TEST_SUITE_END() // Util