void CustomLogFormatter::parseFormatString()

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");
  }
}