}
std::ostream& operator<<(std::ostream& os, const LogHandlerConfig& config) {
- os << config.type;
+ os << (config.type ? config.type.value() : "[no type]");
bool first = true;
for (const auto& opt : config.options) {
if (!first) {
Pair("", LogCategoryConfig{LogLevel::ERR, true, {}})));
EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
- config = parseLogConfig(" ERR:stderr; stderr=file,stream=stderr ");
+ config = parseLogConfig(" ERR:stderr; stderr=stream:stream=stderr ");
EXPECT_THAT(
config.getCategoryConfigs(),
UnorderedElementsAre(
EXPECT_THAT(
config.getHandlerConfigs(),
UnorderedElementsAre(
- Pair("stderr", LogHandlerConfig{"file", {{"stream", "stderr"}}})));
+ Pair("stderr", LogHandlerConfig{"stream", {{"stream", "stderr"}}})));
config = parseLogConfig(
"ERR:myfile:custom, folly=DBG2, folly.io:=WARN:other;"
- "myfile=file,path=/tmp/x.log; "
- "custom=custom,foo=bar,hello=world,a = b = c; "
+ "myfile=file:path=/tmp/x.log; "
+ "custom=custom:foo=bar,hello=world,a = b = c; "
"other=custom2");
EXPECT_THAT(
config.getCategoryConfigs(),
{{"foo", "bar"}, {"hello", "world"}, {"a", "b = c"}}}),
Pair("other", LogHandlerConfig{"custom2"})));
+ // Test updating existing handler configs, with no handler type
+ config = parseLogConfig("ERR;foo");
+ EXPECT_THAT(
+ config.getCategoryConfigs(),
+ UnorderedElementsAre(Pair("", LogCategoryConfig{LogLevel::ERR, true})));
+ EXPECT_THAT(
+ config.getHandlerConfigs(),
+ UnorderedElementsAre(Pair("foo", LogHandlerConfig{})));
+
+ config = parseLogConfig("ERR;foo:a=b,c=d");
+ EXPECT_THAT(
+ config.getCategoryConfigs(),
+ UnorderedElementsAre(Pair("", LogCategoryConfig{LogLevel::ERR, true})));
+ EXPECT_THAT(
+ config.getHandlerConfigs(),
+ UnorderedElementsAre(Pair(
+ "foo", LogHandlerConfig{folly::none, {{"a", "b"}, {"c", "d"}}})));
+
+ config = parseLogConfig("ERR;test=file:path=/tmp/test.log;foo:a=b,c=d");
+ EXPECT_THAT(
+ config.getCategoryConfigs(),
+ UnorderedElementsAre(Pair("", LogCategoryConfig{LogLevel::ERR, true})));
+ EXPECT_THAT(
+ config.getHandlerConfigs(),
+ UnorderedElementsAre(
+ Pair("foo", LogHandlerConfig{folly::none, {{"a", "b"}, {"c", "d"}}}),
+ Pair("test", LogHandlerConfig{"file", {{"path", "/tmp/test.log"}}})));
+
// Log handler changes with no category changes
- config = parseLogConfig("; myhandler=custom,foo=bar");
+ config = parseLogConfig("; myhandler=custom:foo=bar");
EXPECT_THAT(config.getCategoryConfigs(), UnorderedElementsAre());
EXPECT_THAT(
config.getHandlerConfigs(),
EXPECT_THROW_RE(
parseLogConfig("ERR;"),
LogConfigParseError,
- "error parsing log handler configuration \"\": "
- "expected data in the form NAME=TYPE");
- EXPECT_THROW_RE(
- parseLogConfig("ERR;foo"),
- LogConfigParseError,
- "error parsing log handler configuration \"foo\": "
- "expected data in the form NAME=TYPE");
+ "error parsing log handler configuration: empty log handler name");
EXPECT_THROW_RE(
parseLogConfig("ERR;foo="),
LogConfigParseError,
EXPECT_THROW_RE(
parseLogConfig("ERR;handler1=file;"),
LogConfigParseError,
- "error parsing log handler configuration \"\": "
- "expected data in the form NAME=TYPE");
+ "error parsing log handler configuration: empty log handler name");
+ EXPECT_THROW_RE(
+ parseLogConfig("ERR;test=file,path=/tmp/test.log;foo:a=b,c=d"),
+ LogConfigParseError,
+ "error parsing configuration for log handler \"test\": "
+ "invalid type \"file,path=/tmp/test.log\": type name cannot contain "
+ "a comma when using the basic config format");
+ EXPECT_THROW_RE(
+ parseLogConfig("ERR;test,path=/tmp/test.log;foo:a=b,c=d"),
+ LogConfigParseError,
+ "error parsing configuration for log handler \"test,path\": "
+ "name cannot contain a comma when using the basic config format");
}
TEST(LogConfig, parseJson) {
config = parseLogConfig(
"ERROR:h1,foo.bar:=FATAL,folly=INFO:; "
- "h1=custom,foo=bar");
+ "h1=custom:foo=bar");
expectedJson = folly::parseJson(R"JSON({
"categories" : {
"" : {
EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
config =
- parseLogConfig("WARN:default; default=custom,opt1=value1,opt2=value2");
+ parseLogConfig("WARN:default; default=custom:opt1=value1,opt2=value2");
config.update(parseLogConfig("folly.io=DBG2,foo=INFO"));
EXPECT_THAT(
config.getCategoryConfigs(),
// 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");
+ parseLogConfig("WARN:default; default=custom:opt1=value1,opt2=value2");
config.update(parseLogConfig("ERR"));
EXPECT_THAT(
config.getCategoryConfigs(),
"custom", {{"opt1", "value1"}, {"opt2", "value2"}}))));
config =
- parseLogConfig("WARN:default; default=custom,opt1=value1,opt2=value2");
+ parseLogConfig("WARN:default; default=custom:opt1=value1,opt2=value2");
config.update(parseLogConfig(".:=ERR"));
EXPECT_THAT(
config.getCategoryConfigs(),
// Test clearing the root category's log handlers
config =
- parseLogConfig("WARN:default; default=custom,opt1=value1,opt2=value2");
+ parseLogConfig("WARN:default; default=custom:opt1=value1,opt2=value2");
config.update(parseLogConfig("FATAL:"));
EXPECT_THAT(
config.getCategoryConfigs(),
"default",
LogHandlerConfig(
"custom", {{"opt1", "value1"}, {"opt2", "value2"}}))));
+
+ // Test updating the settings on a log handler
+ config =
+ parseLogConfig("WARN:default; default=stream:stream=stderr,async=false");
+ config.update(parseLogConfig("INFO; default:async=true"));
+ EXPECT_THAT(
+ config.getCategoryConfigs(),
+ UnorderedElementsAre(
+ Pair("", LogCategoryConfig{LogLevel::INFO, true, {"default"}})));
+ EXPECT_THAT(
+ config.getHandlerConfigs(),
+ UnorderedElementsAre(Pair(
+ "default",
+ LogHandlerConfig(
+ "stream", {{"stream", "stderr"}, {"async", "true"}}))));
+
+ // Updating the settings for a non-existent log handler should fail
+ config =
+ parseLogConfig("WARN:default; default=stream:stream=stderr,async=false");
+ EXPECT_THROW_RE(
+ config.update(parseLogConfig("INFO; other:async=true")),
+ std::invalid_argument,
+ "cannot update configuration for "
+ "unknown log handler \"other\"");
}