-
Notifications
You must be signed in to change notification settings - Fork 428
Commit
- Loading branch information
There are no files selected for viewing
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" | ||
"fmt" | ||
"strconv" | ||
|
||
"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 | ||
Check failure on line 31 in exporter/currentop_collector.go GitHub Actions / Lint Check
|
||
base *baseCollector | ||
compatibleMode bool | ||
topologyInfo labelsGetter | ||
} | ||
|
||
var ErrInvalidOrMissingInprogEntry = fmt.Errorf("invalid or misssing inprog entry in currentop results") | ||
Check failure on line 37 in exporter/currentop_collector.go GitHub Actions / Lint Check
|
||
|
||
// 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 ¤topCollector{ | ||
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) { | ||
Check failure on line 59 in exporter/currentop_collector.go GitHub Actions / Lint Check
|
||
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 | ||
Check failure on line 83 in exporter/currentop_collector.go GitHub Actions / Lint Check
|
||
} | ||
|
||
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 { | ||
Check failure on line 96 in exporter/currentop_collector.go GitHub Actions / Lint Check
|
||
|
||
bsonMapElement, ok := bsonMap.(primitive.M) | ||
if !ok { | ||
logger.Errorf("Invalid type primitive.M assertion for bsonMap: %t", ok) | ||
break | ||
Check failure on line 101 in exporter/currentop_collector.go GitHub Actions / Lint Check
|
||
} | ||
opid, ok := bsonMapElement["opid"].(int32) | ||
if !ok { | ||
logger.Errorf("Invalid type int32 assertion for 'opid': %t", ok) | ||
break | ||
} | ||
namespace, ok := bsonMapElement["ns"].(string) | ||
if !ok { | ||
logger.Errorf("Invalid type string assertion for 'ns': %t", ok) | ||
break | ||
} | ||
db, collection := splitNamespace(namespace) | ||
op, ok := bsonMapElement["op"].(string) | ||
if !ok { | ||
logger.Errorf("Invalid type string assertion for 'op': %t", ok) | ||
break | ||
} | ||
decs, ok := bsonMapElement["desc"].(string) | ||
if !ok { | ||
logger.Errorf("Invalid type string assertion for 'desc': %t", ok) | ||
break | ||
} | ||
microsecs_running, ok := bsonMapElement["microsecs_running"].(int64) | ||
Check failure on line 124 in exporter/currentop_collector.go GitHub Actions / Lint Check
|
||
if !ok { | ||
logger.Errorf("Invalid type int64 assertion for 'microsecs_running': %t", ok) | ||
break | ||
} | ||
|
||
labels := d.topologyInfo.baseLabels() | ||
labels["opid"] = strconv.Itoa(int(opid)) | ||
labels["op"] = op | ||
labels["decs"] = decs | ||
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 | ||
} | ||
} | ||
} |
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) | ||
Check failure on line 42 in exporter/currentop_collector_test.go GitHub Actions / Lint Check
|
||
|
||
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() | ||
} |