jni: Android-specific custom logger for NFD

Change-Id: I4d98a58b8320735e40b5da3d358b9c71b558d995
Refs: #2508
diff --git a/app/src/main/jni/nfd-android/config.hpp b/app/src/main/jni/nfd-android/config.hpp
index 78b3f8b..f26c0f6 100644
--- a/app/src/main/jni/nfd-android/config.hpp
+++ b/app/src/main/jni/nfd-android/config.hpp
@@ -8,5 +8,6 @@
 #define HAVE_LIBRESOLV
 /*#undef HAVE_UNIX_SOCKETS*/
 #define DEFAULT_CONFIG_FILE "./nfd.conf"
+#define HAVE_CUSTOM_LOGGER 1
 
 #endif /* W_CONFIG_HPP_WAF */
diff --git a/app/src/main/jni/nfd-android/custom-logger-factory.cpp b/app/src/main/jni/nfd-android/custom-logger-factory.cpp
new file mode 100644
index 0000000..f84a663
--- /dev/null
+++ b/app/src/main/jni/nfd-android/custom-logger-factory.cpp
@@ -0,0 +1,213 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "custom-logger-factory.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("LoggerFactory");
+
+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 ((boost::lexical_cast<uint32_t>(LOG_NONE) <= levelNo &&
+           levelNo <= boost::lexical_cast<uint32_t>(LOG_TRACE)) ||
+          levelNo == LOG_ALL)
+        {
+          return static_cast<LogLevel>(levelNo);
+        }
+    }
+  catch (const boost::bad_lexical_cast& error)
+    {
+    }
+  throw LoggerFactory::Error("Unsupported logging level \"" +
+                             level + "\"");
+}
+
+LogLevel
+LoggerFactory::extractLevel(const ConfigSection& item, const std::string& key)
+{
+  std::string levelString;
+  try
+    {
+      levelString = item.get_value<std::string>();
+    }
+  catch (const boost::property_tree::ptree_error& error)
+    {
+    }
+
+  if (levelString.empty())
+    {
+      throw LoggerFactory::Error("No logging level found for option \"" + key + "\"");
+    }
+
+  return parseLevel(levelString);
+}
+
+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
+// }
+
+  if (!isDryRun)
+    {
+      ConfigSection::const_assoc_iterator item = section.find("default_level");
+      if (item != section.not_found())
+        {
+          LogLevel level = extractLevel(item->second, "default_level");
+          setDefaultLevel(level);
+        }
+      else
+        {
+          setDefaultLevel(LOG_INFO);
+        }
+    }
+
+  for (ConfigSection::const_iterator item = section.begin();
+       item != section.end();
+       ++item)
+    {
+      LogLevel level = extractLevel(item->second, item->first);
+
+      if (item->first == "default_level")
+        {
+          // do nothing
+        }
+      else
+        {
+          LoggerMap::iterator loggerIt = m_loggers.find(item->first);
+          if (loggerIt == m_loggers.end())
+            {
+              NFD_LOG_DEBUG("Failed to configure logging level for module \"" <<
+                            item->first << "\" (module not found)");
+            }
+          else 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/app/src/main/jni/nfd-android/custom-logger-factory.hpp b/app/src/main/jni/nfd-android/custom-logger-factory.hpp
new file mode 100644
index 0000000..394c90b
--- /dev/null
+++ b/app/src/main/jni/nfd-android/custom-logger-factory.hpp
@@ -0,0 +1,111 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#ifndef NFD_ANDROID_CUSTOM_LOGGER_FACTORY_HPP
+#define NFD_ANDROID_CUSTOM_LOGGER_FACTORY_HPP
+
+#include "common.hpp"
+
+#include "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);
+
+  LogLevel
+  extractLevel(const ConfigSection& item, const std::string& key);
+
+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_ANDROID_CUSTOM_LOGGER_FACTORY_HPP
diff --git a/app/src/main/jni/nfd-android/custom-logger.cpp b/app/src/main/jni/nfd-android/custom-logger.cpp
new file mode 100644
index 0000000..6b5d3ae
--- /dev/null
+++ b/app/src/main/jni/nfd-android/custom-logger.cpp
@@ -0,0 +1,36 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "custom-logger.hpp"
+
+namespace nfd {
+
+Logger::Logger(const std::string& name, LogLevel level)
+  : m_moduleName(name)
+  , m_enabledLogLevel(level)
+{
+}
+
+} // namespace nfd
diff --git a/app/src/main/jni/nfd-android/custom-logger.hpp b/app/src/main/jni/nfd-android/custom-logger.hpp
new file mode 100644
index 0000000..5355657
--- /dev/null
+++ b/app/src/main/jni/nfd-android/custom-logger.hpp
@@ -0,0 +1,140 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  Regents of the University of California,
+ *                      Arizona Board of Regents,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University,
+ *                      Washington University in St. Louis,
+ *                      Beijing Institute of Technology,
+ *                      The University of Memphis
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_ANDROID_CUSTOM_LOGGER_HPP
+#define NFD_ANDROID_CUSTOM_LOGGER_HPP
+
+#include "common.hpp"
+#include <android/log.h>
+
+namespace nfd {
+
+/** \brief indicates a log level
+ *  \note This type is internal. Logger should be accessed through NFD_LOG_* macros.
+ */
+enum LogLevel {
+  LOG_FATAL          = -1, // fatal (will be logged unconditionally)
+  LOG_NONE           = 0, // no messages
+  LOG_ERROR          = 1, // serious error messages
+  LOG_WARN           = 2, // warning messages
+  LOG_INFO           = 3, // informational messages
+  LOG_DEBUG          = 4, // debug messages
+  LOG_TRACE          = 5, // trace messages (most verbose)
+  LOG_ALL            = 255 // all messages
+};
+
+/** \brief provides logging for a module
+ *  \note This type is internal. Logger should be accessed through NFD_LOG_* macros.
+ *  \note This type is copyable because logger can be declared as a field of
+ *        (usually template) classes, and shouldn't prevent those classes to be copyable.
+ */
+class Logger
+{
+public:
+  Logger(const std::string& name, LogLevel level);
+
+  bool
+  isEnabled(LogLevel level) const
+  {
+    return m_enabledLogLevel >= level;
+  }
+
+  void
+  setLogLevel(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;
+  LogLevel    m_enabledLogLevel;
+};
+
+inline std::ostream&
+operator<<(std::ostream& output, const Logger& logger)
+{
+  output << logger.getName();
+  return output;
+}
+
+} // namespace nfd
+
+#include "core/logger-factory.hpp"
+
+namespace nfd {
+
+#define NFD_LOG_INIT(name) \
+static nfd::Logger& g_logger = nfd::LoggerFactory::create(name)
+
+#define NFD_LOG_INCLASS_DECLARE() \
+static nfd::Logger& g_logger
+
+#define NFD_LOG_INCLASS_DEFINE(cls, 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::LoggerFactory::create(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::LoggerFactory::create(name)
+
+#define NFD_LOG(nfdLevel, androidLevel, msg, expression)                \
+do {                                                                    \
+  if (g_logger.isEnabled(::nfd::LOG_##nfdLevel)) {                      \
+    std::ostringstream os;                                              \
+    os << expression;                                                   \
+    __android_log_print(ANDROID_LOG_##androidLevel,                     \
+                        g_logger.getName().c_str(), "%s", os.str().c_str()); \
+  }                                                                     \
+} while (false)
+
+#define NFD_LOG_TRACE(expression) NFD_LOG(TRACE, VERBOSE, TRACE, expression)
+#define NFD_LOG_DEBUG(expression) NFD_LOG(DEBUG, DEBUG, DEBUG,   expression)
+#define NFD_LOG_INFO(expression)  NFD_LOG(INFO,  INFO,  INFO,    expression)
+#define NFD_LOG_WARN(expression)  NFD_LOG(WARN,  WARN,  WARNING, expression)
+#define NFD_LOG_ERROR(expression) NFD_LOG(ERROR, ERROR, ERROR,   expression)
+#define NFD_LOG_FATAL(expression) NFD_LOG(FATAL, FATAL, FATAL,   expression)
+
+} // namespace nfd
+
+#endif // NFD_ANDROID_CUSTOM_LOGGER_HPP
diff --git a/app/src/main/jni/nfd-android/version.hpp b/app/src/main/jni/nfd-android/version.hpp
index d9aa54a..afa7ae1 100644
--- a/app/src/main/jni/nfd-android/version.hpp
+++ b/app/src/main/jni/nfd-android/version.hpp
@@ -38,13 +38,13 @@
  *
  *  MAJOR*1000000 + MINOR*1000 + PATCH
  */
-#define NFD_VERSION 2000
+#define NFD_VERSION 3000
 
 /** \brief NFD version represented as a string
  *
  *  MAJOR.MINOR.PATCH
  */
-#define NFD_VERSION_STRING "0.2.0"
+#define NFD_VERSION_STRING "0.3.0"
 
 /** \brief NFD version string, including git commit information, if NFD is build from
  *         specific git commit
@@ -60,12 +60,12 @@
  *
  * Example, 0.1.0-rc1-1-g5c86570
  */
-#define NFD_VERSION_BUILD_STRING "0.2.0-134-gde88b46"
+#define NFD_VERSION_BUILD_STRING "0.3.0-8-g6f570de"
 
 /// MAJOR version
 #define NFD_VERSION_MAJOR 0
 /// MINOR version
-#define NFD_VERSION_MINOR 2
+#define NFD_VERSION_MINOR 3
 /// PATCH version
 #define NFD_VERSION_PATCH 0