Skip to content

Commit 0239f77

Browse files
hiroshiJiriCtvrtkadenisokBupycHuk
authored
replSetGetConfigCollector (#295)
* replSetGetConfigCollector. it just works * Use member_idx for host field to match with replStatus * Add --disable.replicasetconfig option flag * Use errors.As() instead of type assertion. * Add replset_config_collector_test. * Revert "Use errors.As() instead of type assertion." This reverts commit 54ded8c. * nolint - errors.As seems to cause segfault. * Update exporter/replset_config_collector.go * Update exporter/replset_config_collector_test.go * Update exporter/replset_config_collector.go * Update exporter/replset_config_collector_test.go * Update exporter/replset_config_collector.go * Update exporter/exporter.go * Update exporter/exporter.go * Update exporter/replset_config_collector_test.go * Update replset_config_collector.go * Update replset_config_collector_test.go * Update exporter/replset_config_collector_test.go * Update exporter/replset_config_collector.go * Update replset_config_collector.go --------- Co-authored-by: JiriCtvrtka <[email protected]> Co-authored-by: Denys Kondratenko <[email protected]> Co-authored-by: Nurlan Moldomurov <[email protected]> Co-authored-by: Nurlan Moldomurov <[email protected]>
1 parent 70ab813 commit 0239f77

File tree

7 files changed

+183
-0
lines changed

7 files changed

+183
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Currently, these metric sources are implemented:
1414
- $indexStats
1515
- getDiagnosticData
1616
- replSetGetStatus
17+
- replSetGetConfig
1718
- serverStatus
1819

1920
## Supported MongoDB versions

exporter/exporter.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ type Opts struct {
6464
EnableDBStatsFreeStorage bool
6565
EnableDiagnosticData bool
6666
EnableReplicasetStatus bool
67+
EnableReplicasetConfig bool
6768
EnableCurrentopMetrics bool
6869
EnableTopMetrics bool
6970
EnableIndexStats bool
@@ -163,6 +164,7 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol
163164
e.opts.EnableCollStats = true
164165
e.opts.EnableTopMetrics = true
165166
e.opts.EnableReplicasetStatus = true
167+
e.opts.EnableReplicasetConfig = true
166168
e.opts.EnableIndexStats = true
167169
e.opts.EnableCurrentopMetrics = true
168170
e.opts.EnableProfile = true
@@ -239,6 +241,12 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol
239241
registry.MustRegister(rsgsc)
240242
}
241243

244+
// replSetGetStatus is not supported through mongos.
245+
if e.opts.EnableReplicasetConfig && nodeType != typeMongos && requestOpts.EnableReplicasetConfig {
246+
rsgsc := newReplicationSetConfigCollector(ctx, client, e.opts.Logger,
247+
e.opts.CompatibleMode, topologyInfo)
248+
registry.MustRegister(rsgsc)
249+
}
242250
if e.opts.EnableShards && nodeType == typeMongos && requestOpts.EnableShards {
243251
sc := newShardsCollector(ctx, client, e.opts.Logger, e.opts.CompatibleMode)
244252
registry.MustRegister(sc)
@@ -374,6 +382,8 @@ func GetRequestOpts(filters []string, defaultOpts *Opts) Opts {
374382
requestOpts.EnableDiagnosticData = true
375383
case "replicasetstatus":
376384
requestOpts.EnableReplicasetStatus = true
385+
case "replicasetconfig":
386+
requestOpts.EnableReplicasetConfig = true
377387
case "dbstats":
378388
requestOpts.EnableDBStats = true
379389
case "topmetrics":

exporter/metrics.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@ func processSlice(prefix, k string, v []interface{}, commonLabels map[string]str
328328
if state, ok := s["stateStr"].(string); ok {
329329
labels["member_state"] = state
330330
}
331+
if host, ok := s["host"].(string); ok {
332+
labels["member_idx"] = host
333+
}
331334

332335
metrics = append(metrics, makeMetrics(prefix+k, s, labels, compatibleMode)...)
333336
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// mongodb_exporter
2+
// Copyright (C) 2017 Percona LLC
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package exporter
17+
18+
import (
19+
"context"
20+
21+
"github.com/pkg/errors"
22+
"github.com/prometheus/client_golang/prometheus"
23+
"github.com/sirupsen/logrus"
24+
"go.mongodb.org/mongo-driver/bson"
25+
"go.mongodb.org/mongo-driver/mongo"
26+
)
27+
28+
type replSetGetConfigCollector struct {
29+
ctx context.Context
30+
base *baseCollector
31+
32+
compatibleMode bool
33+
topologyInfo labelsGetter
34+
}
35+
36+
// newReplicationSetConfigCollector creates a collector for configuration of replication set.
37+
func newReplicationSetConfigCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger, compatible bool, topology labelsGetter) *replSetGetConfigCollector {
38+
return &replSetGetConfigCollector{
39+
ctx: ctx,
40+
base: newBaseCollector(client, logger.WithFields(logrus.Fields{"collector": "replset_config"})),
41+
42+
compatibleMode: compatible,
43+
topologyInfo: topology,
44+
}
45+
}
46+
47+
func (d *replSetGetConfigCollector) Describe(ch chan<- *prometheus.Desc) {
48+
d.base.Describe(d.ctx, ch, d.collect)
49+
}
50+
51+
func (d *replSetGetConfigCollector) Collect(ch chan<- prometheus.Metric) {
52+
d.base.Collect(ch)
53+
}
54+
55+
func (d *replSetGetConfigCollector) collect(ch chan<- prometheus.Metric) {
56+
defer measureCollectTime(ch, "mongodb", "replset_config")()
57+
58+
logger := d.base.logger
59+
client := d.base.client
60+
61+
cmd := bson.D{{Key: "replSetGetConfig", Value: "1"}}
62+
res := client.Database("admin").RunCommand(d.ctx, cmd)
63+
64+
var m bson.M
65+
66+
if err := res.Decode(&m); err != nil {
67+
if e, ok := err.(mongo.CommandError); ok { //nolint // https://github.com/percona/mongodb_exporter/pull/295#issuecomment-922874632
68+
if e.Code == replicationNotYetInitialized || e.Code == replicationNotEnabled {
69+
return
70+
}
71+
}
72+
logger.Errorf("cannot get replSetGetConfig: %s", err)
73+
74+
return
75+
}
76+
77+
config, ok := m["config"].(bson.M)
78+
if !ok {
79+
err := errors.Wrapf(errUnexpectedDataType, "%T for data field", m["config"])
80+
logger.Errorf("cannot decode getDiagnosticData: %s", err)
81+
82+
return
83+
}
84+
m = config
85+
86+
logger.Debug("replSetGetConfig result:")
87+
debugResult(logger, m)
88+
89+
for _, metric := range makeMetrics("rs_cfg", m, d.topologyInfo.baseLabels(), d.compatibleMode) {
90+
ch <- metric
91+
}
92+
}
93+
94+
var _ prometheus.Collector = (*replSetGetConfigCollector)(nil)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// mongodb_exporter
2+
// Copyright (C) 2017 Percona LLC
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package exporter
17+
18+
import (
19+
"context"
20+
"strings"
21+
"testing"
22+
"time"
23+
24+
"github.com/prometheus/client_golang/prometheus/testutil"
25+
"github.com/sirupsen/logrus"
26+
"github.com/stretchr/testify/assert"
27+
28+
"github.com/percona/mongodb_exporter/internal/tu"
29+
)
30+
31+
func TestReplsetConfigCollector(t *testing.T) {
32+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
33+
defer cancel()
34+
35+
client := tu.DefaultTestClient(ctx, t)
36+
37+
ti := labelsGetterMock{}
38+
39+
c := newReplicationSetConfigCollector(ctx, client, logrus.New(), false, ti)
40+
41+
// The last \n at the end of this string is important
42+
expected := strings.NewReader(`
43+
# HELP mongodb_rs_cfg_protocolVersion rs_cfg.
44+
# TYPE mongodb_rs_cfg_protocolVersion untyped
45+
mongodb_rs_cfg_protocolVersion 1` + "\n")
46+
// Filter metrics for 2 reasons:
47+
// 1. The result is huge
48+
// 2. We need to check against know values. Don't use metrics that return counters like uptime
49+
// or counters like the number of transactions because they won't return a known value to compare
50+
filter := []string{
51+
"mongodb_rs_cfg_protocolVersion",
52+
}
53+
err := testutil.CollectAndCompare(c, expected, filter...)
54+
assert.NoError(t, err)
55+
}
56+
57+
func TestReplsetConfigCollectorNoSharding(t *testing.T) {
58+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
59+
defer cancel()
60+
61+
client := tu.TestClient(ctx, tu.MongoDBStandAlonePort, t)
62+
63+
ti := labelsGetterMock{}
64+
65+
c := newReplicationSetConfigCollector(ctx, client, logrus.New(), false, ti)
66+
67+
// Replication set metrics should not be generated for unsharded server
68+
count := testutil.CollectAndCount(c)
69+
70+
metaMetricCount := 1
71+
assert.Equal(t, metaMetricCount, count, "Mismatch in metric count for collector run on unsharded server")
72+
}

main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type GlobalFlags struct {
5454
EnableExporterMetrics bool `name:"collector.exporter-metrics" help:"Enable collecting metrics about the exporter itself (process_*, go_*)" negatable:"" default:"True"`
5555
EnableDiagnosticData bool `name:"collector.diagnosticdata" help:"Enable collecting metrics from getDiagnosticData"`
5656
EnableReplicasetStatus bool `name:"collector.replicasetstatus" help:"Enable collecting metrics from replSetGetStatus"`
57+
EnableReplicasetConfig bool `name:"collector.replicasetconfig" help:"Enable collecting metrics from replSetGetConfig"`
5758
EnableDBStats bool `name:"collector.dbstats" help:"Enable collecting metrics from dbStats"`
5859
EnableDBStatsFreeStorage bool `name:"collector.dbstatsfreestorage" help:"Enable collecting free space metrics from dbStats"`
5960
EnableTopMetrics bool `name:"collector.topmetrics" help:"Enable collecting metrics from top admin command"`
@@ -166,6 +167,7 @@ func buildExporter(opts GlobalFlags, uri string, log *logrus.Logger) *exporter.E
166167
DisableDefaultRegistry: !opts.EnableExporterMetrics,
167168
EnableDiagnosticData: opts.EnableDiagnosticData,
168169
EnableReplicasetStatus: opts.EnableReplicasetStatus,
170+
EnableReplicasetConfig: opts.EnableReplicasetConfig,
169171
EnableCurrentopMetrics: opts.EnableCurrentopMetrics,
170172
EnableTopMetrics: opts.EnableTopMetrics,
171173
EnableDBStats: opts.EnableDBStats,

main_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ func TestBuildExporter(t *testing.T) {
112112

113113
EnableDiagnosticData: true,
114114
EnableReplicasetStatus: true,
115+
EnableReplicasetConfig: true,
115116

116117
CompatibleMode: true,
117118
}

0 commit comments

Comments
 (0)