summaryrefslogtreecommitdiff
path: root/src/mongo/logv2
diff options
context:
space:
mode:
authorBilly Donahue <billy.donahue@mongodb.com>2020-04-02 13:49:54 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-04-21 07:40:44 +0000
commit10ee8c156f747c55895067cb418827c57c59557b (patch)
tree9d10144b571ff253895802656e9cde490470e84a /src/mongo/logv2
parent79cfaa04168ce3994b5499f45fd728ebccbef0bb (diff)
downloadmongo-10ee8c156f747c55895067cb418827c57c59557b.tar.gz
SERVER-47220 log json field padding
Diffstat (limited to 'src/mongo/logv2')
-rw-r--r--src/mongo/logv2/json_formatter.cpp199
-rw-r--r--src/mongo/logv2/text_formatter.cpp16
2 files changed, 108 insertions, 107 deletions
diff --git a/src/mongo/logv2/json_formatter.cpp b/src/mongo/logv2/json_formatter.cpp
index df87a5b40a8..98df9bb898c 100644
--- a/src/mongo/logv2/json_formatter.cpp
+++ b/src/mongo/logv2/json_formatter.cpp
@@ -230,109 +230,116 @@ void JSONFormatter::format(fmt::memory_buffer& buffer,
const TypeErasedAttributeStorage& attrs,
LogTag tags,
LogTruncation truncation) const {
+ namespace c = constants;
+ static constexpr auto kFmt = JsonStringFormat::ExtendedRelaxedV2_0_0;
StringData severityString = severity.toStringDataCompact();
StringData componentString = component.getNameForLog();
-
- // Put all fields up until the message value
- static const auto& fmtStrOpen = *new auto(fmt::compile<StringData>(R"({{)"
- R"("{}":{{"$date":")"));
- compiled_format_to(buffer, fmtStrOpen, constants::kTimestampFieldName);
- switch (_timestampFormat) {
- case LogTimestampFormat::kISO8601UTC:
- outputDateAsISOStringUTC(buffer, date);
- break;
- case LogTimestampFormat::kISO8601Local:
- outputDateAsISOStringLocal(buffer, date);
- break;
+ bool local = _timestampFormat == LogTimestampFormat::kISO8601Local;
+ size_t attributeMaxSize = truncation != LogTruncation::Enabled
+ ? 0
+ : (_maxAttributeSizeKB != 0 ? _maxAttributeSizeKB->loadRelaxed() * 1024
+ : c::kDefaultMaxAttributeOutputSizeKB * 1024);
+ auto write = [&](StringData s) { buffer.append(s.rawData(), s.rawData() + s.size()); };
+
+ struct CommaTracker {
+ StringData comma;
+ size_t padDebt = 0;
};
- static const auto& fmtStrBody =
- *new auto(fmt::compile<StringData, // severity start
- StringData,
- StringData,
- int,
- StringData, // component start
- StringData,
- StringData,
- int,
- StringData, // id start
- StringData,
- StringData,
- int,
- StringData, // context start
- StringData,
- StringData> // message start
- (R"("}},)" // close timestamp
- R"("{}":"{}"{: <{}})" // severity with padding for the comma
- R"("{}":"{}"{: <{}})" // component with padding for the comma
- R"("{}":{}{: <{}})" // id with padding for the comma
- R"("{}":"{}",)" // context
- R"("{}":")" // message
- ));
- fmt::format_int idString(id);
- compiled_format_to(
- buffer,
- fmtStrBody,
- // severity, left align the comma and add padding to create fixed column width
- constants::kSeverityFieldName,
- severityString,
- ","_sd,
- 3 - severityString.size(),
- // component, left align the comma and add padding to create fixed column width
- constants::kComponentFieldName,
- componentString,
- ","_sd,
- 9 - componentString.size(),
- // id
- constants::kIdFieldName,
- StringData(idString.data(), idString.size()),
- ","_sd,
- 8 - idString.size(),
- // context
- constants::kContextFieldName,
- context,
- // message
- constants::kMessageFieldName);
-
- str::escapeForJSON(buffer, message);
- buffer.push_back('"');
-
- static const auto& fmtStrAttr = *new auto(fmt::compile<StringData>(R"(,"{}":{{)"));
- static const auto& fmtStrTruncated = *new auto(fmt::compile<StringData>(R"(,"{}":)"));
- if (!attrs.empty()) {
- compiled_format_to(buffer, fmtStrAttr, constants::kAttributesFieldName);
- // comma separated list of attributes (no opening/closing brace are added here)
- size_t attributeMaxSize = 0;
- if (truncation == LogTruncation::Enabled) {
- if (_maxAttributeSizeKB)
- attributeMaxSize = _maxAttributeSizeKB->loadRelaxed() * 1024;
- else
- attributeMaxSize = constants::kDefaultMaxAttributeOutputSizeKB * 1024;
- }
- JSONValueExtractor extractor(buffer, attributeMaxSize);
- attrs.apply(extractor);
- buffer.push_back('}');
-
- if (BSONObj truncated = extractor.truncated(); !truncated.isEmpty()) {
- compiled_format_to(buffer, fmtStrTruncated, constants::kTruncatedFieldName);
- truncated.jsonStringBuffer(
- JsonStringFormat::ExtendedRelaxedV2_0_0, 0, false, buffer, 0);
- }
- if (BSONObj truncatedSizes = extractor.truncatedSizes(); !truncatedSizes.isEmpty()) {
- compiled_format_to(buffer, fmtStrTruncated, constants::kTruncatedSizeFieldName);
- truncatedSizes.jsonStringBuffer(
- JsonStringFormat::ExtendedRelaxedV2_0_0, 0, false, buffer, 0);
+ // First call to `comma` emits nothing, subsequent calls emit ",", so this is a
+ // prefix used to join elements. If there is any padding debt in the
+ // tracker, then that amount of whitespace is emitted after the comma and the
+ // debt is cleared.
+ auto writeComma = [&](CommaTracker& tracker) {
+ write(tracker.comma);
+ tracker.comma = ","_sd;
+ while (tracker.padDebt) {
+ // Relies on substr's truncation.
+ StringData space = " "_sd.substr(0, tracker.padDebt);
+ write(space);
+ tracker.padDebt -= space.size();
}
- }
+ };
- static const auto& fmtStrTags = *new auto(fmt::compile<StringData>(R"(,"{}":)"));
- if (tags != LogTag::kNone) {
- compiled_format_to(buffer, fmtStrTags, constants::kTagsFieldName);
- tags.toBSONArray().jsonStringBuffer(
- JsonStringFormat::ExtendedRelaxedV2_0_0, 0, true, buffer);
- }
+ // Emit `f()` normally, noting its size. If it is narrower than `width`, we incur a
+ // padding debt which is stored in the `tracker`. The next `comma` emitted by the
+ // `tracker` will be followed by enough whitespace to pay off the debt.
+ auto padNextComma = [&](CommaTracker& tracker, size_t width, auto f) {
+ return [&, width, f] {
+ size_t pre = buffer.size();
+ f();
+ if (size_t wrote = buffer.size() - pre; wrote < width)
+ tracker.padDebt = width - wrote;
+ };
+ };
+
+ auto strFn = [&](StringData s) { return [&, s] { write(s); }; };
+ auto escFn = [&](StringData s) { return [&, s] { str::escapeForJSON(buffer, s); }; };
+ auto intFn = [&](auto x) {
+ return [&, x] {
+ fmt::format_int s{x};
+ write(StringData{s.data(), s.size()});
+ };
+ };
+
+ auto quote = [&](auto f) {
+ return [&, f] {
+ write("\"");
+ f();
+ write("\"");
+ };
+ };
+
+ auto field = [&](CommaTracker& tracker, StringData name, auto f) {
+ writeComma(tracker);
+ quote(strFn(name))();
+ write(":");
+ f();
+ };
+
+ auto jsobj = [&](auto f) {
+ return [&, f] {
+ CommaTracker top;
+ write("{");
+ f(top);
+ write("}");
+ };
+ };
- buffer.push_back('}');
+ auto dateFn = [&](Date_t date) {
+ return jsobj([&, date](CommaTracker& tracker) {
+ field(tracker, "$date", quote([&, date] {
+ write(StringData{DateStringBuffer{}.iso8601(date, local)});
+ }));
+ });
+ };
+ auto bsonObjFn = [&](BSONObj o) {
+ return [&, o] { o.jsonStringBuffer(kFmt, 0, false, buffer, 0); };
+ };
+ auto bsonArrFn = [&](BSONArray a) {
+ return [&, a] { a.jsonStringBuffer(kFmt, 0, true, buffer, 0); };
+ };
+
+ jsobj([&](CommaTracker& top) {
+ field(top, c::kTimestampFieldName, dateFn(date));
+ field(top, c::kSeverityFieldName, padNextComma(top, 5, quote(strFn(severityString))));
+ field(top, c::kComponentFieldName, padNextComma(top, 11, quote(strFn(componentString))));
+ field(top, c::kIdFieldName, padNextComma(top, 8, intFn(id)));
+ field(top, c::kContextFieldName, quote(strFn(context)));
+ field(top, c::kMessageFieldName, quote(escFn(message)));
+ if (!attrs.empty()) {
+ JSONValueExtractor extractor(buffer, attributeMaxSize);
+ field(top, c::kAttributesFieldName, jsobj([&](CommaTracker&) {
+ attrs.apply(extractor);
+ }));
+ if (BSONObj o = extractor.truncated(); !o.isEmpty())
+ field(top, c::kTruncatedFieldName, bsonObjFn(o));
+ if (BSONObj o = extractor.truncatedSizes(); !o.isEmpty())
+ field(top, c::kTruncatedSizeFieldName, bsonObjFn(o));
+ }
+ if (tags != LogTag::kNone)
+ field(top, c::kTagsFieldName, bsonArrFn(tags.toBSONArray()));
+ })();
}
void JSONFormatter::operator()(boost::log::record_view const& rec,
diff --git a/src/mongo/logv2/text_formatter.cpp b/src/mongo/logv2/text_formatter.cpp
index 8314160d436..b0421abf8e9 100644
--- a/src/mongo/logv2/text_formatter.cpp
+++ b/src/mongo/logv2/text_formatter.cpp
@@ -44,20 +44,14 @@ namespace mongo::logv2 {
void TextFormatter::operator()(boost::log::record_view const& rec,
boost::log::formatting_ostream& strm) const {
- using namespace boost::log;
+ using boost::log::extract;
- Date_t timeStamp = extract<Date_t>(attributes::timeStamp(), rec).get();
fmt::memory_buffer buffer;
- switch (_timestampFormat) {
- case LogTimestampFormat::kISO8601UTC:
- outputDateAsISOStringUTC(buffer, timeStamp);
- break;
- case LogTimestampFormat::kISO8601Local:
- outputDateAsISOStringLocal(buffer, timeStamp);
- break;
- };
fmt::format_to(buffer,
- " {:<2} {:<8} [{}] ",
+ "{} {:<2} {:<8} [{}] ",
+ StringData{DateStringBuffer{}.iso8601(
+ extract<Date_t>(attributes::timeStamp(), rec).get(),
+ _timestampFormat == LogTimestampFormat::kISO8601Local)},
extract<LogSeverity>(attributes::severity(), rec).get().toStringDataCompact(),
extract<LogComponent>(attributes::component(), rec).get().getNameForLog(),
extract<StringData>(attributes::threadName(), rec).get());