Skip to content
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

Collector for working with data from the system profile #710

Merged
merged 5 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@
|--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|
12 changes: 12 additions & 0 deletions exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type Opts struct {
DisableDefaultRegistry bool
DiscoveringMode bool
GlobalConnPool bool
ProfileTimeTS int

CollectAll bool
EnableDBStats bool
Expand All @@ -68,6 +69,7 @@ type Opts struct {
EnableTopMetrics bool
EnableIndexStats bool
EnableCollStats bool
EnableProfile bool

EnableOverrideDescendingIndex bool

Expand Down Expand Up @@ -170,6 +172,7 @@ 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.EnableProfile = true
}

// arbiter only have isMaster privileges
Expand All @@ -180,6 +183,7 @@ 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.EnableProfile = false
}

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

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 @@ -292,6 +302,8 @@ func (e *Exporter) Handler() http.Handler {
requestOpts.EnableIndexStats = true
case "collstats":
requestOpts.EnableCollStats = true
case "profile":
requestOpts.EnableProfile = true
}
}

Expand Down
96 changes: 96 additions & 0 deletions exporter/profile_status_collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// 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"
"time"

"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 profileCollector struct {
ctx context.Context
base *baseCollector
compatibleMode bool
topologyInfo labelsGetter
profiletimets int
}

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

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

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

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

logger := d.base.logger
client := d.base.client
timeScrape := d.profiletimets

databases, err := databases(d.ctx, client, nil, nil)
if err != nil {
errors.Wrap(err, "cannot get the database names list")
return
}

// Now time + '--collector.profile-time-ts'
ts := primitive.NewDateTimeFromTime(time.Now().Add(-time.Duration(time.Second * time.Duration(timeScrape))))

labels := d.topologyInfo.baseLabels()

// Get all slow queries from all databases
cmd := bson.M{"ts": bson.M{"$gte": ts}}
for _, db := range databases {
res, err := client.Database(db).Collection("system.profile").CountDocuments(d.ctx, cmd)
if err != nil {
errors.Wrapf(err, "cannot read system.profile")
break
}
labels["database"] = db

m := primitive.M{"count": res}

logger.Debug("profile response from MongoDB:")
debugResult(logger, primitive.M{db: m})

for _, metric := range makeMetrics("profile_slow_query", m, labels, d.compatibleMode) {
ch <- metric
}
}
}
69 changes: 69 additions & 0 deletions exporter/profile_status_collector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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"
"strings"
"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 TestProfileCollector(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

client := tu.DefaultTestClient(ctx, t)

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

defer func() {
err := database.Drop(ctx)
assert.NoError(t, err)
}()

// Enable database profiler https://www.mongodb.com/docs/manual/tutorial/manage-the-database-profiler/
cmd := bson.M{"profile": 2}
_ = database.RunCommand(ctx, cmd)

ti := labelsGetterMock{}

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

expected := strings.NewReader(`
# HELP mongodb_profile_slow_query_count profile_slow_query.
# TYPE mongodb_profile_slow_query_count counter
mongodb_profile_slow_query_count{database="admin"} 0
mongodb_profile_slow_query_count{database="config"} 0
mongodb_profile_slow_query_count{database="local"} 0
mongodb_profile_slow_query_count{database="testdb"} 0` +
"\n")

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

err := testutil.CollectAndCompare(c, expected, filter...)
assert.NoError(t, err)
}
5 changes: 5 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,16 @@ type GlobalFlags struct {
EnableTopMetrics bool `name:"collector.topmetrics" help:"Enable collecting metrics from top admin command"`
EnableIndexStats bool `name:"collector.indexstats" help:"Enable collecting metrics from $indexStats"`
EnableCollStats bool `name:"collector.collstats" help:"Enable collecting metrics from $collStats"`
EnableProfile bool `name:"collector.profile" help:"Enable collecting metrics from profile"`

EnableOverrideDescendingIndex bool `name:"metrics.overridedescendingindex" help:"Enable descending index name override to replace -1 with _DESC"`

CollectAll bool `name:"collect-all" help:"Enable all collectors. Same as specifying all --collector.<name>"`

CollStatsLimit int `name:"collector.collstats-limit" help:"Disable collstats, dbstats, topmetrics and indexstats collector if there are more than <n> collections. 0=No limit" default:"0"`

ProfileTimeTS int `name:"collector.profile-time-ts" help:"Set time for scrape slow queries." default:"30"`

DiscoveringMode bool `name:"discovering-mode" help:"Enable autodiscover collections" negatable:""`
CompatibleMode bool `name:"compatible-mode" help:"Enable old mongodb-exporter compatible metrics" negatable:""`
Version bool `name:"version" help:"Show version and exit"`
Expand Down Expand Up @@ -130,11 +133,13 @@ func buildExporter(opts GlobalFlags) *exporter.Exporter {
EnableDBStatsFreeStorage: opts.EnableDBStatsFreeStorage,
EnableIndexStats: opts.EnableIndexStats,
EnableCollStats: opts.EnableCollStats,
EnableProfile: opts.EnableProfile,

EnableOverrideDescendingIndex: opts.EnableOverrideDescendingIndex,

CollStatsLimit: opts.CollStatsLimit,
CollectAll: opts.CollectAll,
ProfileTimeTS: opts.ProfileTimeTS,
}

e := exporter.New(exporterOpts)
Expand Down