Commit 153c4233 authored by Adam Simpkins's avatar Adam Simpkins Committed by Facebook Github Bot

logging: add a LogConfig::update() method

Summary:
Add a method for merging the settings from two LogConfig objects.
This allows LogConfig objects to be merged before applying them to the
LoggerDB.  The effects are the same as two sequential LoggerDB::updateConfig()
calls, but without having to apply the intermediate state to the LoggerDB.

Reviewed By: bolinfest

Differential Revision: D6342085

fbshipit-source-id: 0f8a1b7d8d195a80bc74342444dd3152d331fcb6
parent b26334e5
......@@ -26,4 +26,32 @@ bool LogConfig::operator!=(const LogConfig& other) const {
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
......@@ -52,6 +52,35 @@ class LogConfig {
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_;
......
......@@ -572,3 +572,75 @@ TEST(LogConfig, toJson) {
})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"}}))));
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment