From dd0c322ddd022e409bde89e8d3566e67ac166cdc Mon Sep 17 00:00:00 2001 From: Billy Donahue Date: Wed, 1 Jul 2020 14:49:38 +0000 Subject: SERVER-47933 runtime attr unique check Recommit: handle pre-init testing proctor (cherry picked from commit 07deced6b3fa2f5927bb33bd4940cc60d6bc4607) --- src/mongo/logv2/attribute_storage.h | 9 +++++++++ src/mongo/logv2/log_detail.cpp | 35 +++++++++++++++++++++++++++++++++++ src/mongo/logv2/logv2_test.cpp | 7 +++++++ 3 files changed, 51 insertions(+) diff --git a/src/mongo/logv2/attribute_storage.h b/src/mongo/logv2/attribute_storage.h index 29ff0ed7dd8..90067013cd2 100644 --- a/src/mongo/logv2/attribute_storage.h +++ b/src/mongo/logv2/attribute_storage.h @@ -679,6 +679,8 @@ private: // Wrapper around internal pointer of AttributeStorage so it does not need any template parameters class TypeErasedAttributeStorage { public: + using const_iterator = const detail::NamedAttribute*; + TypeErasedAttributeStorage() : _size(0) {} template @@ -696,6 +698,13 @@ public: return _size; } + const_iterator begin() const { + return _data; + } + const_iterator end() const { + return _data + _size; + } + // Applies a function to every stored named attribute in order they are captured template void apply(Func&& f) const { diff --git a/src/mongo/logv2/log_detail.cpp b/src/mongo/logv2/log_detail.cpp index 766c79488e4..d90d44ddf9e 100644 --- a/src/mongo/logv2/log_detail.cpp +++ b/src/mongo/logv2/log_detail.cpp @@ -31,12 +31,15 @@ #include "mongo/platform/basic.h" +#include + #include "mongo/logv2/attributes.h" #include "mongo/logv2/log.h" #include "mongo/logv2/log_domain.h" #include "mongo/logv2/log_domain_internal.h" #include "mongo/logv2/log_options.h" #include "mongo/logv2/log_source.h" +#include "mongo/util/testing_proctor.h" namespace mongo::logv2::detail { @@ -99,12 +102,44 @@ private: std::deque _storage; }; +static void checkUniqueAttrs(int32_t id, const TypeErasedAttributeStorage& attrs) { + if (attrs.size() <= 1) + return; + // O(N^2), but N is small and this avoids alloc, sort, and operator<. + auto first = attrs.begin(); + auto last = attrs.end(); + while (first != last) { + auto it = first; + ++first; + if (std::find_if(first, last, [&](auto&& a) { return a.name == it->name; }) == last) + continue; + StringData sep; + std::string msg; + for (auto&& a : attrs) { + msg.append(sep.rawData(), sep.size()) + .append("\"") + .append(a.name.rawData(), a.name.size()) + .append("\""); + sep = ","_sd; + } + uasserted(4793301, format(FMT_STRING("LOGV2 (id={}) attribute collision: [{}]"), id, msg)); + } +} + void doLogImpl(int32_t id, LogSeverity const& severity, LogOptions const& options, StringData message, TypeErasedAttributeStorage const& attrs) { dassert(options.component() != LogComponent::kNumLogComponents); + // TestingProctor isEnabled cannot be called before it has been + // initialized. But log statements occurring earlier than that still need + // to be checked. Log performance isn't as important at startup, so until + // the proctor is initialized, we check everything. + if (const auto& tp = TestingProctor::instance(); !tp.isInitialized() || tp.isEnabled()) { + checkUniqueAttrs(id, attrs); + } + auto& source = options.domain().internal().source(); auto record = source.open_record(id, severity, diff --git a/src/mongo/logv2/logv2_test.cpp b/src/mongo/logv2/logv2_test.cpp index 2d87434a4da..0b1af757618 100644 --- a/src/mongo/logv2/logv2_test.cpp +++ b/src/mongo/logv2/logv2_test.cpp @@ -1319,6 +1319,13 @@ TEST_F(LogV2ContainerTest, StringMapUint32) { }); } +TEST_F(LogV2Test, AttrNameCollision) { + ASSERT_THROWS_CODE( + LOGV2(4793300, "Collision {k1}", "Collision", "k1"_attr = "v1", "k1"_attr = "v2"), + AssertionException, + 4793301); +} + TEST_F(LogV2Test, Unicode) { auto lines = makeLineCapture(JSONFormatter()); -- cgit v1.2.1