core: change logging from environment variable to configuration file-based

GlobalConfigurationFixture uses unit-tests.conf to determine default
and module specific logging levels. Logging will gracefully default to
INFO if unit-tests.conf is not found. See unit-tests.conf.sample for a
sample configuration file.

refs: #1375, #1267

Change-Id: Ib0c4eb4149748e6658f94ef1afa23ddd3072c0fa
diff --git a/daemon/common.hpp b/daemon/common.hpp
index 32ad4a4..76a553d 100644
--- a/daemon/common.hpp
+++ b/daemon/common.hpp
@@ -33,6 +33,8 @@
 #include <boost/asio.hpp>
 #include <boost/assert.hpp>
 #include <boost/functional/hash.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
 
 #include <vector>
 #include <list>
@@ -44,8 +46,6 @@
 #include <algorithm>
 #include <numeric>
 
-#include "core/logger.hpp"
-
 namespace nfd {
 
 using boost::noncopyable;
diff --git a/daemon/core/logger-factory.cpp b/daemon/core/logger-factory.cpp
new file mode 100644
index 0000000..4a353f1
--- /dev/null
+++ b/daemon/core/logger-factory.cpp
@@ -0,0 +1,178 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "logger-factory.hpp"
+
+namespace nfd {
+
+LoggerFactory&
+LoggerFactory::getInstance()
+{
+  static LoggerFactory globalLoggerFactory;
+
+  return globalLoggerFactory;
+}
+
+LoggerFactory::LoggerFactory()
+  : m_defaultLevel(LOG_INFO)
+{
+  m_levelNames["NONE"] = LOG_NONE;
+  m_levelNames["ERROR"] = LOG_ERROR;
+  m_levelNames["WARN"] = LOG_WARN;
+  m_levelNames["INFO"] = LOG_INFO;
+  m_levelNames["DEBUG"] = LOG_DEBUG;
+  m_levelNames["TRACE"] = LOG_TRACE;
+  m_levelNames["ALL"] = LOG_ALL;
+}
+
+void
+LoggerFactory::setConfigFile(ConfigFile& config)
+{
+  config.addSectionHandler("log", bind(&LoggerFactory::onConfig, this, _1, _2, _3));
+}
+
+LogLevel
+LoggerFactory::parseLevel(const std::string& level)
+{
+  std::string upperLevel = level;
+  boost::to_upper(upperLevel);
+
+  // std::cerr << "parsing level: " << upperLevel << std::endl;;
+  // std::cerr << "# levels: " << m_levelNames.size() << std::endl;
+  // std::cerr << m_levelNames.begin()->first << std::endl;
+
+  LevelMap::const_iterator levelIt = m_levelNames.find(upperLevel);
+  if (levelIt != m_levelNames.end())
+    {
+      return levelIt->second;
+    }
+  try
+    {
+      uint32_t levelNo = boost::lexical_cast<uint32_t>(level);
+
+      if ((LOG_NONE <= levelNo && levelNo <= LOG_TRACE) ||
+          levelNo == LOG_ALL)
+        {
+          return static_cast<LogLevel>(levelNo);
+        }
+    }
+  catch (const boost::bad_lexical_cast& error)
+    {
+    }
+  throw LoggerFactory::Error("Unsupported logging level \"" +
+                             level + "\"");
+}
+
+
+void
+LoggerFactory::onConfig(const ConfigSection& section,
+                        bool isDryRun,
+                        const std::string& filename)
+{
+// log
+// {
+//   ; default_level specifies the logging level for modules
+//   ; that are not explicitly named. All debugging levels
+//   ; listed above the selected value are enabled.
+//
+//   default_level INFO
+//
+//   ; You may also override the default for specific modules:
+//
+//   FibManager DEBUG
+//   Forwarder WARN
+// }
+
+  // std::cerr << "loading logging configuration" << std::endl;
+  for (ConfigSection::const_iterator item = section.begin();
+       item != section.end();
+       ++item)
+    {
+      std::string levelString;
+      try
+        {
+          levelString = item->second.get_value<std::string>();
+        }
+      catch (const boost::property_tree::ptree_error& error)
+        {
+        }
+
+      if (levelString.empty())
+        {
+          throw LoggerFactory::Error("No logging level found for option \"" + item->first + "\"");
+        }
+
+      LogLevel level = parseLevel(levelString);
+
+      if (item->first == "default_level")
+        {
+          if (!isDryRun)
+            {
+              setDefaultLevel(level);
+            }
+        }
+      else
+        {
+          LoggerMap::iterator loggerIt = m_loggers.find(item->first);
+          if (loggerIt == m_loggers.end())
+            {
+              throw LoggerFactory::Error("Invalid module name \"" +
+                                         item->first + "\" in configuration file");
+            }
+
+          if (!isDryRun)
+            {
+              // std::cerr << "changing level for module " << item->first << " to " << level << std::endl;
+              loggerIt->second.setLogLevel(level);
+            }
+        }
+    }
+}
+
+void
+LoggerFactory::setDefaultLevel(LogLevel level)
+{
+  // std::cerr << "changing to default_level " << level << std::endl;
+
+  m_defaultLevel = level;
+  for (LoggerMap::iterator i = m_loggers.begin(); i != m_loggers.end(); ++i)
+    {
+      // std::cerr << "changing " << i->first << " to default " << m_defaultLevel << std::endl;
+      i->second.setLogLevel(m_defaultLevel);
+    }
+}
+
+Logger&
+LoggerFactory::create(const std::string& moduleName)
+{
+  return LoggerFactory::getInstance().createLogger(moduleName);
+}
+
+Logger&
+LoggerFactory::createLogger(const std::string& moduleName)
+{
+  // std::cerr << "creating logger for " << moduleName
+  //           << " with level " << m_defaultLevel << std::endl;
+
+  std::pair<LoggerMap::iterator, bool> loggerIt =
+    m_loggers.insert(NameAndLogger(moduleName, Logger(moduleName, m_defaultLevel)));
+
+  return loggerIt.first->second;
+}
+
+std::list<std::string>
+LoggerFactory::getModules() const
+{
+  std::list<std::string> modules;
+  for (LoggerMap::const_iterator i = m_loggers.begin(); i != m_loggers.end(); ++i)
+    {
+      modules.push_back(i->first);
+    }
+
+  return modules;
+}
+
+} // namespace nfd
diff --git a/daemon/core/logger-factory.hpp b/daemon/core/logger-factory.hpp
new file mode 100644
index 0000000..52a05f4
--- /dev/null
+++ b/daemon/core/logger-factory.hpp
@@ -0,0 +1,89 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_CORE_LOGGER_FACTORY_HPP
+#define NFD_CORE_LOGGER_FACTORY_HPP
+
+#include "common.hpp"
+#include "mgmt/config-file.hpp"
+#include "logger.hpp"
+
+namespace nfd {
+
+class LoggerFactory : noncopyable
+{
+public:
+
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& error)
+      : std::runtime_error(error)
+    {
+    }
+  };
+
+  static LoggerFactory&
+  getInstance();
+
+  void
+  setConfigFile(ConfigFile& config);
+
+  void
+  onConfig(const ConfigSection& section, bool isDryRun, const std::string& filename);
+
+  std::list<std::string>
+  getModules() const;
+
+  static Logger&
+  create(const std::string& moduleName);
+
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+
+  // these methods are used during unit-testing
+
+  LogLevel
+  getDefaultLevel() const;
+
+  void
+  setDefaultLevel(LogLevel level);
+
+private:
+
+  LoggerFactory();
+
+  Logger&
+  createLogger(const std::string& moduleName);
+
+  LogLevel
+  parseLevel(const std::string& level);
+
+private:
+
+  typedef std::map<std::string, LogLevel> LevelMap;
+  typedef std::pair<std::string, LogLevel> NameAndLevel;
+
+  LevelMap m_levelNames;
+
+  typedef std::map<std::string, Logger> LoggerMap;
+  typedef std::pair<std::string, Logger> NameAndLogger;
+
+  LoggerMap m_loggers;
+
+  LogLevel m_defaultLevel;
+};
+
+inline LogLevel
+LoggerFactory::getDefaultLevel() const
+{
+  return m_defaultLevel;
+}
+
+} // namespace nfd
+
+#endif // NFD_CORE_LOGGER_FACTORY_HPP
diff --git a/daemon/core/logger.cpp b/daemon/core/logger.cpp
deleted file mode 100644
index 928b112..0000000
--- a/daemon/core/logger.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (C) 2014 Named Data Networking Project
- * See COPYING for copyright and distribution information.
- *
- * Author: Ilya Moiseenko <iliamo@ucla.edu>
- */
-
-#include "logger.hpp"
-#include <boost/algorithm/string.hpp>
-
-namespace nfd {
-
-inline static bool
-isNumber(const std::string& s)
-{
-  if (s.empty())
-    return false;
-
-  for (std::string::const_iterator i = s.begin(); i != s.end(); ++i)
-    {
-      if (!std::isdigit(*i))
-        return false;
-    }
-
-  return true;
-}
-
-Logger::Logger(const std::string& name)
-  : m_moduleName(name)
-{
-  char* nfdLog = getenv("NFD_LOG");
-  if (nfdLog)
-    {
-      std::string level = nfdLog;
-      if (isNumber(nfdLog))
-        {
-          setLogLevel(boost::lexical_cast<uint32_t>(nfdLog));
-        }
-      else if (boost::iequals(level, "none"))
-        {
-          m_enabledLogLevel = LOG_NONE;
-        }
-      else if (boost::iequals(level, "error"))
-        {
-          m_enabledLogLevel = LOG_ERROR;
-        }
-      else if (boost::iequals(level, "warn"))
-        {
-          m_enabledLogLevel = LOG_WARN;
-        }
-      else if (boost::iequals(level, "info"))
-        {
-          m_enabledLogLevel = LOG_INFO;
-        }
-      else if (boost::iequals(level, "debug"))
-        {
-          m_enabledLogLevel = LOG_DEBUG;
-        }
-      else if (boost::iequals(level, "trace"))
-        {
-          m_enabledLogLevel = LOG_TRACE;
-        }
-      else
-        {
-          m_enabledLogLevel = LOG_ALL;
-        }
-    }
-  else
-    {
-      m_enabledLogLevel = LOG_WARN;
-    }
-}
-
-std::ostream&
-operator<<(std::ostream& output, const Logger& obj)
-{
-  output << obj.getName();
-  return output;
-}
-
-} // namespace nfd
diff --git a/daemon/core/logger.hpp b/daemon/core/logger.hpp
index aad2394..604c304 100644
--- a/daemon/core/logger.hpp
+++ b/daemon/core/logger.hpp
@@ -10,7 +10,6 @@
 #define NFD_CORE_LOGGER_HPP
 
 #include "common.hpp"
-#include <iostream>
 
 namespace nfd {
 
@@ -28,81 +27,103 @@
 class Logger
 {
 public:
-  explicit
-  Logger(const std::string& name);
-  
-  bool
-  isEnabled(LogLevel level)
+
+  Logger(const std::string& name, LogLevel level)
+    : m_moduleName(name)
+    , m_enabledLogLevel(level)
   {
+  }
+
+  bool
+  isEnabled(LogLevel level) const
+  {
+    // std::cerr << m_moduleName <<
+    //   " enabled = " << m_enabledLogLevel
+    //           << " level = " << level << std::endl;
     return (m_enabledLogLevel >= level);
   }
 
   void
-  setLogLevel(uint32_t level)
+  setLogLevel(LogLevel level)
   {
-    m_enabledLogLevel = static_cast<LogLevel>(level);
+    m_enabledLogLevel = level;
   }
-  
+
   const std::string&
   getName() const
   {
     return m_moduleName;
   }
-  
+
+  void
+  setName(const std::string& name)
+  {
+    m_moduleName = name;
+  }
+
 private:
   std::string m_moduleName;
-  uint32_t    m_enabledLogLevel;
+  LogLevel    m_enabledLogLevel;
 };
 
-std::ostream&
-operator<<(std::ostream& output, const Logger& obj);
+inline std::ostream&
+operator<<(std::ostream& output, const Logger& obj)
+{
+  output << obj.getName();
+  return output;
+}
+
+} // namespace nfd
+
+#include "core/logger-factory.hpp"
+
+namespace nfd {
 
 #define NFD_LOG_INIT(name) \
-  static nfd::Logger \
-  g_logger = nfd::Logger(name);
+static nfd::Logger& g_logger = nfd::LoggerFactory::create(name);
 
 #define NFD_LOG_INCLASS_DECLARE()        \
-  static nfd::Logger g_logger;
+static nfd::Logger& g_logger;
 
 #define NFD_LOG_INCLASS_DEFINE(cls, name)        \
-  nfd::Logger cls::g_logger = nfd::Logger(name);
+nfd::Logger& cls::g_logger = nfd::LoggerFactory::create(name);
 
 #define NFD_LOG_INCLASS_TEMPLATE_DEFINE(cls, name)   \
-  template<class T> \
-  nfd::Logger cls<T>::g_logger = nfd::Logger(name);
+template<class T>                                                       \
+nfd::Logger& cls<T>::g_logger = nfd::LoggerFactory::create(name);
 
-#define NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(cls, specialization, name)    \
-  template<> \
-  nfd::Logger cls<specialization>::g_logger = nfd::Logger(name);
+#define NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(cls, specialization, name) \
+template<> \
+nfd::Logger& cls<specialization>::g_logger = nfd::LoggerFactory::create(name);
 
 #define NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(cls, s1, s2, name) \
-  template<> \
-  nfd::Logger cls<s1, s2>::g_logger = nfd::Logger(name);
+template<>                                                              \
+nfd::Logger& cls<s1, s2>::g_logger = nfd::LoggerFactory::create(name);
+
 
 #define NFD_LOG_TRACE(expression) \
-    if(g_logger.isEnabled(nfd::LOG_TRACE)) \
-       std::cerr<<"TRACE: "<<"["<<g_logger<<"] " << expression << "\n"
+if (g_logger.isEnabled(nfd::LOG_TRACE))                                 \
+  std::cerr<<"TRACE: "<<"["<<g_logger<<"] " << expression << "\n"
 
 #define NFD_LOG_DEBUG(expression)\
-    if(g_logger.isEnabled(nfd::LOG_DEBUG)) \
-       std::cerr<<"DEBUG: "<<"["<<g_logger<<"] " << expression <<"\n"
+if (g_logger.isEnabled(nfd::LOG_DEBUG))                                 \
+  std::cerr<<"DEBUG: "<<"["<<g_logger<<"] " << expression <<"\n"
 
-#define NFD_LOG_WARN(expression) \
-    if(g_logger.isEnabled(nfd::LOG_WARN)) \
-       std::cerr<<"WARNING: "<<"["<<g_logger<<"] " << expression <<"\n"
+#define NFD_LOG_WARN(expression)                                        \
+if (g_logger.isEnabled(nfd::LOG_WARN))                                  \
+  std::cerr<<"WARNING: "<<"["<<g_logger<<"] " << expression <<"\n"
 
 #define NFD_LOG_INFO(expression)\
-    if(g_logger.isEnabled(nfd::LOG_INFO)) \
-       std::cerr<<"INFO: "<<"["<<g_logger<<"] " << expression <<"\n"
-  
-#define NFD_LOG_ERROR(expression)\
-    if(g_logger.isEnabled(nfd::LOG_ERROR)) \
-       std::cerr<<"ERROR: "<<"["<<g_logger<<"] " << expression <<"\n"
-  
-#define NFD_LOG_FATAL(expression)\
-    std::cerr<<"FATAL: "<<"["<<g_logger<<"] " << expression <<"\n"
-  
-} //namespace nfd
+if (g_logger.isEnabled(nfd::LOG_INFO))                          \
+  std::cerr<<"INFO: "<<"["<<g_logger<<"] " << expression <<"\n"
 
+#define NFD_LOG_ERROR(expression)                                       \
+if (g_logger.isEnabled(nfd::LOG_ERROR))                                  \
+  std::cerr<<"ERROR: "<<"["<<g_logger<<"] " << expression <<"\n"
+
+#define NFD_LOG_FATAL(expression)\
+std::cerr<<"FATAL: "<<"["<<g_logger<<"] " << expression <<"\n"
+
+} //namespace nfd
 
 #endif
diff --git a/daemon/core/network-interface.cpp b/daemon/core/network-interface.cpp
index 65a690c..7319e97 100644
--- a/daemon/core/network-interface.cpp
+++ b/daemon/core/network-interface.cpp
@@ -5,6 +5,7 @@
  */
 
 #include "network-interface.hpp"
+#include "core/logger.hpp"
 
 #include <boost/foreach.hpp>
 
diff --git a/daemon/face/datagram-face.hpp b/daemon/face/datagram-face.hpp
index cbda459..f59374f 100644
--- a/daemon/face/datagram-face.hpp
+++ b/daemon/face/datagram-face.hpp
@@ -8,6 +8,7 @@
 #define NFD_FACE_DATAGRAM_FACE_HPP
 
 #include "face.hpp"
+#include "core/logger.hpp"
 
 namespace nfd {
 
@@ -30,14 +31,14 @@
 
   virtual
   ~DatagramFace();
-  
+
   // from Face
   virtual void
   sendInterest(const Interest& interest);
-  
+
   virtual void
   sendData(const Data& data);
-  
+
   virtual void
   close();
 
@@ -54,13 +55,13 @@
    */
   void
   resetRecentUsage();
-  
+
   bool
   hasBeenUsedRecently() const;
 
   void
   setOnDemand(bool isOnDemand);
-  
+
 protected:
   void
   receiveDatagram(const uint8_t* buffer,
@@ -69,10 +70,10 @@
 
   void
   keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face);
-  
+
   void
   closeSocket();
-  
+
 protected:
   shared_ptr<typename protocol::socket> m_socket;
   uint8_t m_inputBuffer[MAX_NDN_PACKET_SIZE];
@@ -101,7 +102,7 @@
 DatagramFace<T>::~DatagramFace()
 {
 }
-  
+
 template <class T>
 inline void
 DatagramFace<T>::sendInterest(const Interest& interest)
@@ -110,10 +111,10 @@
   m_socket->async_send(boost::asio::buffer(interest.wireEncode().wire(),
                                            interest.wireEncode().size()),
                        bind(&DatagramFace<T>::handleSend, this, _1, interest.wireEncode()));
-    
+
   // anything else should be done here?
 }
-  
+
 template <class T>
 inline void
 DatagramFace<T>::sendData(const Data& data)
@@ -122,7 +123,7 @@
   m_socket->async_send(boost::asio::buffer(data.wireEncode().wire(),
                                            data.wireEncode().size()),
                        bind(&DatagramFace<T>::handleSend, this, _1, data.wireEncode()));
-    
+
   // anything else should be done here?
 }
 
@@ -134,20 +135,20 @@
   if (error != 0) {
     if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
       return;
-    
+
     if (!m_socket->is_open())
     {
       onFail("Tunnel closed");
       return;
     }
-    
+
     NFD_LOG_WARN("[id:" << this->getId()
                   << ",endpoint:" << m_socket->local_endpoint()
                   << "] Send operation failed, closing socket: "
                   << error.category().message(error.value()));
-    
+
     closeSocket();
-    
+
     if (error == boost::asio::error::eof)
     {
       onFail("Tunnel closed");
@@ -159,24 +160,24 @@
     }
     return;
   }
-  
+
   NFD_LOG_TRACE("[id:" << this->getId()
                 << ",endpoint:" << m_socket->local_endpoint()
                 << "] Successfully sent: " << wire.size() << " bytes");
   // do nothing (needed to retain validity of wire memory block
 }
-  
+
 template <class T>
 inline void
 DatagramFace<T>::close()
 {
   if (!m_socket->is_open())
     return;
-    
+
   NFD_LOG_INFO("[id:" << this->getId()
                << ",endpoint:" << m_socket->local_endpoint()
                << "] Close tunnel");
-    
+
   closeSocket();
   onFail("Close tunnel");
 }
@@ -273,20 +274,20 @@
 DatagramFace<T>::keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face)
 {
 }
-  
+
 template <class T>
 inline void
 DatagramFace<T>::closeSocket()
 {
   NFD_LOG_DEBUG("closeSocket  " << m_socket->local_endpoint());
   boost::asio::io_service& io = m_socket->get_io_service();
-    
+
   // use the non-throwing variants and ignore errors, if any
   boost::system::error_code error;
   m_socket->shutdown(protocol::socket::shutdown_both, error);
   m_socket->close(error);
   // after this, handlers will be called with an error code
-    
+
   // ensure that the Face object is alive at least until all pending
   // handlers are dispatched
   io.post(bind(&DatagramFace<T>::keepFaceAliveUntilAllHandlersExecuted,
diff --git a/daemon/face/ethernet-face.cpp b/daemon/face/ethernet-face.cpp
index e619724..66dcf08 100644
--- a/daemon/face/ethernet-face.cpp
+++ b/daemon/face/ethernet-face.cpp
@@ -5,6 +5,7 @@
  */
 
 #include "ethernet-face.hpp"
+#include "core/logger.hpp"
 #include "core/network-interface.hpp"
 
 #include <pcap/pcap.h>
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index 2801c4f..4b1d8c1 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -5,6 +5,7 @@
  */
 
 #include "ethernet-factory.hpp"
+#include "core/logger.hpp"
 #include "core/global-io.hpp"
 #include "core/network-interface.hpp"
 
diff --git a/daemon/face/multicast-udp-face.cpp b/daemon/face/multicast-udp-face.cpp
index b5632e2..34d40f0 100644
--- a/daemon/face/multicast-udp-face.cpp
+++ b/daemon/face/multicast-udp-face.cpp
@@ -33,7 +33,7 @@
                                               interest.wireEncode().size()),
                           m_multicastGroup,
                           bind(&DatagramFace<protocol>::handleSend, this, _1, interest.wireEncode()));
-    
+
   // anything else should be done here?
 }
 
diff --git a/daemon/face/stream-face.hpp b/daemon/face/stream-face.hpp
index a1aa7c1..1b5b124 100644
--- a/daemon/face/stream-face.hpp
+++ b/daemon/face/stream-face.hpp
@@ -9,6 +9,7 @@
 
 #include "face.hpp"
 #include "local-face.hpp"
+#include "core/logger.hpp"
 
 namespace nfd {
 
diff --git a/daemon/fw/client-control-strategy.cpp b/daemon/fw/client-control-strategy.cpp
index 5028879..14b138d 100644
--- a/daemon/fw/client-control-strategy.cpp
+++ b/daemon/fw/client-control-strategy.cpp
@@ -5,6 +5,7 @@
  */
 
 #include "client-control-strategy.hpp"
+#include "core/logger.hpp"
 
 namespace nfd {
 namespace fw {
diff --git a/daemon/fw/face-table.cpp b/daemon/fw/face-table.cpp
index ca7b56f..8601598 100644
--- a/daemon/fw/face-table.cpp
+++ b/daemon/fw/face-table.cpp
@@ -6,6 +6,7 @@
 
 #include "face-table.hpp"
 #include "forwarder.hpp"
+#include "core/logger.hpp"
 
 namespace nfd {
 
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index 58ce5a3..8ffda2f 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -6,6 +6,7 @@
 
 #include "forwarder.hpp"
 #include "available-strategies.hpp"
+#include "core/logger.hpp"
 
 namespace nfd {
 
@@ -26,6 +27,11 @@
   fw::installStrategies(*this);
 }
 
+Forwarder::~Forwarder()
+{
+
+}
+
 void
 Forwarder::onIncomingInterest(Face& inFace, const Interest& interest)
 {
diff --git a/daemon/fw/forwarder.hpp b/daemon/fw/forwarder.hpp
index c3898c6..51a75dc 100644
--- a/daemon/fw/forwarder.hpp
+++ b/daemon/fw/forwarder.hpp
@@ -32,6 +32,9 @@
 public:
   Forwarder();
 
+  VIRTUAL_WITH_TESTS
+  ~Forwarder();
+
   const ForwarderCounters&
   getCounters() const;
 
diff --git a/daemon/fw/strategy.cpp b/daemon/fw/strategy.cpp
index d41786d..8ce46c1 100644
--- a/daemon/fw/strategy.cpp
+++ b/daemon/fw/strategy.cpp
@@ -6,6 +6,7 @@
 
 #include "strategy.hpp"
 #include "forwarder.hpp"
+#include "core/logger.hpp"
 
 namespace nfd {
 namespace fw {
diff --git a/daemon/main.cpp b/daemon/main.cpp
index dff26cd..542d2a0 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -24,39 +24,81 @@
 struct ProgramOptions
 {
   bool showUsage;
+  bool showModules;
   std::string config;
 };
 
 class Nfd : noncopyable
 {
 public:
-  explicit
-  Nfd(const std::string& configFile)
-    : m_internalFace(new InternalFace())
-    , m_fibManager(m_forwarder.getFib(),
-                   bind(&Forwarder::getFace, &m_forwarder, _1),
-                   m_internalFace)
-    , m_faceManager(m_forwarder.getFaceTable(), m_internalFace)
-    , m_strategyChoiceManager(m_forwarder.getStrategyChoice(), m_internalFace)
-    , m_statusServer(m_internalFace, m_forwarder)
+
+  void
+  initialize(const std::string& configFile)
   {
+    initializeLogging(configFile);
+
+    m_forwarder = make_shared<Forwarder>();
+
+    initializeManagement(configFile);
+  }
+
+
+  void
+  initializeLogging(const std::string& configFile)
+  {
+    ConfigFile config;
+    LoggerFactory::getInstance().setConfigFile(config);
+
+    for (size_t i = 0; i < N_SUPPORTED_CONFIG_SECTIONS; ++i)
+      {
+        if (SUPPORTED_CONFIG_SECTIONS[i] != "log")
+          {
+            config.addSectionHandler(SUPPORTED_CONFIG_SECTIONS[i],
+                                     bind(std::plus<int>(), 0, 0)); // no-op.
+          }
+      }
+
+    config.parse(configFile, true);
+    config.parse(configFile, false);
+  }
+
+  void
+  initializeManagement(const std::string& configFile)
+  {
+    m_internalFace = make_shared<InternalFace>();
+
+    m_fibManager = make_shared<FibManager>(boost::ref(m_forwarder->getFib()),
+                                           bind(&Forwarder::getFace, m_forwarder.get(), _1),
+                                           m_internalFace);
+
+    m_faceManager = make_shared<FaceManager>(boost::ref(m_forwarder->getFaceTable()),
+                                             m_internalFace);
+
+    m_strategyChoiceManager =
+      make_shared<StrategyChoiceManager>(boost::ref(m_forwarder->getStrategyChoice()),
+                                         m_internalFace);
+
+    m_statusServer = make_shared<StatusServer>(m_internalFace,
+                                               boost::ref(*m_forwarder));
+
     ConfigFile config;
     m_internalFace->getValidator().setConfigFile(config);
 
-    m_forwarder.addFace(m_internalFace);
+    m_forwarder->addFace(m_internalFace);
 
-    m_faceManager.setConfigFile(config);
+    m_faceManager->setConfigFile(config);
+
+    config.addSectionHandler("log", bind(std::plus<int>(), 0, 0)); // no-op
 
     // parse config file
     config.parse(configFile, true);
     config.parse(configFile, false);
 
     // add FIB entry for NFD Management Protocol
-    shared_ptr<fib::Entry> entry = m_forwarder.getFib().insert("/localhost/nfd").first;
+    shared_ptr<fib::Entry> entry = m_forwarder->getFib().insert("/localhost/nfd").first;
     entry->addNextHop(m_internalFace, 0);
   }
 
-
   static void
   printUsage(std::ostream& os, const std::string& programName)
   {
@@ -67,21 +109,38 @@
        << "\n"
        << "Options:\n"
        << "  [--help]   - print this help message\n"
+       << "  [--modules] - list available logging modules\n"
        << "  [--config /path/to/nfd.conf] - path to configuration file "
        << "(default: " << DEFAULT_CONFIG_FILE << ")\n"
       ;
   }
 
+  static void
+  printModules(std::ostream& os)
+  {
+    using namespace std;
+
+    os << "Available logging modules: \n";
+
+    list<string> modules(LoggerFactory::getInstance().getModules());
+    for (list<string>::const_iterator i = modules.begin(); i != modules.end(); ++i)
+      {
+        os << *i << "\n";
+      }
+  }
+
   static bool
   parseCommandLine(int argc, char** argv, ProgramOptions& options)
   {
     options.showUsage = false;
+    options.showModules = false;
     options.config = DEFAULT_CONFIG_FILE;
 
     while (true) {
       int optionIndex = 0;
       static ::option longOptions[] = {
         { "help"   , no_argument      , 0, 0 },
+        { "modules", no_argument      , 0, 0 },
         { "config" , required_argument, 0, 0 },
         { 0        , 0                , 0, 0 }
       };
@@ -90,21 +149,23 @@
         break;
 
       switch (c) {
-      case 0:
-        switch (optionIndex) {
-        case 0: // help
-          options.showUsage = true;
+        case 0:
+          switch (optionIndex) {
+            case 0: // help
+              options.showUsage = true;
+              break;
+            case 1: // modules
+              options.showModules = true;
+              break;
+            case 2: // config
+              options.config = ::optarg;
+              break;
+            default:
+              return false;
+          }
           break;
-        case 1: // config
-          options.config = ::optarg;
-          break;
-        default:
-          return false;
-        }
-        break;
       }
     }
-
     return true;
   }
 
@@ -131,15 +192,28 @@
   }
 
 private:
-  Forwarder m_forwarder;
-  shared_ptr<InternalFace> m_internalFace;
+  shared_ptr<Forwarder> m_forwarder;
 
-  FibManager            m_fibManager;
-  FaceManager           m_faceManager;
-  StrategyChoiceManager m_strategyChoiceManager;
-  StatusServer          m_statusServer;
+  shared_ptr<InternalFace>          m_internalFace;
+  shared_ptr<FibManager>            m_fibManager;
+  shared_ptr<FaceManager>           m_faceManager;
+  shared_ptr<StrategyChoiceManager> m_strategyChoiceManager;
+  shared_ptr<StatusServer>          m_statusServer;
+
+  static const std::string SUPPORTED_CONFIG_SECTIONS[];
+  static const size_t      N_SUPPORTED_CONFIG_SECTIONS;
 };
 
+const std::string Nfd::SUPPORTED_CONFIG_SECTIONS[] =
+  {
+    "log",
+    "face_system",
+    "authorizations",
+  };
+
+const size_t Nfd::N_SUPPORTED_CONFIG_SECTIONS =
+  sizeof(SUPPORTED_CONFIG_SECTIONS) / sizeof(std::string);
+
 } // namespace nfd
 
 int
@@ -158,33 +232,46 @@
     return 0;
   }
 
+  if (options.showModules) {
+    Nfd::printModules(std::cout);
+    return 0;
+  }
+
+  Nfd nfdInstance;
+
   try {
-    Nfd nfdInstance(options.config);
-
-    boost::asio::signal_set signalSet(getGlobalIoService());
-    signalSet.add(SIGINT);
-    signalSet.add(SIGTERM);
-    signalSet.add(SIGHUP);
-    signalSet.add(SIGUSR1);
-    signalSet.add(SIGUSR2);
-    signalSet.async_wait(bind(&Nfd::terminate, &nfdInstance, _1, _2,
-                              boost::ref(signalSet)));
-
-    getGlobalIoService().run();
+    nfdInstance.initialize(options.config);
   }
   catch (boost::filesystem::filesystem_error& e) {
     if (e.code() == boost::system::errc::permission_denied) {
-      NFD_LOG_FATAL("Permissions denied for " << e.path1() << ". "
-                    << argv[0] << " should be run as superuser");
+      NFD_LOG_FATAL("Permissions denied for " << e.path1() << ". " <<
+                    argv[0] << " should be run as superuser");
     }
     else {
       NFD_LOG_FATAL(e.what());
     }
+    return 1;
+  }
+  catch (const std::exception& e) {
+    NFD_LOG_FATAL(e.what());
     return 2;
   }
+
+  boost::asio::signal_set signalSet(getGlobalIoService());
+  signalSet.add(SIGINT);
+  signalSet.add(SIGTERM);
+  signalSet.add(SIGHUP);
+  signalSet.add(SIGUSR1);
+  signalSet.add(SIGUSR2);
+  signalSet.async_wait(bind(&Nfd::terminate, &nfdInstance, _1, _2,
+                            boost::ref(signalSet)));
+
+  try {
+    getGlobalIoService().run();
+  }
   catch (std::exception& e) {
     NFD_LOG_FATAL(e.what());
-    return 1;
+    return 3;
   }
 
   return 0;
diff --git a/daemon/mgmt/command-validator.cpp b/daemon/mgmt/command-validator.cpp
index fd8a124..eb76572 100644
--- a/daemon/mgmt/command-validator.cpp
+++ b/daemon/mgmt/command-validator.cpp
@@ -5,6 +5,8 @@
  */
 
 #include "command-validator.hpp"
+#include "core/logger.hpp"
+
 #include <ndn-cpp-dev/util/io.hpp>
 #include <ndn-cpp-dev/security/identity-certificate.hpp>
 
@@ -178,4 +180,3 @@
 }
 
 } // namespace nfd
-
diff --git a/daemon/mgmt/command-validator.hpp b/daemon/mgmt/command-validator.hpp
index 290cabd..0162796 100644
--- a/daemon/mgmt/command-validator.hpp
+++ b/daemon/mgmt/command-validator.hpp
@@ -7,6 +7,7 @@
 #ifndef NFD_MGMT_COMMAND_VALIDATOR_HPP
 #define NFD_MGMT_COMMAND_VALIDATOR_HPP
 
+#include "common.hpp"
 #include "config-file.hpp"
 #include <ndn-cpp-dev/util/command-interest-validator.hpp>
 
@@ -19,6 +20,7 @@
   class Error : public std::runtime_error
   {
   public:
+    explicit
     Error(const std::string& what)
       : std::runtime_error(what)
     {
diff --git a/daemon/mgmt/config-file.cpp b/daemon/mgmt/config-file.cpp
index d0eddd5..bc1a495 100644
--- a/daemon/mgmt/config-file.cpp
+++ b/daemon/mgmt/config-file.cpp
@@ -4,8 +4,8 @@
  * See COPYING for copyright and distribution information.
  */
 
-
 #include "config-file.hpp"
+#include "core/logger.hpp"
 
 #include <boost/property_tree/info_parser.hpp>
 
@@ -105,4 +105,3 @@
 }
 
 }
-
diff --git a/daemon/mgmt/config-file.hpp b/daemon/mgmt/config-file.hpp
index 15eca05..dd5a43c 100644
--- a/daemon/mgmt/config-file.hpp
+++ b/daemon/mgmt/config-file.hpp
@@ -25,6 +25,7 @@
   class Error : public std::runtime_error
   {
   public:
+    explicit
     Error(const std::string& what)
       : std::runtime_error(what)
     {
@@ -86,4 +87,3 @@
 
 
 #endif // NFD_MGMT_CONFIG_FILE_HPP
-
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 518021a..55f4d9c 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -6,6 +6,7 @@
 
 #include "face-manager.hpp"
 
+#include "core/logger.hpp"
 #include "core/face-uri.hpp"
 #include "core/network-interface.hpp"
 #include "fw/face-table.hpp"
diff --git a/daemon/mgmt/face-status-publisher.cpp b/daemon/mgmt/face-status-publisher.cpp
index 89c731e..3589d7b 100644
--- a/daemon/mgmt/face-status-publisher.cpp
+++ b/daemon/mgmt/face-status-publisher.cpp
@@ -3,7 +3,9 @@
  * Copyright (C) 2014 Named Data Networking Project
  * See COPYING for copyright and distribution information.
  */
+
 #include "face-status-publisher.hpp"
+#include "core/logger.hpp"
 
 namespace nfd {
 
diff --git a/daemon/mgmt/fib-enumeration-publisher.cpp b/daemon/mgmt/fib-enumeration-publisher.cpp
index 79532ba..b672e7e 100644
--- a/daemon/mgmt/fib-enumeration-publisher.cpp
+++ b/daemon/mgmt/fib-enumeration-publisher.cpp
@@ -6,7 +6,7 @@
 
 #include "fib-enumeration-publisher.hpp"
 
-#include "common.hpp"
+#include "core/logger.hpp"
 
 #include <ndn-cpp-dev/management/nfd-fib-entry.hpp>
 
diff --git a/daemon/mgmt/fib-manager.cpp b/daemon/mgmt/fib-manager.cpp
index 0bf4f34..34554e6 100644
--- a/daemon/mgmt/fib-manager.cpp
+++ b/daemon/mgmt/fib-manager.cpp
@@ -6,6 +6,7 @@
 
 #include "fib-manager.hpp"
 
+#include "core/logger.hpp"
 #include "table/fib.hpp"
 #include "fw/forwarder.hpp"
 #include "mgmt/internal-face.hpp"
diff --git a/daemon/mgmt/internal-face.cpp b/daemon/mgmt/internal-face.cpp
index 81bd2de..a5bcf5a 100644
--- a/daemon/mgmt/internal-face.cpp
+++ b/daemon/mgmt/internal-face.cpp
@@ -5,6 +5,7 @@
  */
 
 #include "internal-face.hpp"
+#include "core/logger.hpp"
 #include "core/global-io.hpp"
 
 namespace nfd {
diff --git a/daemon/mgmt/manager-base.cpp b/daemon/mgmt/manager-base.cpp
index 08d845c..e608ffc 100644
--- a/daemon/mgmt/manager-base.cpp
+++ b/daemon/mgmt/manager-base.cpp
@@ -5,6 +5,7 @@
  */
 
 #include "manager-base.hpp"
+#include "core/logger.hpp"
 
 namespace nfd {
 
diff --git a/daemon/mgmt/segment-publisher.cpp b/daemon/mgmt/segment-publisher.cpp
index 82ae31e..993330f 100644
--- a/daemon/mgmt/segment-publisher.cpp
+++ b/daemon/mgmt/segment-publisher.cpp
@@ -6,7 +6,7 @@
 
 #include "segment-publisher.hpp"
 
-#include "common.hpp"
+#include "core/logger.hpp"
 #include "face/face.hpp"
 
 #include <ndn-cpp-dev/util/time.hpp>
diff --git a/daemon/mgmt/strategy-choice-manager.cpp b/daemon/mgmt/strategy-choice-manager.cpp
index a4c4e38..43824f2 100644
--- a/daemon/mgmt/strategy-choice-manager.cpp
+++ b/daemon/mgmt/strategy-choice-manager.cpp
@@ -6,6 +6,7 @@
 
 #include "strategy-choice-manager.hpp"
 #include "table/strategy-choice.hpp"
+#include "core/logger.hpp"
 #include "mgmt/app-face.hpp"
 
 namespace nfd {
@@ -166,5 +167,3 @@
 
 
 } // namespace nfd
-
-
diff --git a/daemon/table/cs-entry.cpp b/daemon/table/cs-entry.cpp
index 9e99886..c98a66c 100644
--- a/daemon/table/cs-entry.cpp
+++ b/daemon/table/cs-entry.cpp
@@ -7,6 +7,7 @@
  */
 
 #include "cs-entry.hpp"
+#include "core/logger.hpp"
 
 namespace nfd {
 namespace cs {
diff --git a/daemon/table/cs.cpp b/daemon/table/cs.cpp
index bad684e..a4c0525 100644
--- a/daemon/table/cs.cpp
+++ b/daemon/table/cs.cpp
@@ -7,6 +7,8 @@
  */
 
 #include "cs.hpp"
+#include "core/logger.hpp"
+
 #include <ndn-cpp-dev/util/crypto.hpp>
 
 #define SKIPLIST_MAX_LAYERS 32
diff --git a/daemon/table/strategy-choice.cpp b/daemon/table/strategy-choice.cpp
index db4d498..ee63d04 100644
--- a/daemon/table/strategy-choice.cpp
+++ b/daemon/table/strategy-choice.cpp
@@ -5,6 +5,7 @@
  */
 
 #include "strategy-choice.hpp"
+#include "core/logger.hpp"
 #include "fw/strategy.hpp"
 #include "pit-entry.hpp"
 #include "measurements-entry.hpp"
diff --git a/nfd.conf.sample.in b/nfd.conf.sample.in
index 3cbfc0f..b825cca 100644
--- a/nfd.conf.sample.in
+++ b/nfd.conf.sample.in
@@ -3,6 +3,38 @@
 ; {
 ; }
 
+log
+{
+  ; default_level specifies the logging level for modules
+  ; that are not explicitly named. All debugging levels
+  ; listed above the selected value are enabled.
+  ;
+  ; Valid values:
+  ;
+  ;  NONE ; no messages
+  ;  ERROR ; error messages
+  ;  WARN ; warning messages
+  ;  INFO ; informational messages (default)
+  ;  DEBUG ; debugging messages
+  ;  TRACE ; trace messages (most verbose)
+  ;  ALL ; all messages
+
+  ; default_level INFO
+
+  ; You may override default_level by assigning a logging level
+  ; to the desired module name. Module names can be found in two ways:
+  ;
+  ; Run:
+  ;   nfd --modules
+  ;
+  ; Or look for NFD_LOG_INIT(<module name>) statements in .cpp files
+  ;
+  ; Example module-level settings:
+  ;
+  ; FibManager DEBUG
+  ; Forwarder INFO
+}
+
 ; The face_system section defines what faces and channels are created.
 face_system
 {
diff --git a/tests/core/global-configuration.cpp b/tests/core/global-configuration.cpp
new file mode 100644
index 0000000..a288147
--- /dev/null
+++ b/tests/core/global-configuration.cpp
@@ -0,0 +1,35 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "../test-common.hpp"
+#include "core/logger.hpp"
+#include "mgmt/config-file.hpp"
+
+#include <boost/filesystem.hpp>
+
+namespace nfd {
+namespace tests {
+
+class GlobalConfigurationFixture
+{
+public:
+  GlobalConfigurationFixture()
+  {
+    const std::string filename = "unit-tests.conf";
+    if (boost::filesystem::exists(filename))
+      {
+        ConfigFile config;
+        LoggerFactory::getInstance().setConfigFile(config);
+
+        config.parse(filename, false);
+      }
+  }
+};
+
+BOOST_GLOBAL_FIXTURE(GlobalConfigurationFixture)
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/core/limited-io.cpp b/tests/core/limited-io.cpp
index a7b1115..784bddf 100644
--- a/tests/core/limited-io.cpp
+++ b/tests/core/limited-io.cpp
@@ -5,6 +5,7 @@
  */
 
 #include "limited-io.hpp"
+#include "core/logger.hpp"
 
 namespace nfd {
 namespace tests {
diff --git a/tests/core/logger.cpp b/tests/core/logger.cpp
index 0530dcc..0aa2e47 100644
--- a/tests/core/logger.cpp
+++ b/tests/core/logger.cpp
@@ -7,7 +7,9 @@
  */
 
 #include "core/logger.hpp"
+
 #include <boost/test/unit_test.hpp>
+
 #include <iostream>
 
 #include "tests/test-common.hpp"
@@ -17,10 +19,12 @@
 
 BOOST_FIXTURE_TEST_SUITE(CoreLogger, BaseFixture)
 
-struct LoggerFixture : protected BaseFixture
+class LoggerFixture : protected BaseFixture
 {
+public:
   LoggerFixture()
     : m_savedBuf(std::cerr.rdbuf())
+    , m_savedLevel(LoggerFactory::getInstance().getDefaultLevel())
   {
     std::cerr.rdbuf(m_buffer.rdbuf());
   }
@@ -28,10 +32,12 @@
   ~LoggerFixture()
   {
     std::cerr.rdbuf(m_savedBuf);
+    LoggerFactory::getInstance().setDefaultLevel(m_savedLevel);
   }
 
   std::stringstream m_buffer;
   std::streambuf* m_savedBuf;
+  LogLevel m_savedLevel;
 };
 
 BOOST_FIXTURE_TEST_CASE(Basic, LoggerFixture)
@@ -56,9 +62,172 @@
                     );
 }
 
+BOOST_FIXTURE_TEST_CASE(ConfigureFactory, LoggerFixture)
+{
+  NFD_LOG_INIT("ConfigureFactoryTests");
+
+  const std::string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  default_level INFO\n"
+    "}\n";
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  config.parse(LOG_CONFIG, false, "LOG_CONFIG");
+
+  NFD_LOG_TRACE("trace message JHGFDSR^1");
+  NFD_LOG_DEBUG("debug message IGg2474fdksd fo " << 15 << 16 << 17);
+  NFD_LOG_WARN("warning message XXXhdhd11" << 1 << "x");
+  NFD_LOG_INFO("info message Jjxjshj13");
+  NFD_LOG_ERROR("error message !#$&^%$#@");
+  NFD_LOG_FATAL("fatal message JJSjaamcng");
+
+  BOOST_CHECK_EQUAL(m_buffer.str(),
+                    "WARNING: [ConfigureFactoryTests] warning message XXXhdhd111x\n"
+                    "INFO: [ConfigureFactoryTests] info message Jjxjshj13\n"
+                    "ERROR: [ConfigureFactoryTests] error message !#$&^%$#@\n"
+                    "FATAL: [ConfigureFactoryTests] fatal message JJSjaamcng\n"
+                    );
+}
+
+BOOST_FIXTURE_TEST_CASE(TestNumberLevel, LoggerFixture)
+{
+  NFD_LOG_INIT("TestNumberLevel");
+
+  const std::string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  default_level 2\n" // equivalent of WARN
+    "}\n";
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  config.parse(LOG_CONFIG, false, "LOG_CONFIG");
+
+  NFD_LOG_TRACE("trace message JHGFDSR^1");
+  NFD_LOG_DEBUG("debug message IGg2474fdksd fo " << 15 << 16 << 17);
+  NFD_LOG_WARN("warning message XXXhdhd11" << 1 << "x");
+  NFD_LOG_INFO("info message Jjxjshj13");
+  NFD_LOG_ERROR("error message !#$&^%$#@");
+  NFD_LOG_FATAL("fatal message JJSjaamcng");
+
+  BOOST_CHECK_EQUAL(m_buffer.str(),
+                    "WARNING: [TestNumberLevel] warning message XXXhdhd111x\n"
+                    "ERROR: [TestNumberLevel] error message !#$&^%$#@\n"
+                    "FATAL: [TestNumberLevel] fatal message JJSjaamcng\n"
+                    );
+}
+
+static void
+testModuleBPrint()
+{
+  NFD_LOG_INIT("TestModuleB");
+  NFD_LOG_DEBUG("debug message IGg2474fdksd fo " << 15 << 16 << 17);
+}
+
+BOOST_FIXTURE_TEST_CASE(LimitModules, LoggerFixture)
+{
+  NFD_LOG_INIT("TestModuleA");
+
+  const std::string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  default_level WARN\n"
+    "}\n";
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  config.parse(LOG_CONFIG, false, "LOG_CONFIG");
+
+ // this should print
+  NFD_LOG_WARN("warning message XXXhdhd11" << 1 << "x");
+
+  // this should not because it's level is < WARN
+  testModuleBPrint();
+
+  BOOST_CHECK_EQUAL(m_buffer.str(),
+                    "WARNING: [TestModuleA] warning message XXXhdhd111x\n"
+                    );
+}
+
+BOOST_FIXTURE_TEST_CASE(ExplicitlySetModule, LoggerFixture)
+{
+  NFD_LOG_INIT("TestModuleA");
+
+  const std::string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  default_level WARN\n"
+    "  TestModuleB DEBUG\n"
+    "}\n";
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  config.parse(LOG_CONFIG, false, "LOG_CONFIG");
+
+ // this should print
+  NFD_LOG_WARN("warning message XXXhdhd11" << 1 << "x");
+
+  // this too because its level is explicitly set to DEBUG
+  testModuleBPrint();
+
+  BOOST_CHECK_EQUAL(m_buffer.str(),
+                    "WARNING: [TestModuleA] warning message XXXhdhd111x\n"
+                    "DEBUG: [TestModuleB] debug message IGg2474fdksd fo 151617\n"
+                    );
+}
+
+static bool
+checkError(const LoggerFactory::Error& error, const std::string& expected)
+{
+  return error.what() == expected;
+}
+
+BOOST_FIXTURE_TEST_CASE(UnknownLevelString, LoggerFixture)
+{
+  const std::string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  default_level TestMadeUpLevel\n"
+    "}\n";
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  BOOST_REQUIRE_EXCEPTION(config.parse(LOG_CONFIG, false, "LOG_CONFIG"),
+                          LoggerFactory::Error,
+                          bind(&checkError,
+                               _1,
+                               "Unsupported logging level \"TestMadeUpLevel\""));
+}
+
+BOOST_FIXTURE_TEST_CASE(UnknownOption, LoggerFixture)
+{
+  const std::string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  TestMadeUpOption\n"
+    "}\n";
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  BOOST_REQUIRE_EXCEPTION(config.parse(LOG_CONFIG, false, "LOG_CONFIG"),
+                          LoggerFactory::Error,
+                          bind(&checkError,
+                               _1,
+                               "No logging level found for option \"TestMadeUpOption\""));
+}
+
 class InClassLogger : public LoggerFixture
 {
 public:
+
   InClassLogger()
   {
     g_logger.setLogLevel(LOG_ALL);
diff --git a/tests/test-common.hpp b/tests/test-common.hpp
index 8095431..c65d1c2 100644
--- a/tests/test-common.hpp
+++ b/tests/test-common.hpp
@@ -9,6 +9,7 @@
 
 #include <boost/test/unit_test.hpp>
 #include "core/global-io.hpp"
+#include "core/logger.hpp"
 #include <ndn-cpp-dev/security/key-chain.hpp>
 
 namespace nfd {
diff --git a/unit-tests.conf.sample b/unit-tests.conf.sample
new file mode 100644
index 0000000..d75c19c
--- /dev/null
+++ b/unit-tests.conf.sample
@@ -0,0 +1,31 @@
+log
+{
+  ; default_level specifies the logging level for modules
+  ; that are not explicitly named. All debugging levels
+  ; listed above the selected value are enabled.
+  ;
+  ; Valid values:
+  ;
+  ;  NONE ; no messages
+  ;  ERROR ; error messages
+  ;  WARN ; warning messages
+  ;  INFO ; informational messages (default)
+  ;  DEBUG ; debugging messages
+  ;  TRACE ; trace messages (most verbose)
+  ;  ALL ; all messages
+
+  ; default_level INFO
+
+  ; You may override default_level by assigning a logging level
+  ; to the desired module name. Module names can be found in two ways:
+  ;
+  ; Run:
+  ;   nfd --modules
+  ;
+  ; Or look for NFD_LOG_INIT(<module name>) statements in .cpp files
+  ;
+  ; Example module-level settings:
+  ;
+  ; FibManager DEBUG
+  ; Forwarder INFO
+}
\ No newline at end of file