summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJess Balint <jbalint@gmail.com>2022-04-21 15:08:20 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-04-21 16:19:45 +0000
commit08bcb09f03328105f0e21327f1f758bec3123450 (patch)
treea20a68ef2a0da2f7371d8c002f24a159e4eb5c27
parent4443b8a02d836f5006872e8f48fdd35627600b30 (diff)
downloadmongo-08bcb09f03328105f0e21327f1f758bec3123450.tar.gz
SERVER-65271 serverStatus should allow fine-grained metrics exclusion
(cherry picked from commit d964e2b912e26a96ee61cf4272102c8b4415fd2e)
-rw-r--r--jstests/noPassthrough/server_status_metrics_exclusion.js66
-rw-r--r--src/mongo/db/commands/server_status_command.cpp10
-rw-r--r--src/mongo/db/commands/server_status_internal.cpp52
-rw-r--r--src/mongo/db/commands/server_status_internal.h9
4 files changed, 128 insertions, 9 deletions
diff --git a/jstests/noPassthrough/server_status_metrics_exclusion.js b/jstests/noPassthrough/server_status_metrics_exclusion.js
new file mode 100644
index 00000000000..d088231607d
--- /dev/null
+++ b/jstests/noPassthrough/server_status_metrics_exclusion.js
@@ -0,0 +1,66 @@
+/**
+ * Tests that serverStatus metrics are filtered when requested.
+ */
+(function() {
+"use strict";
+const mongod = MongoRunner.runMongod();
+const dbName = jsTestName();
+const db = mongod.getDB(dbName);
+
+// Verify some assumptions about metrics structure for later tests.
+let serverStatusMetrics = db.serverStatus().metrics;
+
+assert(serverStatusMetrics.document.hasOwnProperty("deleted"));
+assert(serverStatusMetrics.document.hasOwnProperty("inserted"));
+assert(serverStatusMetrics.dotsAndDollarsFields.hasOwnProperty("inserts"));
+assert(serverStatusMetrics.dotsAndDollarsFields.hasOwnProperty("updates"));
+
+// Exclude the "document.deleted" field.
+serverStatusMetrics = db.serverStatus({metrics: {document: {deleted: false}}}).metrics;
+
+assert(!serverStatusMetrics.document.hasOwnProperty("deleted"));
+assert(serverStatusMetrics.document.hasOwnProperty("inserted"));
+
+// Exclude the "document.deleted" and "document.inserted" fields.
+serverStatusMetrics =
+ db.serverStatus({metrics: {document: {deleted: false, inserted: false}}}).metrics;
+
+assert(!serverStatusMetrics.document.hasOwnProperty("deleted"));
+assert(!serverStatusMetrics.document.hasOwnProperty("inserted"));
+
+// Exclude the "document.deleted" and "dotsAndDollarsFields.inserts" fields.
+serverStatusMetrics =
+ db.serverStatus({
+ metrics: {document: {deleted: false}, dotsAndDollarsFields: {inserts: false}}
+ }).metrics;
+
+assert(!serverStatusMetrics.document.hasOwnProperty("deleted"));
+assert(!serverStatusMetrics.dotsAndDollarsFields.hasOwnProperty("inserts"));
+
+// Include a "true" value for the "document.deleted" field. It should be included (ie, no-op).
+serverStatusMetrics = db.serverStatus({metrics: {document: {deleted: true}}}).metrics;
+
+assert(serverStatusMetrics.document.hasOwnProperty("deleted"));
+assert(serverStatusMetrics.document.hasOwnProperty("inserted"));
+
+// Attempt a non-boolean values which should be rejected (uassert).
+assert.commandFailedWithCode(db.serverStatus({metrics: {document: "Non-boolean"}}),
+ [ErrorCodes.InvalidBSONType]);
+
+assert.commandFailedWithCode(db.serverStatus({metrics: {document: {deleted: "Non-boolean"}}}),
+ [ErrorCodes.InvalidBSONType]);
+
+assert.commandFailedWithCode(db.serverStatus({metrics: {document: {deleted: ["Non-boolean"]}}}),
+ [ErrorCodes.InvalidBSONType]);
+
+assert.commandFailedWithCode(
+ db.serverStatus({metrics: {document: {deleted: {invalidObjectAtLeftLevel: 1}}}}),
+ [ErrorCodes.InvalidBSONType]);
+
+// Exclude the "document" subtree.
+serverStatusMetrics = db.serverStatus({metrics: {document: false}}).metrics;
+
+assert(!serverStatusMetrics.hasOwnProperty("document"));
+
+MongoRunner.stopMongod(mongod);
+})();
diff --git a/src/mongo/db/commands/server_status_command.cpp b/src/mongo/db/commands/server_status_command.cpp
index 6435da21c95..41d5a45a0f9 100644
--- a/src/mongo/db/commands/server_status_command.cpp
+++ b/src/mongo/db/commands/server_status_command.cpp
@@ -133,11 +133,17 @@ public:
// --- counters
bool includeMetricTree = MetricTree::theMetricTree != nullptr;
- if (cmdObj["metrics"].type() && !cmdObj["metrics"].trueValue())
+ auto metricsEl = cmdObj["metrics"_sd];
+ if (metricsEl.type() && !metricsEl.trueValue())
includeMetricTree = false;
if (includeMetricTree) {
- MetricTree::theMetricTree->appendTo(result);
+ if (metricsEl.type() == BSONType::Object) {
+ MetricTree::theMetricTree->appendTo(BSON("metrics" << metricsEl.embeddedObject()),
+ result);
+ } else {
+ MetricTree::theMetricTree->appendTo(result);
+ }
}
// --- some hard coded global things hard to pull out
diff --git a/src/mongo/db/commands/server_status_internal.cpp b/src/mongo/db/commands/server_status_internal.cpp
index c981a65fd4b..e4ea0e879c3 100644
--- a/src/mongo/db/commands/server_status_internal.cpp
+++ b/src/mongo/db/commands/server_status_internal.cpp
@@ -31,6 +31,7 @@
#include <iostream>
+#include "mongo/bson/bsontypes.h"
#include "mongo/db/commands/server_status_metric.h"
#include "mongo/util/str.h"
@@ -71,16 +72,53 @@ void MetricTree::_add(const string& path, ServerStatusMetric* metric) {
}
void MetricTree::appendTo(BSONObjBuilder& b) const {
- for (map<string, ServerStatusMetric*>::const_iterator i = _metrics.begin(); i != _metrics.end();
- ++i) {
- i->second->appendAtLeaf(b);
+ for (const auto& i : _metrics) {
+ i.second->appendAtLeaf(b);
}
- for (map<string, MetricTree*>::const_iterator i = _subtrees.begin(); i != _subtrees.end();
- ++i) {
- BSONObjBuilder bb(b.subobjStart(i->first));
- i->second->appendTo(bb);
+ for (const auto& i : _subtrees) {
+ BSONObjBuilder bb(b.subobjStart(i.first));
+ i.second->appendTo(bb);
bb.done();
}
}
+
+void MetricTree::appendTo(const BSONObj& excludePaths, BSONObjBuilder& b) const {
+ auto fieldNamesInExclude = excludePaths.getFieldNames<stdx::unordered_set<std::string>>();
+ for (const auto& i : _metrics) {
+ auto key = i.first;
+ auto el = fieldNamesInExclude.contains(key) ? excludePaths.getField(key) : BSONElement();
+ if (el) {
+ uassert(ErrorCodes::InvalidBSONType,
+ "Exclusion value for a leaf must be a boolean.",
+ el.type() == Bool);
+ if (el.boolean() == false) {
+ continue;
+ }
+ }
+ i.second->appendAtLeaf(b);
+ }
+
+ for (const auto& i : _subtrees) {
+ auto key = i.first;
+ auto el = fieldNamesInExclude.contains(key) ? excludePaths.getField(key) : BSONElement();
+ if (el) {
+ uassert(ErrorCodes::InvalidBSONType,
+ "Exclusion value must be a boolean or a nested object.",
+ el.type() == Bool || el.type() == Object);
+ if (el.isBoolean() && el.boolean() == false) {
+ continue;
+ }
+ }
+
+ BSONObjBuilder bb(b.subobjStart(key));
+ if (el.type() == Object) {
+ i.second->appendTo(el.embeddedObject(), bb);
+ } else {
+ i.second->appendTo(bb);
+ }
+ bb.done();
+ }
+}
+
} // namespace mongo
diff --git a/src/mongo/db/commands/server_status_internal.h b/src/mongo/db/commands/server_status_internal.h
index f9bde775db3..b59b0b36762 100644
--- a/src/mongo/db/commands/server_status_internal.h
+++ b/src/mongo/db/commands/server_status_internal.h
@@ -42,8 +42,17 @@ class MetricTree {
public:
void add(ServerStatusMetric* metric);
+ /**
+ * Append the metrics tree to the given BSON builder.
+ */
void appendTo(BSONObjBuilder& b) const;
+ /**
+ * Implementation of appendTo which allows tree of exclude paths. The alternative overload is
+ * preferred to avoid overhead when no excludes are present.
+ */
+ void appendTo(const BSONObj& excludePaths, BSONObjBuilder& b) const;
+
static MetricTree* theMetricTree;
private: