diff --git a/REFERENCE.md b/REFERENCE.md
index a78f7edc7..e7fc501dd 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -17,6 +17,7 @@
|--log.level|Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]|--log.level="error"|
|--collector.diagnosticdata|Enable collecting metrics from getDiagnosticData|
|--collector.replicasetstatus|Enable collecting metrics from replSetGetStatus|
+|--collector.serverstatus|Enable collecting metrics from serverStatus|
|--collector.dbstats|Enable collecting metrics from dbStats||
|--collector.topmetrics|Enable collecting metrics from top admin command|
|--collector.currentopmetrics|Enable collecting metrics from currentop admin command|
diff --git a/exporter/exporter.go b/exporter/exporter.go
index 4787abe7a..b03888e43 100644
--- a/exporter/exporter.go
+++ b/exporter/exporter.go
@@ -66,6 +66,7 @@ type Opts struct {
EnableDBStatsFreeStorage bool
EnableDiagnosticData bool
EnableReplicasetStatus bool
+ EnableServerStatus bool
EnableCurrentopMetrics bool
EnableTopMetrics bool
EnableIndexStats bool
@@ -172,6 +173,7 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol
e.opts.EnableCollStats = true
e.opts.EnableTopMetrics = true
e.opts.EnableReplicasetStatus = true
+ e.opts.EnableServerStatus = true
e.opts.EnableIndexStats = true
e.opts.EnableCurrentopMetrics = true
e.opts.EnableProfile = true
@@ -205,7 +207,9 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol
registry.MustRegister(ic)
}
- if e.opts.EnableDiagnosticData && requestOpts.EnableDiagnosticData {
+ // getDiagnosticData does not return any data on mongos.
+ collectDiagnosticData := e.opts.EnableDiagnosticData && nodeType != typeMongos && requestOpts.EnableDiagnosticData
+ if collectDiagnosticData {
ddc := newDiagnosticDataCollector(ctx, client, e.opts.Logger,
e.opts.CompatibleMode, topologyInfo)
registry.MustRegister(ddc)
@@ -235,11 +239,20 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol
registry.MustRegister(tc)
}
- // replSetGetStatus is not supported through mongos.
- if e.opts.EnableReplicasetStatus && nodeType != typeMongos && requestOpts.EnableReplicasetStatus {
- rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger,
- e.opts.CompatibleMode, topologyInfo)
- registry.MustRegister(rsgsc)
+ // Only collect replica set and server status separately if we're not already fetching via diagnostic data
+ if !collectDiagnosticData {
+ // replSetGetStatus is not supported through mongos.
+ if e.opts.EnableReplicasetStatus && nodeType != typeMongos && requestOpts.EnableReplicasetStatus {
+ rsgsc := newReplicationSetStatusCollector(ctx, client, e.opts.Logger,
+ e.opts.CompatibleMode, topologyInfo)
+ registry.MustRegister(rsgsc)
+ }
+
+ if e.opts.EnableServerStatus && requestOpts.EnableServerStatus {
+ ssc := newServerStatusCollector(ctx, client, e.opts.Logger,
+ e.opts.CompatibleMode, topologyInfo)
+ registry.MustRegister(ssc)
+ }
}
return registry
@@ -303,6 +316,8 @@ func (e *Exporter) Handler() http.Handler {
requestOpts.EnableDiagnosticData = true
case "replicasetstatus":
requestOpts.EnableReplicasetStatus = true
+ case "serverstatus":
+ requestOpts.EnableServerStatus = true
case "dbstats":
requestOpts.EnableDBStats = true
case "topmetrics":
diff --git a/exporter/replset_status_collector.go b/exporter/replset_status_collector.go
index 65e9ceb7e..69664e76f 100644
--- a/exporter/replset_status_collector.go
+++ b/exporter/replset_status_collector.go
@@ -81,7 +81,7 @@ func (d *replSetGetStatusCollector) collect(ch chan<- prometheus.Metric) {
logger.Debug("replSetGetStatus result:")
debugResult(logger, m)
- for _, metric := range makeMetrics("", m, d.topologyInfo.baseLabels(), d.compatibleMode) {
+ for _, metric := range makeMetrics("", bson.M{"replSetGetStatus": m}, d.topologyInfo.baseLabels(), d.compatibleMode) {
ch <- metric
}
}
diff --git a/exporter/replset_status_collector_test.go b/exporter/replset_status_collector_test.go
index 12a5cbcb0..49c02239d 100644
--- a/exporter/replset_status_collector_test.go
+++ b/exporter/replset_status_collector_test.go
@@ -40,19 +40,19 @@ func TestReplsetStatusCollector(t *testing.T) {
// The last \n at the end of this string is important
expected := strings.NewReader(`
- # HELP mongodb_myState myState
- # TYPE mongodb_myState untyped
- mongodb_myState 1
- # HELP mongodb_ok ok
- # TYPE mongodb_ok untyped
- mongodb_ok 1` + "\n")
+ # HELP mongodb_rs_myState replSetGetStatus.
+ # TYPE mongodb_rs_myState untyped
+ mongodb_rs_myState 1
+ # HELP mongodb_rs_ok replSetGetStatus.
+ # TYPE mongodb_rs_ok untyped
+ mongodb_rs_ok 1` + "\n")
// Filter metrics for 2 reasons:
// 1. The result is huge
// 2. We need to check against know values. Don't use metrics that return counters like uptime
// or counters like the number of transactions because they won't return a known value to compare
filter := []string{
- "mongodb_myState",
- "mongodb_ok",
+ "mongodb_rs_myState",
+ "mongodb_rs_ok",
}
err := testutil.CollectAndCompare(c, expected, filter...)
assert.NoError(t, err)
diff --git a/exporter/serverstatus_collector.go b/exporter/serverstatus_collector.go
new file mode 100644
index 000000000..b5375f15e
--- /dev/null
+++ b/exporter/serverstatus_collector.go
@@ -0,0 +1,85 @@
+// mongodb_exporter
+// Copyright (C) 2017 Percona LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package exporter
+
+import (
+ "context"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/sirupsen/logrus"
+ "go.mongodb.org/mongo-driver/bson"
+ "go.mongodb.org/mongo-driver/mongo"
+)
+
+type serverStatusCollector struct {
+ ctx context.Context
+ base *baseCollector
+
+ compatibleMode bool
+ topologyInfo labelsGetter
+}
+
+// newServerStatusCollector creates a collector for statistics on server status.
+func newServerStatusCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, compatible bool, topology labelsGetter) *serverStatusCollector {
+ return &serverStatusCollector{
+ ctx: ctx,
+ base: newBaseCollector(client, logger),
+ compatibleMode: compatible,
+ topologyInfo: topology,
+ }
+}
+
+func (d *serverStatusCollector) Describe(ch chan<- *prometheus.Desc) {
+ d.base.Describe(d.ctx, ch, d.collect)
+}
+
+func (d *serverStatusCollector) Collect(ch chan<- prometheus.Metric) {
+ d.base.Collect(ch)
+}
+
+func (d *serverStatusCollector) collect(ch chan<- prometheus.Metric) {
+ defer measureCollectTime(ch, "mongodb", "serverstatus")()
+
+ logger := d.base.logger
+ client := d.base.client
+
+ cmd := bson.D{
+ {
+ Key: "serverStatus", Value: "1",
+ },
+ {
+ Key: "metrics", Value: bson.M{
+ // TODO: PMM-9568 : Add support to handle histogram metrics
+ "query": bson.M{"multiPlanner": bson.M{"histograms": false}},
+ },
+ },
+ }
+ res := client.Database("admin").RunCommand(d.ctx, cmd)
+
+ var m bson.M
+ if err := res.Decode(&m); err != nil {
+ ch <- prometheus.NewInvalidMetric(prometheus.NewInvalidDesc(err), err)
+ return
+ }
+
+ logger.Debug("serverStatus result:")
+ debugResult(logger, m)
+
+ for _, metric := range makeMetrics("", bson.M{"serverStatus": m}, d.topologyInfo.baseLabels(), d.compatibleMode) {
+ ch <- metric
+ }
+}
diff --git a/exporter/serverstatus_collector_test.go b/exporter/serverstatus_collector_test.go
new file mode 100644
index 000000000..5a513c9dc
--- /dev/null
+++ b/exporter/serverstatus_collector_test.go
@@ -0,0 +1,60 @@
+// mongodb_exporter
+// Copyright (C) 2017 Percona LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package exporter
+
+import (
+ "context"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/prometheus/client_golang/prometheus/testutil"
+ "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/percona/mongodb_exporter/internal/tu"
+)
+
+func TestServerStatusDataCollector(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
+ defer cancel()
+
+ client := tu.DefaultTestClient(ctx, t)
+
+ ti := labelsGetterMock{}
+
+ c := newServerStatusCollector(ctx, client, logrus.New(), false, ti)
+
+ // The last \n at the end of this string is important
+ expected := strings.NewReader(`
+ # HELP mongodb_ss_mem_bits serverStatus.mem.
+ # TYPE mongodb_ss_mem_bits untyped
+ mongodb_ss_mem_bits 64
+ # HELP mongodb_ss_metrics_commands_connPoolSync_failed serverStatus.metrics.commands.connPoolSync.
+ # TYPE mongodb_ss_metrics_commands_connPoolSync_failed untyped
+ mongodb_ss_metrics_commands_connPoolSync_failed 0` + "\n")
+ // Filter metrics for 2 reasons:
+ // 1. The result is huge
+ // 2. We need to check against know values. Don't use metrics that return counters like uptime
+ // or counters like the number of transactions because they won't return a known value to compare
+ filter := []string{
+ "mongodb_ss_mem_bits",
+ "mongodb_ss_metrics_commands_connPoolSync_failed",
+ }
+ err := testutil.CollectAndCompare(c, expected, filter...)
+ assert.NoError(t, err)
+}
diff --git a/main.go b/main.go
index 1e5d13561..578b366fd 100644
--- a/main.go
+++ b/main.go
@@ -48,6 +48,7 @@ type GlobalFlags struct {
EnableDiagnosticData bool `name:"collector.diagnosticdata" help:"Enable collecting metrics from getDiagnosticData"`
EnableReplicasetStatus bool `name:"collector.replicasetstatus" help:"Enable collecting metrics from replSetGetStatus"`
+ EnableServerStatus bool `name:"collector.serverstatus" help:"Enable collecting metrics from serverStatus"`
EnableDBStats bool `name:"collector.dbstats" help:"Enable collecting metrics from dbStats"`
EnableDBStatsFreeStorage bool `name:"collector.dbstatsfreestorage" help:"Enable collecting free space metrics from dbStats"`
EnableTopMetrics bool `name:"collector.topmetrics" help:"Enable collecting metrics from top admin command"`
@@ -143,6 +144,7 @@ func buildExporter(opts GlobalFlags) *exporter.Exporter {
EnableDiagnosticData: opts.EnableDiagnosticData,
EnableReplicasetStatus: opts.EnableReplicasetStatus,
+ EnableServerStatus: opts.EnableServerStatus,
EnableCurrentopMetrics: opts.EnableCurrentopMetrics,
EnableTopMetrics: opts.EnableTopMetrics,
EnableDBStats: opts.EnableDBStats,