Skip to content

Commit

Permalink
Merge branch 'main' into PMM-7-use-image-from-env-for-encrypted
Browse files Browse the repository at this point in the history
  • Loading branch information
BupycHuk committed Oct 5, 2023
2 parents 8eb2882 + 4cbba0f commit c099cc1
Show file tree
Hide file tree
Showing 11 changed files with 540 additions and 8 deletions.
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,25 @@ Connecting user should have sufficient rights to query needed stats:
More info about roles in MongoDB [documentation](https://docs.mongodb.com/manual/reference/built-in-roles/#mongodb-authrole-clusterMonitor).

#### Example
```
```sh
mongodb_exporter_linux_amd64/mongodb_exporter --mongodb.uri=mongodb://127.0.0.1:17001
```

#### MongoDB Authentication
You can supply the mongodb user/password direct in the `--mongodb.uri=` like `--mongodb.uri=mongodb://user:[email protected]:17001`, you can also supply the mongodb user/password with `--mongodb.user=`, `--mongodb.password=`
but the user and password info will be leaked via `ps` or `top` command, for security issue, you can use `MONGODB_USER` and `MONGODB_PASSWORD` env variable to set user/password for given uri
```sh
MONGODB_USER=XXX MONGODB_PASSWORD=YYY mongodb_exporter_linux_amd64/mongodb_exporter --mongodb.uri=mongodb://127.0.0.1:17001 --mongodb.collstats-colls=db1.c1,db2.c2
# or
export MONGODB_USER=XXX
export MONGODB_PASSWORD=YYY
mongodb_exporter_linux_amd64/mongodb_exporter --mongodb.uri=mongodb://127.0.0.1:17001 --mongodb.collstats-colls=db1.c1,db2.c2
```

#### Enabling collstats metrics gathering
`--mongodb.collstats-colls` receives a list of databases and collections to monitor using collstats.
Usage example: `--mongodb.collstats-colls=database1.collection1,database2.collection2`
```
```sh
mongodb_exporter_linux_amd64/mongodb_exporter --mongodb.uri=mongodb://127.0.0.1:17001 --mongodb.collstats-colls=db1.c1,db2.c2
```
#### Enabling compatibility mode.
Expand All @@ -95,6 +106,16 @@ HELP mongodb_mongod_wiredtiger_log_bytes_total mongodb_mongod_wiredtiger_log_byt
# TYPE mongodb_mongod_wiredtiger_log_bytes_total untyped
mongodb_mongod_wiredtiger_log_bytes_total{type="unwritten"} 2.6208e+06
```
#### Enabling profile metrics gathering
`--collector.profile`
To collect metrics, you need to enable the profiler in [MongoDB](https://www.mongodb.com/docs/manual/tutorial/manage-the-database-profiler/):
Usage example: `db.setProfilingLevel(2)`

|Level|Description|
|-----|-----------|
|0| The profiler is off and does not collect any data. This is the default profiler level.|
|1| The profiler collects data for operations that take longer than the value of `slowms` or that match a filter.<br> When a filter is set: <ul><li> The `slowms` and `sampleRate` options are not used for profiling.</li><li>The profiler only captures operations that match the filter.</li></ul>
|2|The profiler collects data for all operations.|

#### Cluster role labels
The exporter sets some topology labels in all metrics.
Expand Down
3 changes: 3 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
|--collector.replicasetstatus|Enable collecting metrics from replSetGetStatus|
|--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|
|--collector.indexstats|Enable collecting metrics from $indexStats|
|--collector.collstats|Enable collecting metrics from $collStats|
|--collect-all|Enable all collectors. Same as specifying all --collector.\<name\>|
|--collector.collstats-limit=0|Disable collstats, dbstats, topmetrics and indexstats collector if there are more than \<n\> collections. 0=No limit|
|--collector.profile-time-ts=30|Set time for scrape slow queries| This interval must be synchronized with the Prometheus scrape interval|
|--collector.profile|Enable collecting metrics from profile|
|--metrics.overridedescendingindex| Enable descending index name override to replace -1 with _DESC ||
|--version|Show version and exit|
144 changes: 144 additions & 0 deletions exporter/currentop_collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// mongodb_exporter
// Copyright (C) 2017 Percona LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package exporter

import (
"context"
"strconv"

"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/sirupsen/logrus"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)

type currentopCollector struct {
ctx context.Context
base *baseCollector
compatibleMode bool
topologyInfo labelsGetter
}

var ErrInvalidOrMissingInprogEntry = errors.New("invalid or missing inprog entry in currentop results")

// newCurrentopCollector creates a collector for being processed queries.
func newCurrentopCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger,
compatible bool, topology labelsGetter,
) *currentopCollector {
return &currentopCollector{
ctx: ctx,
base: newBaseCollector(client, logger),
compatibleMode: compatible,
topologyInfo: topology,
}
}

func (d *currentopCollector) Describe(ch chan<- *prometheus.Desc) {
d.base.Describe(d.ctx, ch, d.collect)
}

func (d *currentopCollector) Collect(ch chan<- prometheus.Metric) {
d.base.Collect(ch)
}

func (d *currentopCollector) collect(ch chan<- prometheus.Metric) {
defer measureCollectTime(ch, "mongodb", "currentop")()

logger := d.base.logger
client := d.base.client

// Get all requests that are being processed except system requests (admin and local).
cmd := bson.D{
{Key: "currentOp", Value: true},
{Key: "active", Value: true},
{Key: "microsecs_running", Value: bson.D{
{Key: "$exists", Value: true},
}},
{Key: "op", Value: bson.D{{Key: "$ne", Value: ""}}},
{Key: "ns", Value: bson.D{
{Key: "$ne", Value: ""},
{Key: "$not", Value: bson.D{{Key: "$regex", Value: "^admin.*|^local.*"}}},
}},
}
res := client.Database("admin").RunCommand(d.ctx, cmd)

var r primitive.M
if err := res.Decode(&r); err != nil {
ch <- prometheus.NewInvalidMetric(prometheus.NewInvalidDesc(err), err)
return
}

logger.Debug("currentop response from MongoDB:")
debugResult(logger, r)

inprog, ok := r["inprog"].(primitive.A)

if !ok {
ch <- prometheus.NewInvalidMetric(prometheus.NewInvalidDesc(ErrInvalidOrMissingInprogEntry),
ErrInvalidOrMissingInprogEntry)
}

for _, bsonMap := range inprog {

bsonMapElement, ok := bsonMap.(primitive.M)
if !ok {
logger.Errorf("Invalid type primitive.M assertion for bsonMap: %T", bsonMapElement)
continue
}
opid, ok := bsonMapElement["opid"].(int32)
if !ok {
logger.Errorf("Invalid type int32 assertion for 'opid': %T", bsonMapElement)
continue
}
namespace, ok := bsonMapElement["ns"].(string)
if !ok {
logger.Errorf("Invalid type string assertion for 'ns': %T", bsonMapElement)
continue
}
db, collection := splitNamespace(namespace)
op, ok := bsonMapElement["op"].(string)
if !ok {
logger.Errorf("Invalid type string assertion for 'op': %T", bsonMapElement)
continue
}
desc, ok := bsonMapElement["desc"].(string)
if !ok {
logger.Errorf("Invalid type string assertion for 'desc': %T", bsonMapElement)
continue
}
microsecs_running, ok := bsonMapElement["microsecs_running"].(int64)
if !ok {
logger.Errorf("Invalid type int64 assertion for 'microsecs_running': %T", bsonMapElement)
continue
}

labels := d.topologyInfo.baseLabels()
labels["opid"] = strconv.Itoa(int(opid))
labels["op"] = op
labels["desc"] = desc
labels["database"] = db
labels["collection"] = collection
labels["ns"] = namespace

m := primitive.M{"uptime": microsecs_running}

for _, metric := range makeMetrics("currentop_query", m, labels, d.compatibleMode) {
ch <- metric
}
}
}
77 changes: 77 additions & 0 deletions exporter/currentop_collector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// mongodb_exporter
// Copyright (C) 2017 Percona LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package exporter

import (
"context"
"fmt"
"sync"
"testing"
"time"

"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"go.mongodb.org/mongo-driver/bson"

"github.com/percona/mongodb_exporter/internal/tu"
)

func TestCurrentopCollector(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

var wg sync.WaitGroup

client := tu.DefaultTestClient(ctx, t)

database := client.Database("testdb")
database.Drop(ctx)

defer func() {
err := database.Drop(ctx)
assert.NoError(t, err)
}()
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < 3; i++ {
coll := fmt.Sprintf("testcol_%02d", i)
_, err := database.Collection(coll).InsertOne(ctx, bson.M{"f1": 1, "f2": "2"})
assert.NoError(t, err)
}
}()

ti := labelsGetterMock{}

c := newCurrentopCollector(ctx, client, logrus.New(), false, ti)

// Filter metrics by reason:
// 1. The result will be different on different hardware
// 2. Can't check labels like 'decs' and 'opid' because they don't return a known value for comparison
// It looks like:
// # HELP mongodb_currentop_query_uptime currentop_query.
// # TYPE mongodb_currentop_query_uptime untyped
// mongodb_currentop_query_uptime{collection="testcol_00",database="testdb",decs="conn6365",ns="testdb.testcol_00",op="insert",opid="448307"} 2524

filter := []string{
"mongodb_currentop_query_uptime",
}

count := testutil.CollectAndCount(c, filter...)
assert.True(t, count > 0)
wg.Wait()
}
23 changes: 23 additions & 0 deletions exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,18 @@ type Opts struct {
DisableDefaultRegistry bool
DiscoveringMode bool
GlobalConnPool bool
ProfileTimeTS int

CollectAll bool
EnableDBStats bool
EnableDBStatsFreeStorage bool
EnableDiagnosticData bool
EnableReplicasetStatus bool
EnableCurrentopMetrics bool
EnableTopMetrics bool
EnableIndexStats bool
EnableCollStats bool
EnableProfile bool

EnableOverrideDescendingIndex bool

Expand Down Expand Up @@ -170,6 +173,8 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol
e.opts.EnableTopMetrics = true
e.opts.EnableReplicasetStatus = true
e.opts.EnableIndexStats = true
e.opts.EnableCurrentopMetrics = true
e.opts.EnableProfile = true
}

// arbiter only have isMaster privileges
Expand All @@ -180,6 +185,8 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol
e.opts.EnableTopMetrics = false
e.opts.EnableReplicasetStatus = false
e.opts.EnableIndexStats = false
e.opts.EnableCurrentopMetrics = false
e.opts.EnableProfile = false
}

// If we manually set the collection names we want or auto discovery is set.
Expand Down Expand Up @@ -210,6 +217,18 @@ func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topol
registry.MustRegister(cc)
}

if e.opts.EnableCurrentopMetrics && nodeType != typeMongos && limitsOk && requestOpts.EnableCurrentopMetrics {
coc := newCurrentopCollector(ctx, client, e.opts.Logger,
e.opts.CompatibleMode, topologyInfo)
registry.MustRegister(coc)
}

if e.opts.EnableProfile && nodeType != typeMongos && limitsOk && requestOpts.EnableProfile && e.opts.ProfileTimeTS != 0 {
pc := newProfileCollector(ctx, client, e.opts.Logger,
e.opts.CompatibleMode, topologyInfo, e.opts.ProfileTimeTS)
registry.MustRegister(pc)
}

if e.opts.EnableTopMetrics && nodeType != typeMongos && limitsOk && requestOpts.EnableTopMetrics {
tc := newTopCollector(ctx, client, e.opts.Logger,
e.opts.CompatibleMode, topologyInfo)
Expand Down Expand Up @@ -288,10 +307,14 @@ func (e *Exporter) Handler() http.Handler {
requestOpts.EnableDBStats = true
case "topmetrics":
requestOpts.EnableTopMetrics = true
case "currentopmetrics":
requestOpts.EnableCurrentopMetrics = true
case "indexstats":
requestOpts.EnableIndexStats = true
case "collstats":
requestOpts.EnableCollStats = true
case "profile":
requestOpts.EnableProfile = true
}
}

Expand Down
4 changes: 3 additions & 1 deletion exporter/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,9 @@ func asFloat64(value interface{}) (*float64, error) {
f = v
case primitive.DateTime:
f = float64(v)
case primitive.A, primitive.ObjectID, primitive.Timestamp, primitive.Binary, string, []uint8, time.Time:
case primitive.Timestamp:
f = float64(v.T)
case primitive.A, primitive.ObjectID, primitive.Binary, string, []uint8, time.Time:
return nil, nil
default:
return nil, errors.Wrapf(errCannotHandleType, "%T", v)
Expand Down
2 changes: 1 addition & 1 deletion exporter/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func TestMakeRawMetric(t *testing.T) {
{value: float32(1.23), wantVal: pointer.ToFloat64(float64(float32(1.23)))},
{value: float64(1.23), wantVal: pointer.ToFloat64(1.23)},
{value: primitive.A{}, wantVal: nil},
{value: primitive.Timestamp{}, wantVal: nil},
{value: primitive.Timestamp{T: 123, I: 456}, wantVal: pointer.ToFloat64(123)},
{value: "zapp", wantVal: nil},
{value: []byte{}, wantVal: nil},
{value: time.Date(2020, 6, 15, 0, 0, 0, 0, time.UTC), wantVal: nil},
Expand Down
Loading

0 comments on commit c099cc1

Please sign in to comment.