logging: add a LogHandler registry to LoggerDB
authorAdam Simpkins <simpkins@fb.com>
Thu, 30 Nov 2017 01:35:08 +0000 (17:35 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Thu, 30 Nov 2017 01:51:07 +0000 (17:51 -0800)
Summary:
Update the LoggerDB to track a list of LogHandlers by name, and
LogHandlerFactories by handler type.

This will be needed to support updating the LoggerDB configuration from a
LogConfig object.

Reviewed By: bolinfest

Differential Revision: D6200562

fbshipit-source-id: e365b4e0df65aa5aaa34e118eb3cee9c9c45cb05

folly/experimental/logging/LogCategory.h
folly/experimental/logging/LoggerDB.cpp
folly/experimental/logging/LoggerDB.h

index 91e72c91c69b164a0073b1b4254d90cba6f06b23..4779a4728850b1ca383c0ff279bcdc4837b5f242 100644 (file)
@@ -169,8 +169,9 @@ class LogCategory {
   void admitMessage(const LogMessage& message) const;
 
   /**
-   * Note: setLevelLocked() may only be called while holding the main
-   * LoggerDB lock.
+   * Note: setLevelLocked() may only be called while holding the
+   * LoggerDB loggersByName_ lock.  It is safe to call this while holding the
+   * loggersByName_ lock in read-mode; holding it exclusively is not required.
    *
    * This method should only be invoked by LoggerDB.
    */
@@ -240,9 +241,9 @@ class LogCategory {
 
   /**
    * Pointers to children and sibling loggers.
-   * These pointers should only ever be accessed while holding the main
-   * LoggerDB lock.  (These are only modified when creating new loggers,
-   * which occurs with the main LoggerDB lock held.)
+   * These pointers should only ever be accessed while holding the
+   * LoggerDB::loggersByName_ lock.  (These are only modified when creating new
+   * loggers, which occurs with the main LoggerDB lock held.)
    */
   LogCategory* firstChild_{nullptr};
   LogCategory* nextSibling_{nullptr};
index ee09681a981ac344ef3b1e70d7f3a20fa11e5032..fdfcdfa51b218d93225abbdb34cb23989a8ced2c 100644 (file)
@@ -22,6 +22,7 @@
 #include <folly/String.h>
 #include <folly/experimental/logging/LogCategory.h>
 #include <folly/experimental/logging/LogHandler.h>
+#include <folly/experimental/logging/LogHandlerFactory.h>
 #include <folly/experimental/logging/LogLevel.h>
 #include <folly/experimental/logging/Logger.h>
 #include <folly/experimental/logging/RateLimiter.h>
@@ -76,6 +77,8 @@ LoggerDB::LoggerDB() {
 
 LoggerDB::LoggerDB(TestConstructorArg) : LoggerDB() {}
 
+LoggerDB::~LoggerDB() {}
+
 LogCategory* LoggerDB::getCategory(StringPiece name) {
   return getOrCreateCategoryLocked(*loggersByName_.wlock(), name);
 }
@@ -173,6 +176,18 @@ void LoggerDB::cleanupHandlers() {
     }
   }
 
+  // Also extract our HandlerFactoryMap and HandlerMap, so we can clear them
+  // later without holding the handlerInfo_ lock.
+  HandlerFactoryMap factories;
+  HandlerMap handlers;
+  {
+    auto handlerInfo = handlerInfo_.wlock();
+    factories.swap(handlerInfo->factories);
+    handlers.swap(handlerInfo->handlers);
+  }
+
+  // Remove all of the LogHandlers from all log categories,
+  // to drop any shared_ptr references to the LogHandlers
   for (auto* category : categories) {
     category->clearHandlers();
   }
@@ -199,6 +214,31 @@ size_t LoggerDB::flushAllHandlers() {
   return handlers.size();
 }
 
+void LoggerDB::registerHandlerFactory(
+    std::unique_ptr<LogHandlerFactory> factory,
+    bool replaceExisting) {
+  auto type = factory->getType();
+  auto handlerInfo = handlerInfo_.wlock();
+  if (replaceExisting) {
+    handlerInfo->factories[type.str()] = std::move(factory);
+  } else {
+    auto ret = handlerInfo->factories.emplace(type.str(), std::move(factory));
+    if (!ret.second) {
+      throw std::range_error(to<std::string>(
+          "a LogHandlerFactory for the type \"", type, "\" already exists"));
+    }
+  }
+}
+
+void LoggerDB::unregisterHandlerFactory(StringPiece type) {
+  auto handlerInfo = handlerInfo_.wlock();
+  auto numRemoved = handlerInfo->factories.erase(type.str());
+  if (numRemoved != 1) {
+    throw std::range_error(
+        to<std::string>("no LogHandlerFactory for type \"", type, "\" found"));
+  }
+}
+
 LogLevel LoggerDB::xlogInit(
     StringPiece categoryName,
     std::atomic<LogLevel>* xlogCategoryLevel,
index 1c80cd965bf2ae171d1017fe9579ac309ee07add..c2c1d423f226016a6cf2b5a95c6fa6ca746fbb6e 100644 (file)
@@ -29,6 +29,8 @@
 namespace folly {
 
 class LogCategory;
+class LogHandler;
+class LogHandlerFactory;
 enum class LogLevel : uint32_t;
 
 /**
@@ -41,6 +43,8 @@ class LoggerDB {
    */
   static LoggerDB* get();
 
+  ~LoggerDB();
+
   /**
    * Get the LogCategory for the specified name.
    *
@@ -99,6 +103,35 @@ class LoggerDB {
    */
   size_t flushAllHandlers();
 
+  /**
+   * Register a LogHandlerFactory.
+   *
+   * The LogHandlerFactory will be used to create LogHandler objects from a
+   * LogConfig object during updateConfig() and resetConfig() calls.
+   *
+   * Only one factory can be registered for a given handler type name.
+   * LogHandlerFactory::getType() returns the handler type supported by this
+   * LogHandlerFactory.
+   *
+   * If an existing LogHandlerFactory is already registered with this type name
+   * and replaceExisting is false a std::range_error will be thrown.
+   * Otherwise, if replaceExisting is true, the new factory will replace the
+   * existing factory.
+   */
+  void registerHandlerFactory(
+      std::unique_ptr<LogHandlerFactory> factory,
+      bool replaceExisting = false);
+
+  /**
+   * Remove a registered LogHandlerFactory.
+   *
+   * The type parameter should be the name of the handler type, as returned by
+   * LogHandlerFactory::getType().
+   *
+   * Throws std::range_error if no handler factory with this type name exists.
+   */
+  void unregisterHandlerFactory(folly::StringPiece type);
+
   /**
    * Initialize the LogCategory* and std::atomic<LogLevel> used by an XLOG()
    * statement.
@@ -171,6 +204,14 @@ class LoggerDB {
       LogName::Hash,
       LogName::Equals>;
 
+  using HandlerFactoryMap =
+      std::unordered_map<std::string, std::unique_ptr<LogHandlerFactory>>;
+  using HandlerMap = std::unordered_map<std::string, std::weak_ptr<LogHandler>>;
+  struct HandlerInfo {
+    HandlerFactoryMap factories;
+    HandlerMap handlers;
+  };
+
   // Forbidden copy constructor and assignment operator
   LoggerDB(LoggerDB const&) = delete;
   LoggerDB& operator=(LoggerDB const&) = delete;
@@ -201,6 +242,11 @@ class LoggerDB {
    */
   folly::Synchronized<LoggerNameMap> loggersByName_;
 
+  /**
+   * The LogHandlers and LogHandlerFactories.
+   */
+  folly::Synchronized<HandlerInfo> handlerInfo_;
+
   static std::atomic<InternalWarningHandler> warningHandler_;
 };
 } // namespace folly