return !(*this == other);
}
+void LogConfig::update(const LogConfig& other) {
+ // Update handlerConfigs_ with all of the entries from the other LogConfig.
+ // Any entries already present in our handlerConfigs_ are replaced wholesale.
+ for (const auto& entry : other.handlerConfigs_) {
+ auto result = handlerConfigs_.insert(entry);
+ if (!result.second) {
+ result.first->second = entry.second;
+ }
+ }
+
+ // Update categoryConfigs_ with all of the entries from the other LogConfig.
+ //
+ // Any entries already present in our categoryConfigs_ are merged: if the new
+ // configuration does not include handler settings our entry's settings are
+ // maintained.
+ for (const auto& entry : other.categoryConfigs_) {
+ auto result = categoryConfigs_.insert(entry);
+ if (!result.second) {
+ auto* existingEntry = &result.first->second;
+ auto oldHandlers = std::move(existingEntry->handlers);
+ *existingEntry = entry.second;
+ if (!existingEntry->handlers.hasValue()) {
+ existingEntry->handlers = std::move(oldHandlers);
+ }
+ }
+ }
+}
+
} // namespace folly
bool operator==(const LogConfig& other) const;
bool operator!=(const LogConfig& other) const;
+ /**
+ * Update this LogConfig object by merging in settings from another
+ * LogConfig.
+ *
+ * All LogHandler settings from the other LogConfig will be inserted into
+ * this LogConfig. If a log handler with the same name was already defined
+ * in this LogConfig it will be replaced with the new settings.
+ *
+ * All LogCategory settings from the other LogConfig will be inserted into
+ * this LogConfig. If a log category with the same name was already defined
+ * in this LogConfig, its settings will be updated with settings from the
+ * other LogConfig. However, if the other LogConfig does not define handler
+ * settings for the category it will retain its current handler settings.
+ *
+ * This method allows LogConfig objects to be combined before applying them.
+ * Using LogConfig::update() will produce the same results as if
+ * LoggerDB::updateConfig() had been called with both configs sequentially.
+ * In other words, this operation:
+ *
+ * configA.update(configB);
+ * loggerDB.updateConfig(configA);
+ *
+ * will produce the same results as:
+ *
+ * loggerDB.updateConfig(configA);
+ * loggerDB.updateConfig(configA);
+ */
+ void update(const LogConfig& other);
+
private:
HandlerConfigMap handlerConfigs_;
CategoryConfigMap categoryConfigs_;
})JSON");
EXPECT_EQ(expectedJson, logConfigToDynamic(config));
}
+
+TEST(LogConfig, mergeConfigs) {
+ auto config = parseLogConfig("bar=ERR:");
+ config.update(parseLogConfig("foo:=INFO"));
+ EXPECT_THAT(
+ config.getCategoryConfigs(),
+ UnorderedElementsAre(
+ Pair("foo", LogCategoryConfig{LogLevel::INFO, false}),
+ Pair("bar", LogCategoryConfig{LogLevel::ERR, true, {}})));
+ EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
+
+ config =
+ parseLogConfig("WARN:default; default=custom,opt1=value1,opt2=value2");
+ config.update(parseLogConfig("folly.io=DBG2,foo=INFO"));
+ EXPECT_THAT(
+ config.getCategoryConfigs(),
+ UnorderedElementsAre(
+ Pair("", LogCategoryConfig{LogLevel::WARN, true, {"default"}}),
+ Pair("foo", LogCategoryConfig{LogLevel::INFO, true}),
+ Pair("folly.io", LogCategoryConfig{LogLevel::DBG2, true})));
+ EXPECT_THAT(
+ config.getHandlerConfigs(),
+ UnorderedElementsAre(Pair(
+ "default",
+ LogHandlerConfig(
+ "custom", {{"opt1", "value1"}, {"opt2", "value2"}}))));
+
+ // Updating the root category's log level without specifying
+ // handlers should leave its current handler list intact
+ config =
+ parseLogConfig("WARN:default; default=custom,opt1=value1,opt2=value2");
+ config.update(parseLogConfig("ERR"));
+ EXPECT_THAT(
+ config.getCategoryConfigs(),
+ UnorderedElementsAre(
+ Pair("", LogCategoryConfig{LogLevel::ERR, true, {"default"}})));
+ EXPECT_THAT(
+ config.getHandlerConfigs(),
+ UnorderedElementsAre(Pair(
+ "default",
+ LogHandlerConfig(
+ "custom", {{"opt1", "value1"}, {"opt2", "value2"}}))));
+
+ config =
+ parseLogConfig("WARN:default; default=custom,opt1=value1,opt2=value2");
+ config.update(parseLogConfig(".:=ERR"));
+ EXPECT_THAT(
+ config.getCategoryConfigs(),
+ UnorderedElementsAre(
+ Pair("", LogCategoryConfig{LogLevel::ERR, false, {"default"}})));
+ EXPECT_THAT(
+ config.getHandlerConfigs(),
+ UnorderedElementsAre(Pair(
+ "default",
+ LogHandlerConfig(
+ "custom", {{"opt1", "value1"}, {"opt2", "value2"}}))));
+
+ // Test clearing the root category's log handlers
+ config =
+ parseLogConfig("WARN:default; default=custom,opt1=value1,opt2=value2");
+ config.update(parseLogConfig("FATAL:"));
+ EXPECT_THAT(
+ config.getCategoryConfigs(),
+ UnorderedElementsAre(
+ Pair("", LogCategoryConfig{LogLevel::FATAL, true, {}})));
+ EXPECT_THAT(
+ config.getHandlerConfigs(),
+ UnorderedElementsAre(Pair(
+ "default",
+ LogHandlerConfig(
+ "custom", {{"opt1", "value1"}, {"opt2", "value2"}}))));
+}