logging: add a LoggerDB::getConfig() method
authorAdam Simpkins <simpkins@fb.com>
Thu, 30 Nov 2017 01:35:13 +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: Add a method to get the current LogConfig state from the LoggerDB.

Reviewed By: bolinfest

Differential Revision: D6200596

fbshipit-source-id: 3bc57d498a5d25d19099d861376d71ea9f7e4039

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

index 4779a4728850b1ca383c0ff279bcdc4837b5f242..565373a7d6f2cc716f3df099ca32ad6233e95a74 100644 (file)
@@ -75,6 +75,15 @@ class LogCategory {
         level_.load(std::memory_order_acquire) & ~FLAG_INHERIT);
   }
 
+  /**
+   * Get the log level and inheritance flag.
+   */
+  std::pair<LogLevel, bool> getLevelInfo() const {
+    auto value = level_.load(std::memory_order_acquire);
+    return {static_cast<LogLevel>(value & ~FLAG_INHERIT),
+            bool(value & FLAG_INHERIT)};
+  }
+
   /**
    * Get the effective level for this log category.
    *
index fdfcdfa51b218d93225abbdb34cb23989a8ced2c..c7a3270502e44194eb0fa3aee757f348b156fbc6 100644 (file)
 #include <folly/FileUtil.h>
 #include <folly/String.h>
 #include <folly/experimental/logging/LogCategory.h>
+#include <folly/experimental/logging/LogConfig.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>
 
+using std::string;
+
 namespace folly {
 
 namespace {
@@ -138,6 +141,73 @@ std::vector<std::string> LoggerDB::processConfigString(
   return errors;
 }
 
+LogConfig LoggerDB::getConfig() const {
+  auto handlerInfo = handlerInfo_.rlock();
+
+  LogConfig::HandlerConfigMap handlerConfigs;
+  std::unordered_map<std::shared_ptr<LogHandler>, string> handlersToName;
+  for (const auto& entry : handlerInfo->handlers) {
+    auto handler = entry.second.lock();
+    if (!handler) {
+      continue;
+    }
+    handlersToName.emplace(handler, entry.first);
+    handlerConfigs.emplace(entry.first, handler->getConfig());
+  }
+
+  size_t anonymousNameIndex = 1;
+  auto generateAnonymousHandlerName = [&]() {
+    // Return a unique name of the form "anonymousHandlerN"
+    // Keep incrementing N until we find a name that isn't currently taken.
+    while (true) {
+      auto name = to<string>("anonymousHandler", anonymousNameIndex);
+      ++anonymousNameIndex;
+      if (handlerInfo->handlers.find(name) == handlerInfo->handlers.end()) {
+        return name;
+      }
+    }
+  };
+
+  LogConfig::CategoryConfigMap categoryConfigs;
+  {
+    auto loggersByName = loggersByName_.rlock();
+    for (const auto& entry : *loggersByName) {
+      auto* category = entry.second.get();
+      auto levelInfo = category->getLevelInfo();
+      auto handlers = category->getHandlers();
+
+      // Don't report categories that have default settings.
+      if (handlers.empty() && levelInfo.first == LogLevel::MAX_LEVEL &&
+          levelInfo.second) {
+        continue;
+      }
+
+      // Translate the handler pointers to names
+      std::vector<string> handlerNames;
+      for (const auto& handler : handlers) {
+        auto iter = handlersToName.find(handler);
+        if (iter == handlersToName.end()) {
+          // This LogHandler must have been manually attached to the category,
+          // rather than defined with `updateConfig()` or `resetConfig()`.
+          // Generate a unique name to use for reporting it in the config.
+          auto name = generateAnonymousHandlerName();
+          handlersToName.emplace(handler, name);
+          handlerConfigs.emplace(name, handler->getConfig());
+          handlerNames.emplace_back(name);
+        } else {
+          handlerNames.emplace_back(iter->second);
+        }
+      }
+
+      LogCategoryConfig categoryConfig(
+          levelInfo.first, levelInfo.second, handlerNames);
+      categoryConfigs.emplace(category->getName(), std::move(categoryConfig));
+    }
+  }
+
+  return LogConfig{std::move(handlerConfigs), std::move(categoryConfigs)};
+}
+
 LogCategory* LoggerDB::getOrCreateCategoryLocked(
     LoggerNameMap& loggersByName,
     StringPiece name) {
index c2c1d423f226016a6cf2b5a95c6fa6ca746fbb6e..d65324f7ce004bc5154f89fc169c2078e9dee92c 100644 (file)
@@ -29,6 +29,7 @@
 namespace folly {
 
 class LogCategory;
+class LogConfig;
 class LogHandler;
 class LogHandlerFactory;
 enum class LogLevel : uint32_t;
@@ -76,6 +77,17 @@ class LoggerDB {
   void setLevel(folly::StringPiece name, LogLevel level, bool inherit = true);
   void setLevel(LogCategory* category, LogLevel level, bool inherit = true);
 
+  /**
+   * Get a LogConfig object describing the current state of the LoggerDB.
+   *
+   * Note that this may not 100% accurately describe the current configuration
+   * if callers have manually added LogHandlers to some categories without
+   * using the updateConfig() or resetConfig() functions.  In this case
+   * getConfig() will simply report these handlers as "unknown_handler" when
+   * returning handler names for the categories in question.
+   */
+  LogConfig getConfig() const;
+
   /**
    * Apply a configuration string specifying a series a log levels.
    *