Skip to content

Restore server status collector #557

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down
27 changes: 21 additions & 6 deletions exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type Opts struct {
EnableDBStatsFreeStorage bool
EnableDiagnosticData bool
EnableReplicasetStatus bool
EnableServerStatus bool
EnableCurrentopMetrics bool
EnableTopMetrics bool
EnableIndexStats bool
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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":
Expand Down
2 changes: 1 addition & 1 deletion exporter/replset_status_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nesting here facilitates the desired renaming and label management in makeMetrics in the same manner as when fetched via getDiagnosticData.

This is a breaking change when running with --collector.replicasetstatus as these metrics were previously prefixed with mongodb_ and will now be prefixed with mongodb_rs_. This achieves parity with the metrics returned when using --collector.diagnosticdata and allows for easy switching between the collection methods.

ch <- metric
}
}
Expand Down
16 changes: 8 additions & 8 deletions exporter/replset_status_collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
85 changes: 85 additions & 0 deletions exporter/serverstatus_collector.go
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.

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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nesting here facilitates the desired renaming and label management in makeMetrics in the same manner as when fetched via getDiagnosticData.

ch <- metric
}
}
60 changes: 60 additions & 0 deletions exporter/serverstatus_collector_test.go
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.

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)
}
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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,
Expand Down