#include <folly/dynamic.h>
#include <folly/experimental/logging/LogName.h>
#include <folly/json.h>
-#include <cassert>
+#include <folly/lang/SafeAssert.h>
using std::shared_ptr;
using std::string;
// Split the configString into level and handler information.
std::vector<StringPiece> handlerPieces;
folly::split(":", configString, handlerPieces);
- // folly::split() always returns a list of length 1
- assert(handlerPieces.size() >= 1);
+ FOLLY_SAFE_DCHECK(
+ handlerPieces.size() >= 1,
+ "folly::split() always returns a list of length 1");
auto levelString = trimWhitespace(handlerPieces[0]);
bool hasHandlerConfig = handlerPieces.size() > 1;
auto emplaceResult =
categoryConfigs.emplace(canonicalName, std::move(categoryConfig));
- assert(emplaceResult.second);
+ FOLLY_SAFE_DCHECK(
+ emplaceResult.second,
+ "category name must be new since it was not in seenCategories");
}
return categoryConfigs;
}
std::pair<std::string, LogHandlerConfig> parseHandlerConfig(StringPiece value) {
- std::vector<StringPiece> pieces;
- folly::split(",", value, pieces);
- // "folly::split() always returns a list of length 1";
- assert(pieces.size() >= 1);
+ // Parse the handler name and optional type
+ auto colonIndex = value.find(':');
+ StringPiece namePortion;
+ StringPiece optionsStr;
+ if (colonIndex == StringPiece::npos) {
+ namePortion = value;
+ } else {
+ namePortion = StringPiece{value.begin(), value.begin() + colonIndex};
+ optionsStr = StringPiece{value.begin() + colonIndex + 1, value.end()};
+ }
StringPiece handlerName;
- StringPiece handlerType;
- if (!splitNameValue(pieces[0], &handlerName, &handlerType)) {
- throw LogConfigParseError{to<string>(
- "error parsing log handler configuration \"",
- pieces[0],
- "\": expected data in the form NAME=TYPE")};
+ Optional<StringPiece> handlerType(in_place);
+ if (!splitNameValue(namePortion, &handlerName, &handlerType.value())) {
+ handlerName = trimWhitespace(namePortion);
+ handlerType = folly::none;
}
+
+ // Make sure the handler name and type are not empty.
+ // Also disallow commas in the name: this helps catch accidental errors where
+ // the user left out the ':' and intended to be specifying options instead of
+ // part of the name or type.
if (handlerName.empty()) {
throw LogConfigParseError{
"error parsing log handler configuration: empty log handler name"};
}
- if (handlerType.empty()) {
+ if (handlerName.contains(',')) {
throw LogConfigParseError{to<string>(
"error parsing configuration for log handler \"",
handlerName,
- "\": empty log handler type")};
+ "\": name cannot contain a comma when using the basic config format")};
}
-
- LogHandlerConfig config{handlerType};
- for (size_t n = 1; n < pieces.size(); ++n) {
- StringPiece optionName;
- StringPiece optionValue;
- if (!splitNameValue(pieces[n], &optionName, &optionValue)) {
+ if (handlerType.hasValue()) {
+ if (handlerType->empty()) {
throw LogConfigParseError{to<string>(
"error parsing configuration for log handler \"",
handlerName,
- "\": options must be of the form NAME=VALUE")};
+ "\": empty log handler type")};
}
-
- auto ret = config.options.emplace(optionName.str(), optionValue.str());
- if (!ret.second) {
+ if (handlerType->contains(',')) {
throw LogConfigParseError{to<string>(
"error parsing configuration for log handler \"",
handlerName,
- "\": duplicate configuration for option \"",
- optionName,
- "\"")};
+ "\": invalid type \"",
+ handlerType.value(),
+ "\": type name cannot contain a comma when using "
+ "the basic config format")};
+ }
+ }
+
+ // Parse the options
+ LogHandlerConfig config{handlerType};
+ optionsStr = trimWhitespace(optionsStr);
+ if (!optionsStr.empty()) {
+ std::vector<StringPiece> pieces;
+ folly::split(",", optionsStr, pieces);
+ FOLLY_SAFE_DCHECK(
+ pieces.size() >= 1, "folly::split() always returns a list of length 1");
+
+ for (const auto& piece : pieces) {
+ StringPiece optionName;
+ StringPiece optionValue;
+ if (!splitNameValue(piece, &optionName, &optionValue)) {
+ throw LogConfigParseError{to<string>(
+ "error parsing configuration for log handler \"",
+ handlerName,
+ "\": options must be of the form NAME=VALUE")};
+ }
+
+ auto ret = config.options.emplace(optionName.str(), optionValue.str());
+ if (!ret.second) {
+ throw LogConfigParseError{to<string>(
+ "error parsing configuration for log handler \"",
+ handlerName,
+ "\": duplicate configuration for option \"",
+ optionName,
+ "\"")};
+ }
}
}
// From then on each section specifies a single LogHandler config.
std::vector<StringPiece> pieces;
folly::split(";", value, pieces);
- // "folly::split() always returns a list of length 1";
- assert(pieces.size() >= 1);
+ FOLLY_SAFE_DCHECK(
+ pieces.size() >= 1, "folly::split() always returns a list of length 1");
auto categoryConfigs = parseCategoryConfigs(pieces[0]);
LogConfig::HandlerConfigMap handlerConfigs;
for (const auto& opt : config.options) {
options.insert(opt.first, opt.second);
}
- return dynamic::object("type", config.type)("options", options);
+ auto result = dynamic::object("options", options);
+ if (config.type.hasValue()) {
+ result("type", config.type.value());
+ }
+ return std::move(result);
}
dynamic logConfigToDynamic(const LogCategoryConfig& config) {
}
value("handlers", std::move(handlers));
}
- return value;
+ return std::move(value);
}
} // namespace folly