From 42d29148ae3ebd46819a39176261ef63e35f728f Mon Sep 17 00:00:00 2001 From: Tim Chan Date: Mon, 27 Jan 2025 13:24:01 -0800 Subject: [PATCH 1/5] Added mongodb replica metrics and routing logic for multiple mongodb instances --- .chloggen/mongodbReplicaMetrics.yaml | 27 ++ receiver/mongodbreceiver/client.go | 6 +- receiver/mongodbreceiver/client_test.go | 17 + receiver/mongodbreceiver/config.go | 23 +- receiver/mongodbreceiver/config_test.go | 4 +- receiver/mongodbreceiver/documentation.md | 48 +++ .../internal/metadata/generated_config.go | 24 ++ .../metadata/generated_config_test.go | 12 + .../internal/metadata/generated_metrics.go | 342 ++++++++++++++++++ .../metadata/generated_metrics_test.go | 90 +++++ .../internal/metadata/testdata/config.yaml | 24 ++ receiver/mongodbreceiver/metadata.yaml | 50 ++- receiver/mongodbreceiver/metrics.go | 63 +++- receiver/mongodbreceiver/scraper.go | 112 +++++- receiver/mongodbreceiver/scraper_test.go | 9 +- 15 files changed, 831 insertions(+), 20 deletions(-) create mode 100644 .chloggen/mongodbReplicaMetrics.yaml diff --git a/.chloggen/mongodbReplicaMetrics.yaml b/.chloggen/mongodbReplicaMetrics.yaml new file mode 100644 index 000000000000..4ae528669af4 --- /dev/null +++ b/.chloggen/mongodbReplicaMetrics.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: mongodbreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Added mongodb replica metrics and routing logic for multiple mongodb instances + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [37517] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/receiver/mongodbreceiver/client.go b/receiver/mongodbreceiver/client.go index 1cf92a5a2c79..ba7b2d2715b7 100644 --- a/receiver/mongodbreceiver/client.go +++ b/receiver/mongodbreceiver/client.go @@ -26,6 +26,7 @@ type client interface { DBStats(ctx context.Context, DBName string) (bson.M, error) TopStats(ctx context.Context) (bson.M, error) IndexStats(ctx context.Context, DBName, collectionName string) ([]bson.M, error) + RunCommand(ctx context.Context, db string, command bson.M) (bson.M, error) } // mongodbClient is a mongodb metric scraper client @@ -37,12 +38,11 @@ type mongodbClient struct { // newClient creates a new client to connect and query mongo for the // mongodbreceiver -func newClient(ctx context.Context, config *Config, logger *zap.Logger) (client, error) { - driver, err := mongo.Connect(ctx, config.ClientOptions()) +var newClient = func(ctx context.Context, config *Config, logger *zap.Logger, secondary bool) (client, error) { + driver, err := mongo.Connect(ctx, config.ClientOptions(secondary)) if err != nil { return nil, err } - return &mongodbClient{ cfg: config, logger: logger, diff --git a/receiver/mongodbreceiver/client_test.go b/receiver/mongodbreceiver/client_test.go index 9856e0a6389c..2e7487391a94 100644 --- a/receiver/mongodbreceiver/client_test.go +++ b/receiver/mongodbreceiver/client_test.go @@ -69,6 +69,23 @@ func (fc *fakeClient) IndexStats(ctx context.Context, dbName, collectionName str return args.Get(0).([]bson.M), args.Error(1) } +func (fc *fakeClient) RunCommand(ctx context.Context, db string, command bson.M) (bson.M, error) { + args := fc.Called(ctx, db, command) + if args.Get(0) == nil { + return nil, args.Error(1) + } + + result, ok := args.Get(0).(bson.M) + if !ok { + err := errors.New("mock returned invalid type") + zap.L().Error("type assertion failed", + zap.String("expected", "bson.M")) + return nil, err + } + + return result, args.Error(1) +} + func TestListDatabaseNames(t *testing.T) { mont := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) diff --git a/receiver/mongodbreceiver/config.go b/receiver/mongodbreceiver/config.go index 4d89797d324e..370a77ba24ec 100644 --- a/receiver/mongodbreceiver/config.go +++ b/receiver/mongodbreceiver/config.go @@ -11,6 +11,7 @@ import ( "time" "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" "go.opentelemetry.io/collector/config/confignet" "go.opentelemetry.io/collector/config/configopaque" "go.opentelemetry.io/collector/config/configtls" @@ -59,7 +60,27 @@ func (c *Config) Validate() error { return err } -func (c *Config) ClientOptions() *options.ClientOptions { +func (c *Config) ClientOptions(secondary bool) *options.ClientOptions { + if secondary { + // For secondary nodes, create a direct connection + clientOptions := options.Client(). + SetHosts(c.hostlist()). + SetDirect(true). + SetReadPreference(readpref.SecondaryPreferred()) + + if c.Timeout > 0 { + clientOptions.SetConnectTimeout(c.Timeout) + } + + if c.Username != "" && c.Password != "" { + clientOptions.SetAuth(options.Credential{ + Username: c.Username, + Password: string(c.Password), + }) + } + + return clientOptions + } clientOptions := options.Client() connString := "mongodb://" + strings.Join(c.hostlist(), ",") clientOptions.ApplyURI(connString) diff --git a/receiver/mongodbreceiver/config_test.go b/receiver/mongodbreceiver/config_test.go index 4ee7df8d87e6..98856ac35691 100644 --- a/receiver/mongodbreceiver/config_test.go +++ b/receiver/mongodbreceiver/config_test.go @@ -166,7 +166,7 @@ func TestOptions(t *testing.T) { ReplicaSet: "rs-1", } - clientOptions := cfg.ClientOptions() + clientOptions := cfg.ClientOptions(false) require.Equal(t, clientOptions.Auth.Username, cfg.Username) require.Equal(t, clientOptions.ConnectTimeout.Milliseconds(), @@ -192,7 +192,7 @@ func TestOptionsTLS(t *testing.T) { }, }, } - opts := cfg.ClientOptions() + opts := cfg.ClientOptions(false) require.NotNil(t, opts.TLSConfig) } diff --git a/receiver/mongodbreceiver/documentation.md b/receiver/mongodbreceiver/documentation.md index 1a605c560614..48f819e030bc 100644 --- a/receiver/mongodbreceiver/documentation.md +++ b/receiver/mongodbreceiver/documentation.md @@ -340,6 +340,54 @@ The number of replicated operations executed. | ---- | ----------- | ------ | | operation | The MongoDB operation being counted. | Str: ``insert``, ``query``, ``update``, ``delete``, ``getmore``, ``command`` | +### mongodb.repl_commands_per_sec + +The number of replicated commands executed per second. + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| {command}/s | Gauge | Double | + +### mongodb.repl_deletes_per_sec + +The number of replicated deletes executed per second. + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| {delete}/s | Gauge | Double | + +### mongodb.repl_getmores_per_sec + +The number of replicated getmores executed per second. + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| {getmore}/s | Gauge | Double | + +### mongodb.repl_inserts_per_sec + +The number of replicated insertions executed per second. + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| {insert}/s | Gauge | Double | + +### mongodb.repl_queries_per_sec + +The number of replicated queries executed per second. + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| {query}/s | Gauge | Double | + +### mongodb.repl_updates_per_sec + +The number of replicated updates executed per second. + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| {update}/s | Gauge | Double | + ### mongodb.uptime The amount of time that the server has been running. diff --git a/receiver/mongodbreceiver/internal/metadata/generated_config.go b/receiver/mongodbreceiver/internal/metadata/generated_config.go index ab438c54658f..7531be6f6343 100644 --- a/receiver/mongodbreceiver/internal/metadata/generated_config.go +++ b/receiver/mongodbreceiver/internal/metadata/generated_config.go @@ -55,6 +55,12 @@ type MetricsConfig struct { MongodbOperationLatencyTime MetricConfig `mapstructure:"mongodb.operation.latency.time"` MongodbOperationReplCount MetricConfig `mapstructure:"mongodb.operation.repl.count"` MongodbOperationTime MetricConfig `mapstructure:"mongodb.operation.time"` + MongodbReplCommandsPerSec MetricConfig `mapstructure:"mongodb.repl_commands_per_sec"` + MongodbReplDeletesPerSec MetricConfig `mapstructure:"mongodb.repl_deletes_per_sec"` + MongodbReplGetmoresPerSec MetricConfig `mapstructure:"mongodb.repl_getmores_per_sec"` + MongodbReplInsertsPerSec MetricConfig `mapstructure:"mongodb.repl_inserts_per_sec"` + MongodbReplQueriesPerSec MetricConfig `mapstructure:"mongodb.repl_queries_per_sec"` + MongodbReplUpdatesPerSec MetricConfig `mapstructure:"mongodb.repl_updates_per_sec"` MongodbSessionCount MetricConfig `mapstructure:"mongodb.session.count"` MongodbStorageSize MetricConfig `mapstructure:"mongodb.storage.size"` MongodbUptime MetricConfig `mapstructure:"mongodb.uptime"` @@ -143,6 +149,24 @@ func DefaultMetricsConfig() MetricsConfig { MongodbOperationTime: MetricConfig{ Enabled: true, }, + MongodbReplCommandsPerSec: MetricConfig{ + Enabled: false, + }, + MongodbReplDeletesPerSec: MetricConfig{ + Enabled: false, + }, + MongodbReplGetmoresPerSec: MetricConfig{ + Enabled: false, + }, + MongodbReplInsertsPerSec: MetricConfig{ + Enabled: false, + }, + MongodbReplQueriesPerSec: MetricConfig{ + Enabled: false, + }, + MongodbReplUpdatesPerSec: MetricConfig{ + Enabled: false, + }, MongodbSessionCount: MetricConfig{ Enabled: true, }, diff --git a/receiver/mongodbreceiver/internal/metadata/generated_config_test.go b/receiver/mongodbreceiver/internal/metadata/generated_config_test.go index 8d245c9849ef..a2d9e13f578e 100644 --- a/receiver/mongodbreceiver/internal/metadata/generated_config_test.go +++ b/receiver/mongodbreceiver/internal/metadata/generated_config_test.go @@ -52,6 +52,12 @@ func TestMetricsBuilderConfig(t *testing.T) { MongodbOperationLatencyTime: MetricConfig{Enabled: true}, MongodbOperationReplCount: MetricConfig{Enabled: true}, MongodbOperationTime: MetricConfig{Enabled: true}, + MongodbReplCommandsPerSec: MetricConfig{Enabled: true}, + MongodbReplDeletesPerSec: MetricConfig{Enabled: true}, + MongodbReplGetmoresPerSec: MetricConfig{Enabled: true}, + MongodbReplInsertsPerSec: MetricConfig{Enabled: true}, + MongodbReplQueriesPerSec: MetricConfig{Enabled: true}, + MongodbReplUpdatesPerSec: MetricConfig{Enabled: true}, MongodbSessionCount: MetricConfig{Enabled: true}, MongodbStorageSize: MetricConfig{Enabled: true}, MongodbUptime: MetricConfig{Enabled: true}, @@ -94,6 +100,12 @@ func TestMetricsBuilderConfig(t *testing.T) { MongodbOperationLatencyTime: MetricConfig{Enabled: false}, MongodbOperationReplCount: MetricConfig{Enabled: false}, MongodbOperationTime: MetricConfig{Enabled: false}, + MongodbReplCommandsPerSec: MetricConfig{Enabled: false}, + MongodbReplDeletesPerSec: MetricConfig{Enabled: false}, + MongodbReplGetmoresPerSec: MetricConfig{Enabled: false}, + MongodbReplInsertsPerSec: MetricConfig{Enabled: false}, + MongodbReplQueriesPerSec: MetricConfig{Enabled: false}, + MongodbReplUpdatesPerSec: MetricConfig{Enabled: false}, MongodbSessionCount: MetricConfig{Enabled: false}, MongodbStorageSize: MetricConfig{Enabled: false}, MongodbUptime: MetricConfig{Enabled: false}, diff --git a/receiver/mongodbreceiver/internal/metadata/generated_metrics.go b/receiver/mongodbreceiver/internal/metadata/generated_metrics.go index 38b6d26c65e9..79a2a62b88cc 100644 --- a/receiver/mongodbreceiver/internal/metadata/generated_metrics.go +++ b/receiver/mongodbreceiver/internal/metadata/generated_metrics.go @@ -1653,6 +1653,300 @@ func newMetricMongodbOperationTime(cfg MetricConfig) metricMongodbOperationTime return m } +type metricMongodbReplCommandsPerSec struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills mongodb.repl_commands_per_sec metric with initial data. +func (m *metricMongodbReplCommandsPerSec) init() { + m.data.SetName("mongodb.repl_commands_per_sec") + m.data.SetDescription("The number of replicated commands executed per second.") + m.data.SetUnit("{command}/s") + m.data.SetEmptyGauge() +} + +func (m *metricMongodbReplCommandsPerSec) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val float64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetDoubleValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMongodbReplCommandsPerSec) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricMongodbReplCommandsPerSec) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMongodbReplCommandsPerSec(cfg MetricConfig) metricMongodbReplCommandsPerSec { + m := metricMongodbReplCommandsPerSec{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricMongodbReplDeletesPerSec struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills mongodb.repl_deletes_per_sec metric with initial data. +func (m *metricMongodbReplDeletesPerSec) init() { + m.data.SetName("mongodb.repl_deletes_per_sec") + m.data.SetDescription("The number of replicated deletes executed per second.") + m.data.SetUnit("{delete}/s") + m.data.SetEmptyGauge() +} + +func (m *metricMongodbReplDeletesPerSec) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val float64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetDoubleValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMongodbReplDeletesPerSec) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricMongodbReplDeletesPerSec) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMongodbReplDeletesPerSec(cfg MetricConfig) metricMongodbReplDeletesPerSec { + m := metricMongodbReplDeletesPerSec{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricMongodbReplGetmoresPerSec struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills mongodb.repl_getmores_per_sec metric with initial data. +func (m *metricMongodbReplGetmoresPerSec) init() { + m.data.SetName("mongodb.repl_getmores_per_sec") + m.data.SetDescription("The number of replicated getmores executed per second.") + m.data.SetUnit("{getmore}/s") + m.data.SetEmptyGauge() +} + +func (m *metricMongodbReplGetmoresPerSec) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val float64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetDoubleValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMongodbReplGetmoresPerSec) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricMongodbReplGetmoresPerSec) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMongodbReplGetmoresPerSec(cfg MetricConfig) metricMongodbReplGetmoresPerSec { + m := metricMongodbReplGetmoresPerSec{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricMongodbReplInsertsPerSec struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills mongodb.repl_inserts_per_sec metric with initial data. +func (m *metricMongodbReplInsertsPerSec) init() { + m.data.SetName("mongodb.repl_inserts_per_sec") + m.data.SetDescription("The number of replicated insertions executed per second.") + m.data.SetUnit("{insert}/s") + m.data.SetEmptyGauge() +} + +func (m *metricMongodbReplInsertsPerSec) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val float64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetDoubleValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMongodbReplInsertsPerSec) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricMongodbReplInsertsPerSec) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMongodbReplInsertsPerSec(cfg MetricConfig) metricMongodbReplInsertsPerSec { + m := metricMongodbReplInsertsPerSec{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricMongodbReplQueriesPerSec struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills mongodb.repl_queries_per_sec metric with initial data. +func (m *metricMongodbReplQueriesPerSec) init() { + m.data.SetName("mongodb.repl_queries_per_sec") + m.data.SetDescription("The number of replicated queries executed per second.") + m.data.SetUnit("{query}/s") + m.data.SetEmptyGauge() +} + +func (m *metricMongodbReplQueriesPerSec) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val float64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetDoubleValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMongodbReplQueriesPerSec) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricMongodbReplQueriesPerSec) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMongodbReplQueriesPerSec(cfg MetricConfig) metricMongodbReplQueriesPerSec { + m := metricMongodbReplQueriesPerSec{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricMongodbReplUpdatesPerSec struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills mongodb.repl_updates_per_sec metric with initial data. +func (m *metricMongodbReplUpdatesPerSec) init() { + m.data.SetName("mongodb.repl_updates_per_sec") + m.data.SetDescription("The number of replicated updates executed per second.") + m.data.SetUnit("{update}/s") + m.data.SetEmptyGauge() +} + +func (m *metricMongodbReplUpdatesPerSec) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val float64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetDoubleValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMongodbReplUpdatesPerSec) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricMongodbReplUpdatesPerSec) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMongodbReplUpdatesPerSec(cfg MetricConfig) metricMongodbReplUpdatesPerSec { + m := metricMongodbReplUpdatesPerSec{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricMongodbSessionCount struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. @@ -1843,6 +2137,12 @@ type MetricsBuilder struct { metricMongodbOperationLatencyTime metricMongodbOperationLatencyTime metricMongodbOperationReplCount metricMongodbOperationReplCount metricMongodbOperationTime metricMongodbOperationTime + metricMongodbReplCommandsPerSec metricMongodbReplCommandsPerSec + metricMongodbReplDeletesPerSec metricMongodbReplDeletesPerSec + metricMongodbReplGetmoresPerSec metricMongodbReplGetmoresPerSec + metricMongodbReplInsertsPerSec metricMongodbReplInsertsPerSec + metricMongodbReplQueriesPerSec metricMongodbReplQueriesPerSec + metricMongodbReplUpdatesPerSec metricMongodbReplUpdatesPerSec metricMongodbSessionCount metricMongodbSessionCount metricMongodbStorageSize metricMongodbStorageSize metricMongodbUptime metricMongodbUptime @@ -1898,6 +2198,12 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.Settings, opt metricMongodbOperationLatencyTime: newMetricMongodbOperationLatencyTime(mbc.Metrics.MongodbOperationLatencyTime), metricMongodbOperationReplCount: newMetricMongodbOperationReplCount(mbc.Metrics.MongodbOperationReplCount), metricMongodbOperationTime: newMetricMongodbOperationTime(mbc.Metrics.MongodbOperationTime), + metricMongodbReplCommandsPerSec: newMetricMongodbReplCommandsPerSec(mbc.Metrics.MongodbReplCommandsPerSec), + metricMongodbReplDeletesPerSec: newMetricMongodbReplDeletesPerSec(mbc.Metrics.MongodbReplDeletesPerSec), + metricMongodbReplGetmoresPerSec: newMetricMongodbReplGetmoresPerSec(mbc.Metrics.MongodbReplGetmoresPerSec), + metricMongodbReplInsertsPerSec: newMetricMongodbReplInsertsPerSec(mbc.Metrics.MongodbReplInsertsPerSec), + metricMongodbReplQueriesPerSec: newMetricMongodbReplQueriesPerSec(mbc.Metrics.MongodbReplQueriesPerSec), + metricMongodbReplUpdatesPerSec: newMetricMongodbReplUpdatesPerSec(mbc.Metrics.MongodbReplUpdatesPerSec), metricMongodbSessionCount: newMetricMongodbSessionCount(mbc.Metrics.MongodbSessionCount), metricMongodbStorageSize: newMetricMongodbStorageSize(mbc.Metrics.MongodbStorageSize), metricMongodbUptime: newMetricMongodbUptime(mbc.Metrics.MongodbUptime), @@ -2018,6 +2324,12 @@ func (mb *MetricsBuilder) EmitForResource(options ...ResourceMetricsOption) { mb.metricMongodbOperationLatencyTime.emit(ils.Metrics()) mb.metricMongodbOperationReplCount.emit(ils.Metrics()) mb.metricMongodbOperationTime.emit(ils.Metrics()) + mb.metricMongodbReplCommandsPerSec.emit(ils.Metrics()) + mb.metricMongodbReplDeletesPerSec.emit(ils.Metrics()) + mb.metricMongodbReplGetmoresPerSec.emit(ils.Metrics()) + mb.metricMongodbReplInsertsPerSec.emit(ils.Metrics()) + mb.metricMongodbReplQueriesPerSec.emit(ils.Metrics()) + mb.metricMongodbReplUpdatesPerSec.emit(ils.Metrics()) mb.metricMongodbSessionCount.emit(ils.Metrics()) mb.metricMongodbStorageSize.emit(ils.Metrics()) mb.metricMongodbUptime.emit(ils.Metrics()) @@ -2187,6 +2499,36 @@ func (mb *MetricsBuilder) RecordMongodbOperationTimeDataPoint(ts pcommon.Timesta mb.metricMongodbOperationTime.recordDataPoint(mb.startTime, ts, val, operationAttributeValue.String()) } +// RecordMongodbReplCommandsPerSecDataPoint adds a data point to mongodb.repl_commands_per_sec metric. +func (mb *MetricsBuilder) RecordMongodbReplCommandsPerSecDataPoint(ts pcommon.Timestamp, val float64) { + mb.metricMongodbReplCommandsPerSec.recordDataPoint(mb.startTime, ts, val) +} + +// RecordMongodbReplDeletesPerSecDataPoint adds a data point to mongodb.repl_deletes_per_sec metric. +func (mb *MetricsBuilder) RecordMongodbReplDeletesPerSecDataPoint(ts pcommon.Timestamp, val float64) { + mb.metricMongodbReplDeletesPerSec.recordDataPoint(mb.startTime, ts, val) +} + +// RecordMongodbReplGetmoresPerSecDataPoint adds a data point to mongodb.repl_getmores_per_sec metric. +func (mb *MetricsBuilder) RecordMongodbReplGetmoresPerSecDataPoint(ts pcommon.Timestamp, val float64) { + mb.metricMongodbReplGetmoresPerSec.recordDataPoint(mb.startTime, ts, val) +} + +// RecordMongodbReplInsertsPerSecDataPoint adds a data point to mongodb.repl_inserts_per_sec metric. +func (mb *MetricsBuilder) RecordMongodbReplInsertsPerSecDataPoint(ts pcommon.Timestamp, val float64) { + mb.metricMongodbReplInsertsPerSec.recordDataPoint(mb.startTime, ts, val) +} + +// RecordMongodbReplQueriesPerSecDataPoint adds a data point to mongodb.repl_queries_per_sec metric. +func (mb *MetricsBuilder) RecordMongodbReplQueriesPerSecDataPoint(ts pcommon.Timestamp, val float64) { + mb.metricMongodbReplQueriesPerSec.recordDataPoint(mb.startTime, ts, val) +} + +// RecordMongodbReplUpdatesPerSecDataPoint adds a data point to mongodb.repl_updates_per_sec metric. +func (mb *MetricsBuilder) RecordMongodbReplUpdatesPerSecDataPoint(ts pcommon.Timestamp, val float64) { + mb.metricMongodbReplUpdatesPerSec.recordDataPoint(mb.startTime, ts, val) +} + // RecordMongodbSessionCountDataPoint adds a data point to mongodb.session.count metric. func (mb *MetricsBuilder) RecordMongodbSessionCountDataPoint(ts pcommon.Timestamp, val int64) { mb.metricMongodbSessionCount.recordDataPoint(mb.startTime, ts, val) diff --git a/receiver/mongodbreceiver/internal/metadata/generated_metrics_test.go b/receiver/mongodbreceiver/internal/metadata/generated_metrics_test.go index ff3f62821673..e0d51a2f2e36 100644 --- a/receiver/mongodbreceiver/internal/metadata/generated_metrics_test.go +++ b/receiver/mongodbreceiver/internal/metadata/generated_metrics_test.go @@ -169,6 +169,24 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordMongodbOperationTimeDataPoint(ts, 1, AttributeOperationInsert) + allMetricsCount++ + mb.RecordMongodbReplCommandsPerSecDataPoint(ts, 1) + + allMetricsCount++ + mb.RecordMongodbReplDeletesPerSecDataPoint(ts, 1) + + allMetricsCount++ + mb.RecordMongodbReplGetmoresPerSecDataPoint(ts, 1) + + allMetricsCount++ + mb.RecordMongodbReplInsertsPerSecDataPoint(ts, 1) + + allMetricsCount++ + mb.RecordMongodbReplQueriesPerSecDataPoint(ts, 1) + + allMetricsCount++ + mb.RecordMongodbReplUpdatesPerSecDataPoint(ts, 1) + defaultMetricsCount++ allMetricsCount++ mb.RecordMongodbSessionCountDataPoint(ts, 1) @@ -631,6 +649,78 @@ func TestMetricsBuilder(t *testing.T) { attrVal, ok := dp.Attributes().Get("operation") assert.True(t, ok) assert.EqualValues(t, "insert", attrVal.Str()) + case "mongodb.repl_commands_per_sec": + assert.False(t, validatedMetrics["mongodb.repl_commands_per_sec"], "Found a duplicate in the metrics slice: mongodb.repl_commands_per_sec") + validatedMetrics["mongodb.repl_commands_per_sec"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "The number of replicated commands executed per second.", ms.At(i).Description()) + assert.Equal(t, "{command}/s", ms.At(i).Unit()) + dp := ms.At(i).Gauge().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "mongodb.repl_deletes_per_sec": + assert.False(t, validatedMetrics["mongodb.repl_deletes_per_sec"], "Found a duplicate in the metrics slice: mongodb.repl_deletes_per_sec") + validatedMetrics["mongodb.repl_deletes_per_sec"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "The number of replicated deletes executed per second.", ms.At(i).Description()) + assert.Equal(t, "{delete}/s", ms.At(i).Unit()) + dp := ms.At(i).Gauge().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "mongodb.repl_getmores_per_sec": + assert.False(t, validatedMetrics["mongodb.repl_getmores_per_sec"], "Found a duplicate in the metrics slice: mongodb.repl_getmores_per_sec") + validatedMetrics["mongodb.repl_getmores_per_sec"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "The number of replicated getmores executed per second.", ms.At(i).Description()) + assert.Equal(t, "{getmore}/s", ms.At(i).Unit()) + dp := ms.At(i).Gauge().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "mongodb.repl_inserts_per_sec": + assert.False(t, validatedMetrics["mongodb.repl_inserts_per_sec"], "Found a duplicate in the metrics slice: mongodb.repl_inserts_per_sec") + validatedMetrics["mongodb.repl_inserts_per_sec"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "The number of replicated insertions executed per second.", ms.At(i).Description()) + assert.Equal(t, "{insert}/s", ms.At(i).Unit()) + dp := ms.At(i).Gauge().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "mongodb.repl_queries_per_sec": + assert.False(t, validatedMetrics["mongodb.repl_queries_per_sec"], "Found a duplicate in the metrics slice: mongodb.repl_queries_per_sec") + validatedMetrics["mongodb.repl_queries_per_sec"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "The number of replicated queries executed per second.", ms.At(i).Description()) + assert.Equal(t, "{query}/s", ms.At(i).Unit()) + dp := ms.At(i).Gauge().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "mongodb.repl_updates_per_sec": + assert.False(t, validatedMetrics["mongodb.repl_updates_per_sec"], "Found a duplicate in the metrics slice: mongodb.repl_updates_per_sec") + validatedMetrics["mongodb.repl_updates_per_sec"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "The number of replicated updates executed per second.", ms.At(i).Description()) + assert.Equal(t, "{update}/s", ms.At(i).Unit()) + dp := ms.At(i).Gauge().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) case "mongodb.session.count": assert.False(t, validatedMetrics["mongodb.session.count"], "Found a duplicate in the metrics slice: mongodb.session.count") validatedMetrics["mongodb.session.count"] = true diff --git a/receiver/mongodbreceiver/internal/metadata/testdata/config.yaml b/receiver/mongodbreceiver/internal/metadata/testdata/config.yaml index 5f97ca6b4081..69facb8fd75e 100644 --- a/receiver/mongodbreceiver/internal/metadata/testdata/config.yaml +++ b/receiver/mongodbreceiver/internal/metadata/testdata/config.yaml @@ -55,6 +55,18 @@ all_set: enabled: true mongodb.operation.time: enabled: true + mongodb.repl_commands_per_sec: + enabled: true + mongodb.repl_deletes_per_sec: + enabled: true + mongodb.repl_getmores_per_sec: + enabled: true + mongodb.repl_inserts_per_sec: + enabled: true + mongodb.repl_queries_per_sec: + enabled: true + mongodb.repl_updates_per_sec: + enabled: true mongodb.session.count: enabled: true mongodb.storage.size: @@ -124,6 +136,18 @@ none_set: enabled: false mongodb.operation.time: enabled: false + mongodb.repl_commands_per_sec: + enabled: false + mongodb.repl_deletes_per_sec: + enabled: false + mongodb.repl_getmores_per_sec: + enabled: false + mongodb.repl_inserts_per_sec: + enabled: false + mongodb.repl_queries_per_sec: + enabled: false + mongodb.repl_updates_per_sec: + enabled: false mongodb.session.count: enabled: false mongodb.storage.size: diff --git a/receiver/mongodbreceiver/metadata.yaml b/receiver/mongodbreceiver/metadata.yaml index b95816191cef..aa9083314fb4 100644 --- a/receiver/mongodbreceiver/metadata.yaml +++ b/receiver/mongodbreceiver/metadata.yaml @@ -357,7 +357,55 @@ metrics: value_type: int monotonic: true aggregation_temporality: cumulative - attributes: [ ] + attributes: [ ] + mongodb.repl_queries_per_sec: + description: The number of replicated queries executed per second. + unit: "{query}/s" + enabled: false + gauge: + value_type: double + aggregation_temporality: delta + monotonic: false + mongodb.repl_inserts_per_sec: + description: The number of replicated insertions executed per second. + unit: "{insert}/s" + enabled: false + gauge: + value_type: double + aggregation_temporality: delta + monotonic: false + mongodb.repl_commands_per_sec: + description: The number of replicated commands executed per second. + unit: "{command}/s" + enabled: false + gauge: + value_type: double + aggregation_temporality: delta + monotonic: false + mongodb.repl_getmores_per_sec: + description: The number of replicated getmores executed per second. + unit: "{getmore}/s" + enabled: false + gauge: + value_type: double + aggregation_temporality: delta + monotonic: false + mongodb.repl_deletes_per_sec: + description: The number of replicated deletes executed per second. + unit: "{delete}/s" + enabled: false + gauge: + value_type: double + aggregation_temporality: delta + monotonic: false + mongodb.repl_updates_per_sec: + description: The number of replicated updates executed per second. + unit: "{update}/s" + enabled: false + gauge: + value_type: double + aggregation_temporality: delta + monotonic: false tests: config: diff --git a/receiver/mongodbreceiver/metrics.go b/receiver/mongodbreceiver/metrics.go index 6c12cceb60eb..01825acf0211 100644 --- a/receiver/mongodbreceiver/metrics.go +++ b/receiver/mongodbreceiver/metrics.go @@ -4,6 +4,7 @@ package mongodbreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/mongodbreceiver" import ( + "context" "errors" "fmt" "reflect" @@ -12,6 +13,7 @@ import ( "go.mongodb.org/mongo-driver/bson" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/scraper/scrapererror" + "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/mongodbreceiver/internal/metadata" ) @@ -233,15 +235,74 @@ func (s *mongodbScraper) recordOperations(now pcommon.Timestamp, doc bson.M, err } func (s *mongodbScraper) recordOperationsRepl(now pcommon.Timestamp, doc bson.M, errs *scrapererror.ScrapeErrors) { + var replDoc bson.M = doc + var highestInsertCount int64 = -1 + + if len(s.secondaryClients) > 0 { + ctx := context.Background() + for _, secondaryClient := range s.secondaryClients { + status, err := secondaryClient.ServerStatus(ctx, "admin") + if err != nil { + s.logger.Debug("Failed to get secondary server status", zap.Error(err)) + continue + } + + if opcountersRepl, ok := status["opcountersRepl"].(bson.M); ok { + if insertCount, ok := opcountersRepl["insert"].(int64); ok { + if insertCount > highestInsertCount { + highestInsertCount = insertCount + replDoc = status + } + } + } + } + } + + currentCounts := make(map[string]int64) for operationVal, operation := range metadata.MapAttributeOperation { metricPath := []string{"opcountersRepl", operationVal} metricName := "mongodb.operation.repl.count" - val, err := collectMetric(doc, metricPath) + val, err := collectMetric(replDoc, metricPath) if err != nil { errs.AddPartial(1, fmt.Errorf(collectMetricWithAttributes, metricName, operationVal, err)) continue } s.mb.RecordMongodbOperationReplCountDataPoint(now, val, operation) + + currentCounts[operationVal] = val + s.recordReplOperationPerSecond(now, operationVal, val) + } + + s.prevReplCounts = currentCounts + s.prevReplTimestamp = now +} + +func (s *mongodbScraper) recordReplOperationPerSecond(now pcommon.Timestamp, operationVal string, currentCount int64) { + if s.prevReplTimestamp > 0 { + timeDelta := float64(now-s.prevReplTimestamp) / 1e9 + if timeDelta > 0 { + if prevReplCount, exists := s.prevReplCounts[operationVal]; exists { + delta := currentCount - prevReplCount + queriesPerSec := float64(delta) / timeDelta + + switch operationVal { + case "query": + s.mb.RecordMongodbReplQueriesPerSecDataPoint(now, queriesPerSec) + case "insert": + s.mb.RecordMongodbReplInsertsPerSecDataPoint(now, queriesPerSec) + case "command": + s.mb.RecordMongodbReplCommandsPerSecDataPoint(now, queriesPerSec) + case "getmore": + s.mb.RecordMongodbReplGetmoresPerSecDataPoint(now, queriesPerSec) + case "delete": + s.mb.RecordMongodbReplDeletesPerSecDataPoint(now, queriesPerSec) + case "update": + s.mb.RecordMongodbReplUpdatesPerSecDataPoint(now, queriesPerSec) + default: + fmt.Printf("Unhandled repl operation: %s\n", operationVal) + } + } + } } } diff --git a/receiver/mongodbreceiver/scraper.go b/receiver/mongodbreceiver/scraper.go index 234884d07523..b2f67730b0ce 100644 --- a/receiver/mongodbreceiver/scraper.go +++ b/receiver/mongodbreceiver/scraper.go @@ -13,7 +13,9 @@ import ( "github.com/hashicorp/go-version" "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/confignet" "go.opentelemetry.io/collector/featuregate" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" @@ -37,34 +39,81 @@ var ( ) type mongodbScraper struct { - logger *zap.Logger - config *Config - client client - mongoVersion *version.Version - mb *metadata.MetricsBuilder + logger *zap.Logger + config *Config + client client + secondaryClients []client + mongoVersion *version.Version + mb *metadata.MetricsBuilder + prevReplTimestamp pcommon.Timestamp + prevReplCounts map[string]int64 } func newMongodbScraper(settings receiver.Settings, config *Config) *mongodbScraper { return &mongodbScraper{ - logger: settings.Logger, - config: config, - mb: metadata.NewMetricsBuilder(config.MetricsBuilderConfig, settings), - mongoVersion: unknownVersion(), + logger: settings.Logger, + config: config, + mb: metadata.NewMetricsBuilder(config.MetricsBuilderConfig, settings), + mongoVersion: unknownVersion(), + prevReplTimestamp: pcommon.Timestamp(0), + prevReplCounts: make(map[string]int64), } } func (s *mongodbScraper) start(ctx context.Context, _ component.Host) error { - c, err := newClient(ctx, s.config, s.logger) + c, err := newClient(ctx, s.config, s.logger, false) if err != nil { return fmt.Errorf("create mongo client: %w", err) } s.client = c + + // Skip secondary host discovery if direct connection is enabled + if s.config.DirectConnection { + return nil + } + + secondaries, err := s.findSecondaryHosts(ctx) + if err != nil { + s.logger.Warn("failed to find secondary hosts", zap.Error(err)) + return nil + } + + for _, secondary := range secondaries { + secondaryConfig := *s.config + secondaryConfig.Hosts = []confignet.TCPAddrConfig{ + { + Endpoint: secondary, + }, + } + + client, err := newClient(ctx, &secondaryConfig, s.logger, true) + if err != nil { + s.logger.Warn("failed to connect to secondary", zap.String("host", secondary), zap.Error(err)) + continue + } + s.secondaryClients = append(s.secondaryClients, client) + } + return nil } func (s *mongodbScraper) shutdown(ctx context.Context) error { + var errs []error + if s.client != nil { - return s.client.Disconnect(ctx) + if err := s.client.Disconnect(ctx); err != nil { + errs = append(errs, err) + } + } + + for _, client := range s.secondaryClients { + if err := client.Disconnect(ctx); err != nil { + errs = append(errs, err) + } + } + + if len(errs) > 0 { + return fmt.Errorf("multiple disconnect errors: %v", errs) } return nil } @@ -231,3 +280,44 @@ func serverAddressAndPort(serverStatus bson.M) (string, int64, error) { return "", 0, fmt.Errorf("unexpected host format: %s", host) } } + +func (s *mongodbScraper) findSecondaryHosts(ctx context.Context) ([]string, error) { + result, err := s.client.RunCommand(ctx, "admin", bson.M{"replSetGetStatus": 1}) + if err != nil { + s.logger.Error("Failed to get replica set status", zap.Error(err)) + return nil, fmt.Errorf("failed to get replica set status: %w", err) + } + + members, ok := result["members"].(primitive.A) + if !ok { + return nil, fmt.Errorf("invalid members format: expected type primitive.A but got %T, value: %v", result["members"], result["members"]) + } + + var hosts []string + for _, member := range members { + m, ok := member.(bson.M) + if !ok { + continue + } + + state, ok := m["stateStr"].(string) + if !ok { + continue + } + + name, ok := m["name"].(string) + if !ok { + continue + } + + // Only add actual secondaries, not arbiters or other states + if state == "SECONDARY" { + s.logger.Debug("Found secondary", + zap.String("host", name), + zap.String("state", state)) + hosts = append(hosts, name) + } + } + + return hosts, nil +} diff --git a/receiver/mongodbreceiver/scraper_test.go b/receiver/mongodbreceiver/scraper_test.go index 2b327f20ea50..2c4337cb6d40 100644 --- a/receiver/mongodbreceiver/scraper_test.go +++ b/receiver/mongodbreceiver/scraper_test.go @@ -41,7 +41,14 @@ func TestScraperLifecycle(t *testing.T) { f := NewFactory() cfg := f.CreateDefaultConfig().(*Config) - scraper := newMongodbScraper(receivertest.NewNopSettings(metadata.Type), cfg) + /* NOTE: + setting direct connection to true because originally, the scraper tests only ONE mongodb instance. + added in routing logic to detect multiple mongodb instances which takes longer than 2 milliseconds. + since this test is testing for lifecycle (start and shutting down ONE instance). + */ + cfg.DirectConnection = true + + scraper := newMongodbScraper(receivertest.NewNopSettings(), cfg) require.NoError(t, scraper.start(context.Background(), componenttest.NewNopHost())) require.NoError(t, scraper.shutdown(context.Background())) From 4bf23f97b2bc43829d0242e414414e59a4df3a2e Mon Sep 17 00:00:00 2001 From: Tim Chan Date: Fri, 28 Feb 2025 10:11:29 -0800 Subject: [PATCH 2/5] fixed go mod tidy error from updated branch --- receiver/mongodbreceiver/scraper_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/receiver/mongodbreceiver/scraper_test.go b/receiver/mongodbreceiver/scraper_test.go index 2c4337cb6d40..eca60eee09b7 100644 --- a/receiver/mongodbreceiver/scraper_test.go +++ b/receiver/mongodbreceiver/scraper_test.go @@ -41,7 +41,8 @@ func TestScraperLifecycle(t *testing.T) { f := NewFactory() cfg := f.CreateDefaultConfig().(*Config) - /* NOTE: + /* + NOTE: setting direct connection to true because originally, the scraper tests only ONE mongodb instance. added in routing logic to detect multiple mongodb instances which takes longer than 2 milliseconds. since this test is testing for lifecycle (start and shutting down ONE instance). From d7a006f477996890d9c0c3b1ea50f26360957f0a Mon Sep 17 00:00:00 2001 From: Tim Chan Date: Fri, 28 Feb 2025 10:20:13 -0800 Subject: [PATCH 3/5] fixed lint --- receiver/mongodbreceiver/scraper_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/receiver/mongodbreceiver/scraper_test.go b/receiver/mongodbreceiver/scraper_test.go index eca60eee09b7..8c85e70e6db1 100644 --- a/receiver/mongodbreceiver/scraper_test.go +++ b/receiver/mongodbreceiver/scraper_test.go @@ -49,7 +49,7 @@ func TestScraperLifecycle(t *testing.T) { */ cfg.DirectConnection = true - scraper := newMongodbScraper(receivertest.NewNopSettings(), cfg) + scraper := newMongodbScraper(receivertest.NewNopSettings(metadata.Type), cfg) require.NoError(t, scraper.start(context.Background(), componenttest.NewNopHost())) require.NoError(t, scraper.shutdown(context.Background())) From ade4881b5cfb17fbcb9caaae1836aae657d0e878 Mon Sep 17 00:00:00 2001 From: Tim Chan Date: Fri, 28 Feb 2025 10:37:44 -0800 Subject: [PATCH 4/5] fixed go mod tidy --- receiver/mongodbreceiver/go.mod | 3 ++- receiver/mongodbreceiver/go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/receiver/mongodbreceiver/go.mod b/receiver/mongodbreceiver/go.mod index 0cd092f3fa65..4f537a7319e7 100644 --- a/receiver/mongodbreceiver/go.mod +++ b/receiver/mongodbreceiver/go.mod @@ -55,6 +55,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/knadh/koanf/maps v0.1.1 // indirect @@ -110,7 +111,7 @@ require ( golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb // indirect google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/receiver/mongodbreceiver/go.sum b/receiver/mongodbreceiver/go.sum index 5ebe79d49b50..86f2a8258c58 100644 --- a/receiver/mongodbreceiver/go.sum +++ b/receiver/mongodbreceiver/go.sum @@ -54,8 +54,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -280,10 +280,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E= -google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/genproto/googleapis/api v0.0.0-20241219192143-6b3ec007d9bb h1:B7GIB7sr443wZ/EAEl7VZjmh1V6qzkt5V+RYcUYtS1U= +google.golang.org/genproto/googleapis/api v0.0.0-20241219192143-6b3ec007d9bb/go.mod h1:E5//3O5ZIG2l71Xnt+P/CYUY8Bxs8E7WMoZ9tlcMbAY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb h1:3oy2tynMOP1QbTC0MsNNAV+Se8M2Bd0A5+x1QHyw+pI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= From 6ba451e5536257ccfabc3ece08b3ee02a32ed098 Mon Sep 17 00:00:00 2001 From: Tim Chan Date: Fri, 28 Feb 2025 12:46:04 -0800 Subject: [PATCH 5/5] fix google apis go mod tidy? --- receiver/mongodbreceiver/go.mod | 2 +- receiver/mongodbreceiver/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/receiver/mongodbreceiver/go.mod b/receiver/mongodbreceiver/go.mod index 4f537a7319e7..bae13681c18c 100644 --- a/receiver/mongodbreceiver/go.mod +++ b/receiver/mongodbreceiver/go.mod @@ -111,7 +111,7 @@ require ( golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/receiver/mongodbreceiver/go.sum b/receiver/mongodbreceiver/go.sum index 86f2a8258c58..823b246d5568 100644 --- a/receiver/mongodbreceiver/go.sum +++ b/receiver/mongodbreceiver/go.sum @@ -282,8 +282,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/api v0.0.0-20241219192143-6b3ec007d9bb h1:B7GIB7sr443wZ/EAEl7VZjmh1V6qzkt5V+RYcUYtS1U= google.golang.org/genproto/googleapis/api v0.0.0-20241219192143-6b3ec007d9bb/go.mod h1:E5//3O5ZIG2l71Xnt+P/CYUY8Bxs8E7WMoZ9tlcMbAY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb h1:3oy2tynMOP1QbTC0MsNNAV+Se8M2Bd0A5+x1QHyw+pI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=