in folly/logging/CustomLogFormatter.cpp [110:230]
void CustomLogFormatter::parseFormatString(StringPiece input) {
std::size_t estimatedWidth = 0;
functionNameCount_ = 0;
fileNameCount_ = 0;
// Replace all format keys to numbers to improve performance and to use
// varying value types (which is not possible using folly::vformat()).
std::string output;
output.reserve(input.size());
const char* varNameStart = nullptr;
enum StateEnum {
LITERAL,
FMT_NAME,
FMT_MODIFIERS,
} state = LITERAL;
for (const char* p = input.begin(); p < input.end(); ++p) {
switch (state) {
case LITERAL:
output.append(p, 1);
// In case of `{{` or `}}`, copy it as it is and only increment the
// estimatedWidth once as it will result to a single character in
// output.
if ((p + 1) != input.end() /* ensure not last character */ &&
(0 == memcmp(p, "}}", 2) || 0 == memcmp(p, "{{", 2))) {
output.append(p + 1, 1);
estimatedWidth++;
p++;
}
// If we see a single open curly brace, it denotes a start of a format
// name and so we change the state to FMT_NAME and do not increment
// estimatedWidth as it won't be in the output.
else if (*p == '{') {
varNameStart = p + 1;
state = FMT_NAME;
}
// In case it is just a regular literal, just increment estimatedWidth
// by one and move on to the next character.
else {
estimatedWidth++;
}
break;
// In case we have started processing a format name/key
case FMT_NAME:
// Unless it is the end of the format name/key, do nothing and scan over
// the name/key. When it is the end of the format name/key, look up
// the argIndex for it and replace the name/key with that index.
if (*p == ':' || *p == '}') {
StringPiece varName(varNameStart, p);
auto item = std::lower_bound(
formatKeys.begin(),
formatKeys.end(),
varName,
[](const auto& a, const auto& b) { return a.key < b; });
if (UNLIKELY(item == formatKeys.end() || item->key != varName)) {
throw std::runtime_error(folly::to<std::string>(
"unknown format argument \"", varName, "\""));
}
output.append(folly::to<std::string>(item->argIndex));
output.append(p, 1);
// Based on the format key, increment estimatedWidth with the
// estimate of how many characters long the value of the format key
// will be. If it is a FILE or a FUN, the width will be variable
// depending on the values of those fields.
estimatedWidth += item->width;
if (item->key == "FILE") {
fileNameCount_++;
} else if (item->key == "FUN") {
functionNameCount_++;
}
// Figure out if there are modifiers that follow the key or if we
// continue processing literals.
if (*p == ':') {
state = FMT_MODIFIERS;
} else {
state = LITERAL;
}
}
break;
// In case we have started processing a format modifier (after :)
case FMT_MODIFIERS:
// Modifiers are just copied as is and are not considered to determine
// the estimatedWidth.
output.append(p, 1);
if (*p == '}') {
state = LITERAL;
}
break;
}
}
if (state != LITERAL) {
throw std::runtime_error("unterminated format string");
}
// Append a single space after the header format if header is not empty.
if (!output.empty()) {
output.append(" ");
estimatedWidth++;
}
logFormat_ = output;
staticEstimatedWidth_ = estimatedWidth;
// populate singleLineLogFormat_ with the padded line format.
if (colored_) {
singleLineLogFormat_ = folly::to<std::string>(
"{",
messageIndex + 1,
"}",
logFormat_,
"{",
messageIndex,
"}{",
messageIndex + 2,
"}\n");
} else {
singleLineLogFormat_ =
folly::to<std::string>(logFormat_, "{", messageIndex, "}\n");
}
}