diff options
Diffstat (limited to 'src/go/collectors/go.d.plugin/modules/mongodb')
21 files changed, 4796 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/README.md b/src/go/collectors/go.d.plugin/modules/mongodb/README.md new file mode 120000 index 000000000..a28253054 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/README.md @@ -0,0 +1 @@ +integrations/mongodb.md
\ No newline at end of file diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/charts.go b/src/go/collectors/go.d.plugin/modules/mongodb/charts.go new file mode 100644 index 000000000..f1b9c1a07 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/charts.go @@ -0,0 +1,1036 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package mongo + +import ( + "github.com/netdata/netdata/go/go.d.plugin/agent/module" +) + +const ( + prioOperationsRate = module.Priority + iota + prioOperationsLatencyTime + prioOperationsByTypeRate + prioDocumentOperationsRate + prioScannedIndexesRate + prioScannedDocumentsRate + + prioActiveClientsCount + prioQueuedOperationsCount + + prioGlobalLockAcquisitionsRate + prioDatabaseLockAcquisitionsRate + prioCollectionLockAcquisitionsRate + prioMutexLockAcquisitionsRate + prioMetadataLockAcquisitionsRate + prioOpLogLockAcquisitionsRate + + prioCursorsOpenCount + prioCursorsOpenNoTimeoutCount + prioCursorsOpenedRate + prioTimedOutCursorsRate + prioCursorsByLifespanCount + + prioTransactionsCount + prioTransactionsRate + prioTransactionsNoShardsCommitsRate + prioTransactionsNoShardsCommitsDurationTime + prioTransactionsSingleShardCommitsRate + prioTransactionsSingleShardCommitsDurationTime + prioTransactionsSingleWriteShardCommitsRate + prioTransactionsSingleWriteShardCommitsDurationTime + prioTransactionsReadOnlyCommitsRate + prioTransactionsReadOnlyCommitsDurationTime + prioTransactionsTwoPhaseCommitCommitsRate + prioTransactionsTwoPhaseCommitCommitsDurationTime + prioTransactionsRecoverWithTokenCommitsRate + prioTransactionsRecoverWithTokenCommitsDurationTime + + prioConnectionsUsage + prioConnectionsByStateCount + prioConnectionsRate + + prioAssertsRate + + prioNetworkTrafficRate + prioNetworkRequestsRate + prioNetworkSlowDNSResolutionsRate + prioNetworkSlowSSLHandshakesRate + + prioMemoryResidentSize + prioMemoryVirtualSize + prioMemoryPageFaultsRate + prioMemoryTCMallocStats + + prioWiredTigerConcurrentReadTransactionsUsage + prioWiredTigerConcurrentWriteTransactionsUsage + prioWiredTigerCacheUsage + prioWiredTigerCacheDirtySpaceSize + prioWiredTigerCacheIORate + prioWiredTigerCacheEvictionsRate + + prioDatabaseCollectionsCount + prioDatabaseIndexesCount + prioDatabaseViewsCount + prioDatabaseDocumentsCount + prioDatabaseDataSize + prioDatabaseStorageSize + prioDatabaseIndexSize + + prioReplSetMemberState + prioReplSetMemberHealthStatus + prioReplSetMemberReplicationLagTime + prioReplSetMemberHeartbeatLatencyTime + prioReplSetMemberPingRTTTime + prioReplSetMemberUptime + + prioShardingNodesCount + prioShardingShardedDatabasesCount + prioShardingShardedCollectionsCount + prioShardChunks +) + +const ( + chartPxDatabase = "database_" + chartPxReplSetMember = "replica_set_member_" + chartPxShard = "sharding_shard_" +) + +// these charts are expected to be available in many versions +var chartsServerStatus = module.Charts{ + chartOperationsByTypeRate.Copy(), + chartDocumentOperationsRate.Copy(), + chartScannedIndexesRate.Copy(), + chartScannedDocumentsRate.Copy(), + + chartConnectionsUsage.Copy(), + chartConnectionsByStateCount.Copy(), + chartConnectionsRate.Copy(), + + chartNetworkTrafficRate.Copy(), + chartNetworkRequestsRate.Copy(), + + chartMemoryResidentSize.Copy(), + chartMemoryVirtualSize.Copy(), + chartMemoryPageFaultsRate.Copy(), + + chartAssertsRate.Copy(), +} + +var chartsTmplDatabase = module.Charts{ + chartTmplDatabaseCollectionsCount.Copy(), + chartTmplDatabaseIndexesCount.Copy(), + chartTmplDatabaseViewsCount.Copy(), + chartTmplDatabaseDocumentsCount.Copy(), + chartTmplDatabaseDataSize.Copy(), + chartTmplDatabaseStorageSize.Copy(), + chartTmplDatabaseIndexSize.Copy(), +} + +var chartsTmplReplSetMember = module.Charts{ + chartTmplReplSetMemberState.Copy(), + chartTmplReplSetMemberHealthStatus.Copy(), + chartTmplReplSetMemberReplicationLagTime.Copy(), + chartTmplReplSetMemberHeartbeatLatencyTime.Copy(), + chartTmplReplSetMemberPingRTTTime.Copy(), + chartTmplReplSetMemberUptime.Copy(), +} + +var chartsSharding = module.Charts{ + chartShardingNodesCount.Copy(), + chartShardingShardedDatabases.Copy(), + chartShardingShardedCollectionsCount.Copy(), +} + +var chartsTmplShardingShard = module.Charts{ + chartTmplShardChunks.Copy(), +} + +var ( + chartOperationsRate = module.Chart{ + ID: "operations_rate", + Title: "Operations rate", + Units: "operations/s", + Fam: "operations", + Ctx: "mongodb.operations_rate", + Priority: prioOperationsRate, + Dims: module.Dims{ + {ID: "operations_latencies_reads_ops", Name: "reads", Algo: module.Incremental}, + {ID: "operations_latencies_writes_ops", Name: "writes", Algo: module.Incremental}, + {ID: "operations_latencies_commands_ops", Name: "commands", Algo: module.Incremental}, + }, + } + chartOperationsLatencyTime = module.Chart{ + ID: "operations_latency_time", + Title: "Operations Latency", + Units: "milliseconds", + Fam: "operations", + Ctx: "mongodb.operations_latency_time", + Priority: prioOperationsLatencyTime, + Dims: module.Dims{ + {ID: "operations_latencies_reads_latency", Name: "reads", Algo: module.Incremental, Div: 1000}, + {ID: "operations_latencies_writes_latency", Name: "writes", Algo: module.Incremental, Div: 1000}, + {ID: "operations_latencies_commands_latency", Name: "commands", Algo: module.Incremental, Div: 1000}, + }, + } + chartOperationsByTypeRate = module.Chart{ + ID: "operations_by_type_rate", + Title: "Operations by type", + Units: "operations/s", + Fam: "operations", + Ctx: "mongodb.operations_by_type_rate", + Priority: prioOperationsByTypeRate, + Dims: module.Dims{ + {ID: "operations_insert", Name: "insert", Algo: module.Incremental}, + {ID: "operations_query", Name: "query", Algo: module.Incremental}, + {ID: "operations_update", Name: "update", Algo: module.Incremental}, + {ID: "operations_delete", Name: "delete", Algo: module.Incremental}, + {ID: "operations_getmore", Name: "getmore", Algo: module.Incremental}, + {ID: "operations_command", Name: "command", Algo: module.Incremental}, + }, + } + chartDocumentOperationsRate = module.Chart{ + ID: "document_operations_rate", + Title: "Document operations", + Units: "operations/s", + Fam: "operations", + Ctx: "mongodb.document_operations_rate", + Type: module.Stacked, + Priority: prioDocumentOperationsRate, + Dims: module.Dims{ + {ID: "metrics_document_inserted", Name: "inserted", Algo: module.Incremental}, + {ID: "metrics_document_deleted", Name: "deleted", Algo: module.Incremental}, + {ID: "metrics_document_returned", Name: "returned", Algo: module.Incremental}, + {ID: "metrics_document_updated", Name: "updated", Algo: module.Incremental}, + }, + } + chartScannedIndexesRate = module.Chart{ + ID: "scanned_indexes_rate", + Title: "Scanned indexes", + Units: "indexes/s", + Fam: "operations", + Ctx: "mongodb.scanned_indexes_rate", + Priority: prioScannedIndexesRate, + Dims: module.Dims{ + {ID: "metrics_query_executor_scanned", Name: "scanned", Algo: module.Incremental}, + }, + } + chartScannedDocumentsRate = module.Chart{ + ID: "scanned_documents_rate", + Title: "Scanned documents", + Units: "documents/s", + Fam: "operations", + Ctx: "mongodb.scanned_documents_rate", + Priority: prioScannedDocumentsRate, + Dims: module.Dims{ + {ID: "metrics_query_executor_scanned_objects", Name: "scanned", Algo: module.Incremental}, + }, + } + + chartGlobalLockActiveClientsCount = module.Chart{ + ID: "active_clients_count", + Title: "Connected clients", + Units: "clients", + Fam: "clients", + Ctx: "mongodb.active_clients_count", + Priority: prioActiveClientsCount, + Dims: module.Dims{ + {ID: "global_lock_active_clients_readers", Name: "readers"}, + {ID: "global_lock_active_clients_writers", Name: "writers"}, + }, + } + chartGlobalLockCurrentQueueCount = module.Chart{ + ID: "queued_operations", + Title: "Queued operations because of a lock", + Units: "operations", + Fam: "clients", + Ctx: "mongodb.queued_operations_count", + Priority: prioQueuedOperationsCount, + Dims: module.Dims{ + {ID: "global_lock_current_queue_readers", Name: "readers"}, + {ID: "global_lock_current_queue_writers", Name: "writers"}, + }, + } + + chartConnectionsUsage = module.Chart{ + ID: "connections_usage", + Title: "Connections usage", + Units: "connections", + Fam: "connections", + Ctx: "mongodb.connections_usage", + Type: module.Stacked, + Priority: prioConnectionsUsage, + Dims: module.Dims{ + {ID: "connections_available", Name: "available"}, + {ID: "connections_current", Name: "used"}, + }, + } + chartConnectionsByStateCount = module.Chart{ + ID: "connections_by_state_count", + Title: "Connections By State", + Units: "connections", + Fam: "connections", + Ctx: "mongodb.connections_by_state_count", + Priority: prioConnectionsByStateCount, + Dims: module.Dims{ + {ID: "connections_active", Name: "active"}, + {ID: "connections_threaded", Name: "threaded"}, + {ID: "connections_exhaust_is_master", Name: "exhaust_is_master"}, + {ID: "connections_exhaust_hello", Name: "exhaust_hello"}, + {ID: "connections_awaiting_topology_changes", Name: "awaiting_topology_changes"}, + }, + } + chartConnectionsRate = module.Chart{ + ID: "connections_rate", + Title: "Connections Rate", + Units: "connections/s", + Fam: "connections", + Ctx: "mongodb.connections_rate", + Priority: prioConnectionsRate, + Dims: module.Dims{ + {ID: "connections_total_created", Name: "created", Algo: module.Incremental}, + }, + } + + chartNetworkTrafficRate = module.Chart{ + ID: "network_traffic", + Title: "Network traffic", + Units: "bytes/s", + Fam: "network", + Ctx: "mongodb.network_traffic_rate", + Priority: prioNetworkTrafficRate, + Type: module.Area, + Dims: module.Dims{ + {ID: "network_bytes_in", Name: "in", Algo: module.Incremental}, + {ID: "network_bytes_out", Name: "out", Algo: module.Incremental}, + }, + } + chartNetworkRequestsRate = module.Chart{ + ID: "network_requests_rate", + Title: "Network Requests", + Units: "requests/s", + Fam: "network", + Ctx: "mongodb.network_requests_rate", + Priority: prioNetworkRequestsRate, + Dims: module.Dims{ + {ID: "network_requests", Name: "requests", Algo: module.Incremental}, + }, + } + chartNetworkSlowDNSResolutionsRate = module.Chart{ + ID: "network_slow_dns_resolutions_rate", + Title: "Slow DNS resolution operations", + Units: "resolutions/s", + Fam: "network", + Ctx: "mongodb.network_slow_dns_resolutions_rate", + Priority: prioNetworkSlowDNSResolutionsRate, + Dims: module.Dims{ + {ID: "network_slow_dns_operations", Name: "slow_dns", Algo: module.Incremental}, + }, + } + chartNetworkSlowSSLHandshakesRate = module.Chart{ + ID: "network_slow_ssl_handshakes_rate", + Title: "Slow SSL handshake operations", + Units: "handshakes/s", + Fam: "network", + Ctx: "mongodb.network_slow_ssl_handshakes_rate", + Priority: prioNetworkSlowSSLHandshakesRate, + Dims: module.Dims{ + {ID: "network_slow_ssl_operations", Name: "slow_ssl", Algo: module.Incremental}, + }, + } + + chartMemoryResidentSize = module.Chart{ + ID: "memory_resident_size", + Title: "Used resident memory", + Units: "bytes", + Fam: "memory", + Ctx: "mongodb.memory_resident_size", + Priority: prioMemoryResidentSize, + Dims: module.Dims{ + {ID: "memory_resident", Name: "used"}, + }, + } + chartMemoryVirtualSize = module.Chart{ + ID: "memory_virtual_size", + Title: "Used virtual memory", + Units: "bytes", + Fam: "memory", + Ctx: "mongodb.memory_virtual_size", + Priority: prioMemoryVirtualSize, + Dims: module.Dims{ + {ID: "memory_virtual", Name: "used"}, + }, + } + chartMemoryPageFaultsRate = module.Chart{ + ID: "memory_page_faults", + Title: "Memory page faults", + Units: "pgfaults/s", + Fam: "memory", + Ctx: "mongodb.memory_page_faults_rate", + Priority: prioMemoryPageFaultsRate, + Dims: module.Dims{ + {ID: "extra_info_page_faults", Name: "pgfaults", Algo: module.Incremental}, + }, + } + chartMemoryTCMallocStatsChart = module.Chart{ + ID: "memory_tcmalloc_stats", + Title: "TCMalloc statistics", + Units: "bytes", + Fam: "memory", + Ctx: "mongodb.memory_tcmalloc_stats", + Priority: prioMemoryTCMallocStats, + Dims: module.Dims{ + {ID: "tcmalloc_generic_current_allocated_bytes", Name: "allocated"}, + {ID: "tcmalloc_central_cache_free_bytes", Name: "central_cache_freelist"}, + {ID: "tcmalloc_transfer_cache_free_bytes", Name: "transfer_cache_freelist"}, + {ID: "tcmalloc_thread_cache_free_bytes", Name: "thread_cache_freelists"}, + {ID: "tcmalloc_pageheap_free_bytes", Name: "pageheap_freelist"}, + {ID: "tcmalloc_pageheap_unmapped_bytes", Name: "pageheap_unmapped"}, + }, + } + + chartAssertsRate = module.Chart{ + ID: "asserts_rate", + Title: "Raised assertions", + Units: "asserts/s", + Fam: "asserts", + Ctx: "mongodb.asserts_rate", + Type: module.Stacked, + Priority: prioAssertsRate, + Dims: module.Dims{ + {ID: "asserts_regular", Name: "regular", Algo: module.Incremental}, + {ID: "asserts_warning", Name: "warning", Algo: module.Incremental}, + {ID: "asserts_msg", Name: "msg", Algo: module.Incremental}, + {ID: "asserts_user", Name: "user", Algo: module.Incremental}, + {ID: "asserts_tripwire", Name: "tripwire", Algo: module.Incremental}, + {ID: "asserts_rollovers", Name: "rollovers", Algo: module.Incremental}, + }, + } + + chartTransactionsCount = module.Chart{ + ID: "transactions_count", + Title: "Current transactions", + Units: "transactions", + Fam: "transactions", + Ctx: "mongodb.transactions_count", + Priority: prioTransactionsCount, + Dims: module.Dims{ + {ID: "txn_active", Name: "active"}, + {ID: "txn_inactive", Name: "inactive"}, + {ID: "txn_open", Name: "open"}, + {ID: "txn_prepared", Name: "prepared"}, + }, + } + chartTransactionsRate = module.Chart{ + ID: "transactions_rate", + Title: "Transactions rate", + Units: "transactions/s", + Fam: "transactions", + Ctx: "mongodb.transactions_rate", + Priority: prioTransactionsRate, + Dims: module.Dims{ + {ID: "txn_total_started", Name: "started", Algo: module.Incremental}, + {ID: "txn_total_aborted", Name: "aborted", Algo: module.Incremental}, + {ID: "txn_total_committed", Name: "committed", Algo: module.Incremental}, + {ID: "txn_total_prepared", Name: "prepared", Algo: module.Incremental}, + }, + } + chartTransactionsNoShardsCommitsRate = module.Chart{ + ID: "transactions_no_shards_commits_rate", + Title: "Transactions commits", + Units: "commits/s", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_rate", + Priority: prioTransactionsNoShardsCommitsRate, + Type: module.Stacked, + Labels: []module.Label{{Key: "commit_type", Value: "noShards"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_no_shards_successful", Name: "success", Algo: module.Incremental}, + {ID: "txn_commit_types_no_shards_unsuccessful", Name: "fail", Algo: module.Incremental}, + }, + } + chartTransactionsNoShardsCommitsDurationTime = module.Chart{ + ID: "transactions_no_shards_commits_duration_time", + Title: "Transactions successful commits duration", + Units: "milliseconds", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_duration_time", + Priority: prioTransactionsNoShardsCommitsDurationTime, + Labels: []module.Label{{Key: "commit_type", Value: "noShards"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_no_shards_successful_duration_micros", Name: "commits", Algo: module.Incremental, Div: 1000}, + }, + } + chartTransactionsSingleShardCommitsRate = module.Chart{ + ID: "transactions_single_shard_commits_rate", + Title: "Transactions commits", + Units: "commits/s", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_rate", + Priority: prioTransactionsSingleShardCommitsRate, + Type: module.Stacked, + Labels: []module.Label{{Key: "commit_type", Value: "singleShard"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_single_shard_successful", Name: "success", Algo: module.Incremental}, + {ID: "txn_commit_types_single_shard_unsuccessful", Name: "fail", Algo: module.Incremental}, + }, + } + chartTransactionsSingleShardCommitsDurationTime = module.Chart{ + ID: "transactions_single_shard_commits_duration_time", + Title: "Transactions successful commits duration", + Units: "milliseconds", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_duration_time", + Priority: prioTransactionsSingleShardCommitsDurationTime, + Labels: []module.Label{{Key: "commit_type", Value: "singleShard"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_single_shard_successful_duration_micros", Name: "commits", Algo: module.Incremental, Div: 1000}, + }, + } + chartTransactionsSingleWriteShardCommitsRate = module.Chart{ + ID: "transactions_single_write_shard_commits_rate", + Title: "Transactions commits", + Units: "commits/s", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_rate", + Priority: prioTransactionsSingleWriteShardCommitsRate, + Type: module.Stacked, + Labels: []module.Label{{Key: "commit_type", Value: "singleWriteShard"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_single_write_shard_successful", Name: "success", Algo: module.Incremental}, + {ID: "txn_commit_types_single_write_shard_unsuccessful", Name: "fail", Algo: module.Incremental}, + }, + } + chartTransactionsSingleWriteShardCommitsDurationTime = module.Chart{ + ID: "transactions_single_write_shard_commits_duration_time", + Title: "Transactions successful commits duration", + Units: "milliseconds", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_duration_time", + Priority: prioTransactionsSingleWriteShardCommitsDurationTime, + Labels: []module.Label{{Key: "commit_type", Value: "singleWriteShard"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_single_write_shard_successful_duration_micros", Name: "commits", Algo: module.Incremental, Div: 1000}, + }, + } + chartTransactionsReadOnlyCommitsRate = module.Chart{ + ID: "transactions_read_only_commits_rate", + Title: "Transactions commits", + Units: "commits/s", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_rate", + Priority: prioTransactionsReadOnlyCommitsRate, + Type: module.Stacked, + Labels: []module.Label{{Key: "commit_type", Value: "readOnly"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_read_only_successful", Name: "success", Algo: module.Incremental}, + {ID: "txn_commit_types_read_only_unsuccessful", Name: "fail", Algo: module.Incremental}, + }, + } + chartTransactionsReadOnlyCommitsDurationTime = module.Chart{ + ID: "transactions_read_only_commits_duration_time", + Title: "Transactions successful commits duration", + Units: "milliseconds", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_duration_time", + Priority: prioTransactionsReadOnlyCommitsDurationTime, + Labels: []module.Label{{Key: "commit_type", Value: "readOnly"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_read_only_successful_duration_micros", Name: "commits", Algo: module.Incremental, Div: 1000}, + }, + } + chartTransactionsTwoPhaseCommitCommitsRate = module.Chart{ + ID: "transactions_two_phase_commit_commits_rate", + Title: "Transactions commits", + Units: "commits/s", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_rate", + Priority: prioTransactionsTwoPhaseCommitCommitsRate, + Type: module.Stacked, + Labels: []module.Label{{Key: "commit_type", Value: "twoPhaseCommit"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_two_phase_commit_successful", Name: "success", Algo: module.Incremental}, + {ID: "txn_commit_types_two_phase_commit_unsuccessful", Name: "fail", Algo: module.Incremental}, + }, + } + chartTransactionsTwoPhaseCommitCommitsDurationTime = module.Chart{ + ID: "transactions_two_phase_commit_commits_duration_time", + Title: "Transactions successful commits duration", + Units: "milliseconds", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_duration_time", + Priority: prioTransactionsTwoPhaseCommitCommitsDurationTime, + Labels: []module.Label{{Key: "commit_type", Value: "twoPhaseCommit"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_two_phase_commit_successful_duration_micros", Name: "commits", Algo: module.Incremental, Div: 1000}, + }, + } + chartTransactionsRecoverWithTokenCommitsRate = module.Chart{ + ID: "transactions_recover_with_token_commits_rate", + Title: "Transactions commits", + Units: "commits/s", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_rate", + Priority: prioTransactionsRecoverWithTokenCommitsRate, + Type: module.Stacked, + Labels: []module.Label{{Key: "commit_type", Value: "recoverWithToken"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_recover_with_token_successful", Name: "success", Algo: module.Incremental}, + {ID: "txn_commit_types_recover_with_token_unsuccessful", Name: "fail", Algo: module.Incremental}, + }, + } + chartTransactionsRecoverWithTokenCommitsDurationTime = module.Chart{ + ID: "transactions_recover_with_token_commits_duration_time", + Title: "Transactions successful commits duration", + Units: "milliseconds", + Fam: "transactions", + Ctx: "mongodb.transactions_commits_duration_time", + Priority: prioTransactionsRecoverWithTokenCommitsDurationTime, + Labels: []module.Label{{Key: "commit_type", Value: "recoverWithToken"}}, + Dims: module.Dims{ + {ID: "txn_commit_types_recover_with_token_successful_duration_micros", Name: "commits", Algo: module.Incremental, Div: 1000}, + }, + } + + chartGlobalLockAcquisitionsRate = module.Chart{ + ID: "global_lock_acquisitions_rate", + Title: "Global lock acquisitions", + Units: "acquisitions/s", + Fam: "locks", + Ctx: "mongodb.lock_acquisitions_rate", + Priority: prioGlobalLockAcquisitionsRate, + Labels: []module.Label{{Key: "lock_type", Value: "global"}}, + Dims: module.Dims{ + {ID: "locks_global_acquire_shared", Name: "shared", Algo: module.Incremental}, + {ID: "locks_global_acquire_exclusive", Name: "exclusive", Algo: module.Incremental}, + {ID: "locks_global_acquire_intent_shared", Name: "intent_shared", Algo: module.Incremental}, + {ID: "locks_global_acquire_intent_exclusive", Name: "intent_exclusive", Algo: module.Incremental}, + }, + } + chartDatabaseLockAcquisitionsRate = module.Chart{ + ID: "database_lock_acquisitions_rate", + Title: "Database lock acquisitions", + Units: "acquisitions/s", + Fam: "locks", + Ctx: "mongodb.lock_acquisitions_rate", + Priority: prioDatabaseLockAcquisitionsRate, + Labels: []module.Label{{Key: "lock_type", Value: "database"}}, + Dims: module.Dims{ + {ID: "locks_database_acquire_shared", Name: "shared", Algo: module.Incremental}, + {ID: "locks_database_acquire_exclusive", Name: "exclusive", Algo: module.Incremental}, + {ID: "locks_database_acquire_intent_shared", Name: "intent_shared", Algo: module.Incremental}, + {ID: "locks_database_acquire_intent_exclusive", Name: "intent_exclusive", Algo: module.Incremental}, + }, + } + chartCollectionLockAcquisitionsRate = module.Chart{ + ID: "collection_lock_acquisitions_rate", + Title: "Collection lock acquisitions", + Units: "acquisitions/s", + Fam: "locks", + Ctx: "mongodb.lock_acquisitions_rate", + Priority: prioCollectionLockAcquisitionsRate, + Labels: []module.Label{{Key: "lock_type", Value: "collection"}}, + Dims: module.Dims{ + {ID: "locks_collection_acquire_shared", Name: "shared", Algo: module.Incremental}, + {ID: "locks_collection_acquire_exclusive", Name: "exclusive", Algo: module.Incremental}, + {ID: "locks_collection_acquire_intent_shared", Name: "intent_shared", Algo: module.Incremental}, + {ID: "locks_collection_acquire_intent_exclusive", Name: "intent_exclusive", Algo: module.Incremental}, + }, + } + chartMutexLockAcquisitionsRate = module.Chart{ + ID: "mutex_lock_acquisitions_rate", + Title: "Mutex lock acquisitions", + Units: "acquisitions/s", + Fam: "locks", + Ctx: "mongodb.lock_acquisitions_rate", + Priority: prioMutexLockAcquisitionsRate, + Labels: []module.Label{{Key: "lock_type", Value: "mutex"}}, + Dims: module.Dims{ + {ID: "locks_mutex_acquire_shared", Name: "shared", Algo: module.Incremental}, + {ID: "locks_mutex_acquire_exclusive", Name: "exclusive", Algo: module.Incremental}, + {ID: "locks_mutex_acquire_intent_shared", Name: "intent_shared", Algo: module.Incremental}, + {ID: "locks_mutex_acquire_intent_exclusive", Name: "intent_exclusive", Algo: module.Incremental}, + }, + } + chartMetadataLockAcquisitionsRate = module.Chart{ + ID: "metadata_lock_acquisitions_rate", + Title: "Metadata lock acquisitions", + Units: "acquisitions/s", + Fam: "locks", + Ctx: "mongodb.lock_acquisitions_rate", + Priority: prioMetadataLockAcquisitionsRate, + Labels: []module.Label{{Key: "lock_type", Value: "metadata"}}, + Dims: module.Dims{ + {ID: "locks_metadata_acquire_shared", Name: "shared", Algo: module.Incremental}, + {ID: "locks_metadata_acquire_exclusive", Name: "exclusive", Algo: module.Incremental}, + {ID: "locks_metadata_acquire_intent_shared", Name: "intent_shared", Algo: module.Incremental}, + {ID: "locks_metadata_acquire_intent_exclusive", Name: "intent_exclusive", Algo: module.Incremental}, + }, + } + chartOpLogLockAcquisitionsRate = module.Chart{ + ID: "oplog_lock_acquisitions_rate", + Title: "Operations log lock acquisitions", + Units: "acquisitions/s", + Fam: "locks", + Ctx: "mongodb.lock_acquisitions_rate", + Priority: prioOpLogLockAcquisitionsRate, + Labels: []module.Label{{Key: "lock_type", Value: "oplog"}}, + Dims: module.Dims{ + {ID: "locks_oplog_acquire_shared", Name: "shared", Algo: module.Incremental}, + {ID: "locks_oplog_acquire_exclusive", Name: "exclusive", Algo: module.Incremental}, + {ID: "locks_oplog_acquire_intent_shared", Name: "intent_shared", Algo: module.Incremental}, + {ID: "locks_oplog_acquire_intent_exclusive", Name: "intent_exclusive", Algo: module.Incremental}, + }, + } + + chartCursorsOpenCount = module.Chart{ + ID: "cursors_open_count", + Title: "Open cursors", + Units: "cursors", + Fam: "cursors", + Ctx: "mongodb.cursors_open_count", + Priority: prioCursorsOpenCount, + Dims: module.Dims{ + {ID: "metrics_cursor_open_total", Name: "open"}, + }, + } + chartCursorsOpenNoTimeoutCount = module.Chart{ + ID: "cursors_open_no_timeout_count", + Title: "Open cursors with disabled timeout", + Units: "cursors", + Fam: "cursors", + Ctx: "mongodb.cursors_open_no_timeout_count", + Priority: prioCursorsOpenNoTimeoutCount, + Dims: module.Dims{ + {ID: "metrics_cursor_open_no_timeout", Name: "open_no_timeout"}, + }, + } + chartCursorsOpenedRate = module.Chart{ + ID: "cursors_opened_rate", + Title: "Opened cursors rate", + Units: "cursors/s", + Fam: "cursors", + Ctx: "mongodb.cursors_opened_rate", + Priority: prioCursorsOpenedRate, + Dims: module.Dims{ + {ID: "metrics_cursor_total_opened", Name: "opened"}, + }, + } + chartCursorsTimedOutRate = module.Chart{ + ID: "cursors_timed_out_rate", + Title: "Timed-out cursors", + Units: "cursors/s", + Fam: "cursors", + Ctx: "mongodb.cursors_timed_out_rate", + Priority: prioTimedOutCursorsRate, + Dims: module.Dims{ + {ID: "metrics_cursor_timed_out", Name: "timed_out"}, + }, + } + chartCursorsByLifespanCount = module.Chart{ + ID: "cursors_by_lifespan_count", + Title: "Cursors lifespan", + Units: "cursors", + Fam: "cursors", + Ctx: "mongodb.cursors_by_lifespan_count", + Priority: prioCursorsByLifespanCount, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "metrics_cursor_lifespan_less_than_1_second", Name: "le_1s"}, + {ID: "metrics_cursor_lifespan_less_than_5_seconds", Name: "1s_5s"}, + {ID: "metrics_cursor_lifespan_less_than_15_seconds", Name: "5s_15s"}, + {ID: "metrics_cursor_lifespan_less_than_30_seconds", Name: "15s_30s"}, + {ID: "metrics_cursor_lifespan_less_than_1_minute", Name: "30s_1m"}, + {ID: "metrics_cursor_lifespan_less_than_10_minutes", Name: "1m_10m"}, + {ID: "metrics_cursor_lifespan_greater_than_or_equal_10_minutes", Name: "ge_10m"}, + }, + } + + chartWiredTigerConcurrentReadTransactionsUsage = module.Chart{ + ID: "wiredtiger_concurrent_read_transactions_usage", + Title: "Wired Tiger concurrent read transactions usage", + Units: "transactions", + Fam: "wiredtiger", + Ctx: "mongodb.wiredtiger_concurrent_read_transactions_usage", + Priority: prioWiredTigerConcurrentReadTransactionsUsage, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "wiredtiger_concurrent_txn_read_available", Name: "available"}, + {ID: "wiredtiger_concurrent_txn_read_out", Name: "used"}, + }, + } + chartWiredTigerConcurrentWriteTransactionsUsage = module.Chart{ + ID: "wiredtiger_concurrent_write_transactions_usage", + Title: "Wired Tiger concurrent write transactions usage", + Units: "transactions", + Fam: "wiredtiger", + Ctx: "mongodb.wiredtiger_concurrent_write_transactions_usage", + Priority: prioWiredTigerConcurrentWriteTransactionsUsage, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "wiredtiger_concurrent_txn_write_available", Name: "available"}, + {ID: "wiredtiger_concurrent_txn_write_out", Name: "used"}, + }, + } + chartWiredTigerCacheUsage = module.Chart{ + ID: "wiredtiger_cache_usage", + Title: "Wired Tiger cache usage", + Units: "bytes", + Fam: "wiredtiger", + Ctx: "mongodb.wiredtiger_cache_usage", + Priority: prioWiredTigerCacheUsage, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "wiredtiger_cache_currently_in_cache_bytes", Name: "used"}, + }, + } + chartWiredTigerCacheDirtySpaceSize = module.Chart{ + ID: "wiredtiger_cache_dirty_space_size", + Title: "Wired Tiger cache dirty space size", + Units: "bytes", + Fam: "wiredtiger", + Ctx: "mongodb.wiredtiger_cache_dirty_space_size", + Priority: prioWiredTigerCacheDirtySpaceSize, + Dims: module.Dims{ + {ID: "wiredtiger_cache_tracked_dirty_in_the_cache_bytes", Name: "dirty"}, + }, + } + chartWiredTigerCacheIORate = module.Chart{ + ID: "wiredtiger_cache_io_rate", + Title: "Wired Tiger IO activity", + Units: "pages/s", + Fam: "wiredtiger", + Ctx: "mongodb.wiredtiger_cache_io_rate", + Priority: prioWiredTigerCacheIORate, + Dims: module.Dims{ + {ID: "wiredtiger_cache_read_into_cache_pages", Name: "read", Algo: module.Incremental}, + {ID: "wiredtiger_cache_written_from_cache_pages", Name: "written", Algo: module.Incremental}, + }, + } + chartWiredTigerCacheEvictionsRate = module.Chart{ + ID: "wiredtiger_cache_eviction_rate", + Title: "Wired Tiger cache evictions", + Units: "pages/s", + Fam: "wiredtiger", + Ctx: "mongodb.wiredtiger_cache_evictions_rate", + Type: module.Stacked, + Priority: prioWiredTigerCacheEvictionsRate, + Dims: module.Dims{ + {ID: "wiredtiger_cache_unmodified_evicted_pages", Name: "unmodified", Algo: module.Incremental}, + {ID: "wiredtiger_cache_modified_evicted_pages", Name: "modified", Algo: module.Incremental}, + }, + } +) + +var ( + chartTmplDatabaseCollectionsCount = &module.Chart{ + ID: chartPxDatabase + "%s_collections_count", + Title: "Database collections", + Units: "collections", + Fam: "databases", + Ctx: "mongodb.database_collections_count", + Priority: prioDatabaseCollectionsCount, + Dims: module.Dims{ + {ID: "database_%s_collections", Name: "collections"}, + }, + } + chartTmplDatabaseIndexesCount = &module.Chart{ + ID: chartPxDatabase + "%s_indexes_count", + Title: "Database indexes", + Units: "indexes", + Fam: "databases", + Ctx: "mongodb.database_indexes_count", + Priority: prioDatabaseIndexesCount, + Dims: module.Dims{ + {ID: "database_%s_indexes", Name: "indexes"}, + }, + } + chartTmplDatabaseViewsCount = &module.Chart{ + ID: chartPxDatabase + "%s_views_count", + Title: "Database views", + Units: "views", + Fam: "databases", + Ctx: "mongodb.database_views_count", + Priority: prioDatabaseViewsCount, + Dims: module.Dims{ + {ID: "database_%s_views", Name: "views"}, + }, + } + chartTmplDatabaseDocumentsCount = &module.Chart{ + ID: chartPxDatabase + "%s_documents_count", + Title: "Database documents", + Units: "documents", + Fam: "databases", + Ctx: "mongodb.database_documents_count", + Priority: prioDatabaseDocumentsCount, + Dims: module.Dims{ + {ID: "database_%s_documents", Name: "documents"}, + }, + } + chartTmplDatabaseDataSize = &module.Chart{ + ID: chartPxDatabase + "%s_data_size", + Title: "Database data size", + Units: "bytes", + Fam: "databases", + Ctx: "mongodb.database_data_size", + Priority: prioDatabaseDataSize, + Dims: module.Dims{ + {ID: "database_%s_data_size", Name: "data_size"}, + }, + } + chartTmplDatabaseStorageSize = &module.Chart{ + ID: chartPxDatabase + "%s_storage_size", + Title: "Database storage size", + Units: "bytes", + Fam: "databases", + Ctx: "mongodb.database_storage_size", + Priority: prioDatabaseStorageSize, + Dims: module.Dims{ + {ID: "database_%s_storage_size", Name: "storage_size"}, + }, + } + chartTmplDatabaseIndexSize = &module.Chart{ + ID: chartPxDatabase + "%s_index_size", + Title: "Database index size", + Units: "bytes", + Fam: "databases", + Ctx: "mongodb.database_index_size", + Priority: prioDatabaseIndexSize, + Dims: module.Dims{ + {ID: "database_%s_index_size", Name: "index_size"}, + }, + } +) + +var ( + chartTmplReplSetMemberState = &module.Chart{ + ID: chartPxReplSetMember + "%s_state", + Title: "Replica Set member state", + Units: "state", + Fam: "replica sets", + Ctx: "mongodb.repl_set_member_state", + Priority: prioReplSetMemberState, + Dims: module.Dims{ + {ID: "repl_set_member_%s_state_primary", Name: "primary"}, + {ID: "repl_set_member_%s_state_startup", Name: "startup"}, + {ID: "repl_set_member_%s_state_secondary", Name: "secondary"}, + {ID: "repl_set_member_%s_state_recovering", Name: "recovering"}, + {ID: "repl_set_member_%s_state_startup2", Name: "startup2"}, + {ID: "repl_set_member_%s_state_unknown", Name: "unknown"}, + {ID: "repl_set_member_%s_state_arbiter", Name: "arbiter"}, + {ID: "repl_set_member_%s_state_down", Name: "down"}, + {ID: "repl_set_member_%s_state_rollback", Name: "rollback"}, + {ID: "repl_set_member_%s_state_removed", Name: "removed"}, + }, + } + chartTmplReplSetMemberHealthStatus = &module.Chart{ + ID: chartPxReplSetMember + "%s_health_status", + Title: "Replica Set member health status", + Units: "status", + Fam: "replica sets", + Ctx: "mongodb.repl_set_member_health_status", + Priority: prioReplSetMemberHealthStatus, + Dims: module.Dims{ + {ID: "repl_set_member_%s_health_status_up", Name: "up"}, + {ID: "repl_set_member_%s_health_status_down", Name: "down"}, + }, + } + chartTmplReplSetMemberReplicationLagTime = &module.Chart{ + ID: chartPxReplSetMember + "%s_replication_lag_time", + Title: "Replica Set member replication lag", + Units: "milliseconds", + Fam: "replica sets", + Ctx: "mongodb.repl_set_member_replication_lag_time", + Priority: prioReplSetMemberReplicationLagTime, + Dims: module.Dims{ + {ID: "repl_set_member_%s_replication_lag", Name: "replication_lag"}, + }, + } + chartTmplReplSetMemberHeartbeatLatencyTime = &module.Chart{ + ID: chartPxReplSetMember + "%s_heartbeat_latency_time", + Title: "Replica Set member heartbeat latency", + Units: "milliseconds", + Fam: "replica sets", + Ctx: "mongodb.repl_set_member_heartbeat_latency_time", + Priority: prioReplSetMemberHeartbeatLatencyTime, + Dims: module.Dims{ + {ID: "repl_set_member_%s_heartbeat_latency", Name: "heartbeat_latency"}, + }, + } + chartTmplReplSetMemberPingRTTTime = &module.Chart{ + ID: chartPxReplSetMember + "%s_ping_rtt_time", + Title: "Replica Set member ping RTT", + Units: "milliseconds", + Fam: "replica sets", + Ctx: "mongodb.repl_set_member_ping_rtt_time", + Priority: prioReplSetMemberPingRTTTime, + Dims: module.Dims{ + {ID: "repl_set_member_%s_ping_rtt", Name: "ping_rtt"}, + }, + } + chartTmplReplSetMemberUptime = &module.Chart{ + ID: chartPxReplSetMember + "%s_uptime", + Title: "Replica Set member uptime", + Units: "seconds", + Fam: "replica sets", + Ctx: "mongodb.repl_set_member_uptime", + Priority: prioReplSetMemberUptime, + Dims: module.Dims{ + {ID: "repl_set_member_%s_uptime", Name: "uptime"}, + }, + } +) + +var ( + chartShardingNodesCount = &module.Chart{ + ID: "sharding_nodes_count", + Title: "Sharding Nodes", + Units: "nodes", + Fam: "sharding", + Ctx: "mongodb.sharding_nodes_count", + Type: module.Stacked, + Priority: prioShardingNodesCount, + Dims: module.Dims{ + {ID: "shard_nodes_aware", Name: "shard_aware"}, + {ID: "shard_nodes_unaware", Name: "shard_unaware"}, + }, + } + chartShardingShardedDatabases = &module.Chart{ + ID: "sharding_sharded_databases_count", + Title: "Sharded databases", + Units: "databases", + Fam: "sharding", + Ctx: "mongodb.sharding_sharded_databases_count", + Type: module.Stacked, + Priority: prioShardingShardedDatabasesCount, + Dims: module.Dims{ + {ID: "shard_databases_partitioned", Name: "partitioned"}, + {ID: "shard_databases_unpartitioned", Name: "unpartitioned"}, + }, + } + + chartShardingShardedCollectionsCount = &module.Chart{ + ID: "sharding_sharded_collections_count", + Title: "Sharded collections", + Units: "collections", + Fam: "sharding", + Ctx: "mongodb.sharding_sharded_collections_count", + Type: module.Stacked, + Priority: prioShardingShardedCollectionsCount, + Dims: module.Dims{ + {ID: "shard_collections_partitioned", Name: "partitioned"}, + {ID: "shard_collections_unpartitioned", Name: "unpartitioned"}, + }, + } + + chartTmplShardChunks = &module.Chart{ + ID: chartPxShard + "%s_chunks", + Title: "Shard chunks", + Units: "chunks", + Fam: "sharding", + Ctx: "mongodb.sharding_shard_chunks_count", + Priority: prioShardChunks, + Dims: module.Dims{ + {ID: "shard_id_%s_chunks", Name: "chunks"}, + }, + } +) diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/client.go b/src/go/collectors/go.d.plugin/modules/mongodb/client.go new file mode 100644 index 000000000..eb36fa8ac --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/client.go @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package mongo + +import ( + "context" + "fmt" + "time" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +const ( + mongos = "mongos" +) + +type mongoConn interface { + serverStatus() (*documentServerStatus, error) + listDatabaseNames() ([]string, error) + dbStats(name string) (*documentDBStats, error) + isReplicaSet() bool + isMongos() bool + replSetGetStatus() (*documentReplSetStatus, error) + shardNodes() (*documentShardNodesResult, error) + shardDatabasesPartitioning() (*documentPartitionedResult, error) + shardCollectionsPartitioning() (*documentPartitionedResult, error) + shardChunks() (map[string]int64, error) + initClient(uri string, timeout time.Duration) error + close() error +} + +type mongoClient struct { + client *mongo.Client + timeout time.Duration + replicaSetFlag *bool + mongosFlag *bool +} + +func (c *mongoClient) serverStatus() (*documentServerStatus, error) { + ctx, cancel := context.WithTimeout(context.Background(), c.timeout) + defer cancel() + + cmd := bson.D{ + {Key: "serverStatus", Value: 1}, + {Key: "repl", Value: 1}, + {Key: "metrics", + Value: bson.D{ + {Key: "document", Value: true}, + {Key: "cursor", Value: true}, + {Key: "queryExecutor", Value: true}, + {Key: "apiVersions", Value: false}, + {Key: "aggStageCounters", Value: false}, + {Key: "commands", Value: false}, + {Key: "dotsAndDollarsFields", Value: false}, + {Key: "getLastError", Value: false}, + {Key: "mongos", Value: false}, + {Key: "operation", Value: false}, + {Key: "operatorCounters", Value: false}, + {Key: "query", Value: false}, + {Key: "record", Value: false}, + {Key: "repl", Value: false}, + {Key: "storage", Value: false}, + {Key: "ttl", Value: false}, + }, + }, + } + var status *documentServerStatus + + err := c.client.Database("admin").RunCommand(ctx, cmd).Decode(&status) + if err != nil { + return nil, err + } + + isReplSet := status.Repl != nil + c.replicaSetFlag = &isReplSet + + isMongos := status.Process == mongos + c.mongosFlag = &isMongos + + return status, err +} + +func (c *mongoClient) listDatabaseNames() ([]string, error) { + ctx, cancel := context.WithTimeout(context.Background(), c.timeout) + defer cancel() + + return c.client.ListDatabaseNames(ctx, bson.M{}) +} + +func (c *mongoClient) dbStats(name string) (*documentDBStats, error) { + ctx, cancel := context.WithTimeout(context.Background(), c.timeout) + defer cancel() + + cmd := bson.M{"dbStats": 1} + var stats documentDBStats + + if err := c.client.Database(name).RunCommand(ctx, cmd).Decode(&stats); err != nil { + return nil, err + } + + return &stats, nil +} + +func (c *mongoClient) isReplicaSet() bool { + if c.replicaSetFlag != nil { + return *c.replicaSetFlag + } + + status, err := c.serverStatus() + if err != nil { + return false + } + + return status.Repl != nil +} + +func (c *mongoClient) isMongos() bool { + if c.mongosFlag != nil { + return *c.mongosFlag + } + + status, err := c.serverStatus() + if err != nil { + return false + } + + return status.Process == mongos +} + +func (c *mongoClient) replSetGetStatus() (*documentReplSetStatus, error) { + ctx, cancel := context.WithTimeout(context.Background(), c.timeout) + defer cancel() + + var status *documentReplSetStatus + cmd := bson.M{"replSetGetStatus": 1} + + err := c.client.Database("admin").RunCommand(ctx, cmd).Decode(&status) + if err != nil { + return nil, err + } + + return status, err +} + +func (c *mongoClient) shardNodes() (*documentShardNodesResult, error) { + collection := "shards" + groupStage := bson.D{{Key: "$sortByCount", Value: "$state"}} + + nodesByState, err := c.shardCollectAggregation(collection, []bson.D{groupStage}) + if err != nil { + return nil, err + } + + return &documentShardNodesResult{nodesByState.True, nodesByState.False}, nil +} + +func (c *mongoClient) shardDatabasesPartitioning() (*documentPartitionedResult, error) { + collection := "databases" + groupStage := bson.D{{Key: "$sortByCount", Value: "$partitioned"}} + + partitioning, err := c.shardCollectAggregation(collection, []bson.D{groupStage}) + if err != nil { + return nil, err + } + + return &documentPartitionedResult{partitioning.True, partitioning.False}, nil +} + +func (c *mongoClient) shardCollectionsPartitioning() (*documentPartitionedResult, error) { + collection := "collections" + matchStage := bson.D{{Key: "$match", Value: bson.D{{Key: "dropped", Value: false}}}} + countStage := bson.D{{Key: "$sortByCount", Value: bson.D{{Key: "$eq", Value: bson.A{"$distributionMode", "sharded"}}}}} + + partitioning, err := c.shardCollectAggregation(collection, []bson.D{matchStage, countStage}) + if err != nil { + return nil, err + } + + return &documentPartitionedResult{partitioning.True, partitioning.False}, nil +} + +func (c *mongoClient) shardCollectAggregation(collection string, aggr []bson.D) (*documentAggrResult, error) { + rows, err := c.dbAggregate(collection, aggr) + if err != nil { + return nil, err + } + + result := &documentAggrResult{} + + for _, row := range rows { + if row.Bool { + result.True = row.Count + } else { + result.False = row.Count + } + } + + return result, err +} + +func (c *mongoClient) shardChunks() (map[string]int64, error) { + ctx, cancel := context.WithTimeout(context.Background(), c.timeout) + defer cancel() + + col := c.client.Database("config").Collection("chunks") + + cursor, err := col.Aggregate(ctx, mongo.Pipeline{bson.D{{Key: "$sortByCount", Value: "$shard"}}}) + if err != nil { + return nil, err + } + + var shards []bson.M + if err = cursor.All(ctx, &shards); err != nil { + return nil, err + } + + defer func() { _ = cursor.Close(ctx) }() + + result := map[string]int64{} + + for _, row := range shards { + k, ok := row["_id"].(string) + if !ok { + return nil, fmt.Errorf("shard name is not a string: %v", row["_id"]) + } + v, ok := row["count"].(int32) + if !ok { + return nil, fmt.Errorf("shard chunk count is not a int32: %v", row["count"]) + } + result[k] = int64(v) + } + + return result, err +} + +func (c *mongoClient) initClient(uri string, timeout time.Duration) error { + if c.client != nil { + return nil + } + + c.timeout = timeout + + ctxConn, cancelConn := context.WithTimeout(context.Background(), c.timeout) + defer cancelConn() + + client, err := mongo.Connect(ctxConn, options.Client().ApplyURI(uri)) + if err != nil { + return err + } + + ctxPing, cancelPing := context.WithTimeout(context.Background(), c.timeout) + defer cancelPing() + + if err := client.Ping(ctxPing, nil); err != nil { + return err + } + + c.client = client + + return nil +} + +func (c *mongoClient) close() error { + if c.client == nil { + return nil + } + + ctx, cancel := context.WithTimeout(context.Background(), c.timeout) + defer cancel() + + if err := c.client.Disconnect(ctx); err != nil { + return err + } + + c.client = nil + + return nil +} + +func (c *mongoClient) dbAggregate(collection string, aggr []bson.D) ([]documentAggrResults, error) { + ctx, cancel := context.WithTimeout(context.Background(), c.timeout) + defer cancel() + + cursor, err := c.client.Database("config").Collection(collection).Aggregate(ctx, aggr) + if err != nil { + return nil, err + } + + defer func() { _ = cursor.Close(ctx) }() + + var rows []documentAggrResults + if err := cursor.All(ctx, &rows); err != nil { + return nil, err + } + + return rows, nil +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/collect.go b/src/go/collectors/go.d.plugin/modules/mongodb/collect.go new file mode 100644 index 000000000..232145de3 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/collect.go @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package mongo + +import "fmt" + +func (m *Mongo) collect() (map[string]int64, error) { + if err := m.conn.initClient(m.URI, m.Timeout.Duration()); err != nil { + return nil, fmt.Errorf("init mongo conn: %v", err) + } + + mx := make(map[string]int64) + + if err := m.collectServerStatus(mx); err != nil { + return nil, fmt.Errorf("couldn't collect server status metrics: %v", err) + } + + if err := m.collectDbStats(mx); err != nil { + return mx, fmt.Errorf("couldn't collect dbstats metrics: %v", err) + } + + if m.conn.isReplicaSet() { + if err := m.collectReplSetStatus(mx); err != nil { + return mx, fmt.Errorf("couldn't collect documentReplSetStatus metrics: %v", err) + } + } + + if m.conn.isMongos() { + m.addShardingChartsOnce.Do(m.addShardingCharts) + if err := m.collectSharding(mx); err != nil { + return mx, fmt.Errorf("couldn't collect sharding metrics: %v", err) + } + } + + return mx, nil +} + +func boolToInt(v bool) int64 { + if v { + return 1 + } + return 0 +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/collect_dbstats.go b/src/go/collectors/go.d.plugin/modules/mongodb/collect_dbstats.go new file mode 100644 index 000000000..edd7077e1 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/collect_dbstats.go @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package mongo + +import ( + "fmt" + "strings" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" +) + +func (m *Mongo) collectDbStats(mx map[string]int64) error { + if m.dbSelector == nil { + m.Debug("'database' selector not set, skip collecting database statistics") + return nil + } + + allDBs, err := m.conn.listDatabaseNames() + if err != nil { + return fmt.Errorf("cannot get database names: %v", err) + } + + m.Debugf("all databases on the server: '%v'", allDBs) + + var dbs []string + for _, db := range allDBs { + if m.dbSelector.MatchString(db) { + dbs = append(dbs, db) + } + } + + if len(allDBs) != len(dbs) { + m.Debugf("databases remaining after filtering: %v", dbs) + } + + seen := make(map[string]bool) + for _, db := range dbs { + s, err := m.conn.dbStats(db) + if err != nil { + return fmt.Errorf("dbStats command failed: %v", err) + } + + seen[db] = true + + mx["database_"+db+"_collections"] = s.Collections + mx["database_"+db+"_views"] = s.Views + mx["database_"+db+"_indexes"] = s.Indexes + mx["database_"+db+"_documents"] = s.Objects + mx["database_"+db+"_data_size"] = s.DataSize + mx["database_"+db+"_index_size"] = s.IndexSize + mx["database_"+db+"_storage_size"] = s.StorageSize + } + + for db := range seen { + if !m.databases[db] { + m.databases[db] = true + m.Debugf("new database '%s': creating charts", db) + m.addDatabaseCharts(db) + } + } + + for db := range m.databases { + if !seen[db] { + delete(m.databases, db) + m.Debugf("stale database '%s': removing charts", db) + m.removeDatabaseCharts(db) + } + } + + return nil +} + +func (m *Mongo) addDatabaseCharts(name string) { + charts := chartsTmplDatabase.Copy() + + for _, chart := range *charts { + chart.ID = fmt.Sprintf(chart.ID, name) + chart.Labels = []module.Label{ + {Key: "database", Value: name}, + } + for _, dim := range chart.Dims { + dim.ID = fmt.Sprintf(dim.ID, name) + } + } + + if err := m.Charts().Add(*charts...); err != nil { + m.Warning(err) + } +} + +func (m *Mongo) removeDatabaseCharts(name string) { + px := fmt.Sprintf("%s%s_", chartPxDatabase, name) + + for _, chart := range *m.Charts() { + if strings.HasPrefix(chart.ID, px) { + chart.MarkRemove() + chart.MarkNotCreated() + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/collect_replsetgetstatus.go b/src/go/collectors/go.d.plugin/modules/mongodb/collect_replsetgetstatus.go new file mode 100644 index 000000000..235e8900e --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/collect_replsetgetstatus.go @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package mongo + +import ( + "fmt" + "strings" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" +) + +// https://www.mongodb.com/docs/manual/reference/replica-states/#replica-set-member-states +var replicaSetMemberStates = map[string]int{ + "startup": 0, + "primary": 1, + "secondary": 2, + "recovering": 3, + "startup2": 5, + "unknown": 6, + "arbiter": 7, + "down": 8, + "rollback": 9, + "removed": 10, +} + +// TODO: deal with duplicates if we collect metrics from all cluster nodes +// should we only collect ReplSetStatus (at least by default) from primary nodes? (db.runCommand( { isMaster: 1 } )) +func (m *Mongo) collectReplSetStatus(mx map[string]int64) error { + s, err := m.conn.replSetGetStatus() + if err != nil { + return fmt.Errorf("error get status of the replica set from mongo: %s", err) + } + + seen := make(map[string]documentReplSetMember) + + for _, member := range s.Members { + seen[member.Name] = member + + px := fmt.Sprintf("repl_set_member_%s_", member.Name) + + mx[px+"replication_lag"] = s.Date.Sub(member.OptimeDate).Milliseconds() + + for k, v := range replicaSetMemberStates { + mx[px+"state_"+k] = boolToInt(member.State == v) + } + + mx[px+"health_status_up"] = boolToInt(member.Health == 1) + mx[px+"health_status_down"] = boolToInt(member.Health == 0) + + if member.Self == nil { + mx[px+"uptime"] = member.Uptime + if v := member.LastHeartbeatRecv; v != nil && !v.IsZero() { + mx[px+"heartbeat_latency"] = s.Date.Sub(*v).Milliseconds() + } + if v := member.PingMs; v != nil { + mx[px+"ping_rtt"] = *v + } + } + } + + for name, member := range seen { + if !m.replSetMembers[name] { + m.replSetMembers[name] = true + m.Debugf("new replica set member '%s': adding charts", name) + m.addReplSetMemberCharts(member) + } + } + + for name := range m.replSetMembers { + if _, ok := seen[name]; !ok { + delete(m.replSetMembers, name) + m.Debugf("stale replica set member '%s': removing charts", name) + m.removeReplSetMemberCharts(name) + } + } + + return nil +} + +func (m *Mongo) addReplSetMemberCharts(v documentReplSetMember) { + charts := chartsTmplReplSetMember.Copy() + + if v.Self != nil { + _ = charts.Remove(chartTmplReplSetMemberHeartbeatLatencyTime.ID) + _ = charts.Remove(chartTmplReplSetMemberPingRTTTime.ID) + _ = charts.Remove(chartTmplReplSetMemberUptime.ID) + } + + for _, chart := range *charts { + chart.ID = fmt.Sprintf(chart.ID, v.Name) + chart.Labels = []module.Label{ + {Key: "repl_set_member", Value: v.Name}, + } + for _, dim := range chart.Dims { + dim.ID = fmt.Sprintf(dim.ID, v.Name) + } + } + + if err := m.Charts().Add(*charts...); err != nil { + m.Warning(err) + } +} + +func (m *Mongo) removeReplSetMemberCharts(name string) { + px := fmt.Sprintf("%s%s_", chartPxReplSetMember, name) + + for _, chart := range *m.Charts() { + if strings.HasPrefix(chart.ID, px) { + chart.MarkRemove() + chart.MarkNotCreated() + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/collect_serverstatus.go b/src/go/collectors/go.d.plugin/modules/mongodb/collect_serverstatus.go new file mode 100644 index 000000000..33fd86b76 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/collect_serverstatus.go @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package mongo + +import ( + "fmt" + "reflect" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/stm" +) + +// collectServerStatus creates the map[string]int64 for the available dims. +// nil values will be ignored and not added to the map and thus metrics should not appear on the dashboard. +// Because mongo reports a metric only after it first appears,some dims might take a while to appear. +// For example, in order to report number of create commands, a document must be created first. +func (m *Mongo) collectServerStatus(mx map[string]int64) error { + s, err := m.conn.serverStatus() + if err != nil { + return fmt.Errorf("serverStatus command failed: %s", err) + } + + m.addOptionalCharts(s) + + for k, v := range stm.ToMap(s) { + mx[k] = v + } + + if s.Transactions != nil && s.Transactions.CommitTypes != nil { + px := "txn_commit_types_" + v := s.Transactions.CommitTypes + mx[px+"no_shards_unsuccessful"] = v.NoShards.Initiated - v.NoShards.Successful + mx[px+"single_shard_unsuccessful"] = v.SingleShard.Initiated - v.SingleShard.Successful + mx[px+"single_write_shard_unsuccessful"] = v.SingleWriteShard.Initiated - v.SingleWriteShard.Successful + mx[px+"read_only_unsuccessful"] = v.ReadOnly.Initiated - v.ReadOnly.Successful + mx[px+"two_phase_commit_unsuccessful"] = v.TwoPhaseCommit.Initiated - v.TwoPhaseCommit.Successful + mx[px+"recover_with_token_unsuccessful"] = v.RecoverWithToken.Initiated - v.RecoverWithToken.Successful + } + + return nil +} + +func (m *Mongo) addOptionalCharts(s *documentServerStatus) { + m.addOptionalChart(s.OpLatencies, + &chartOperationsRate, + &chartOperationsLatencyTime, + ) + m.addOptionalChart(s.WiredTiger, + &chartWiredTigerConcurrentReadTransactionsUsage, + &chartWiredTigerConcurrentWriteTransactionsUsage, + &chartWiredTigerCacheUsage, + &chartWiredTigerCacheDirtySpaceSize, + &chartWiredTigerCacheIORate, + &chartWiredTigerCacheEvictionsRate, + ) + m.addOptionalChart(s.Tcmalloc, + &chartMemoryTCMallocStatsChart, + ) + m.addOptionalChart(s.GlobalLock, + &chartGlobalLockActiveClientsCount, + &chartGlobalLockCurrentQueueCount, + ) + m.addOptionalChart(s.Network.NumSlowDNSOperations, + &chartNetworkSlowDNSResolutionsRate, + ) + m.addOptionalChart(s.Network.NumSlowSSLOperations, + &chartNetworkSlowSSLHandshakesRate, + ) + m.addOptionalChart(s.Metrics.Cursor.TotalOpened, + &chartCursorsOpenedRate, + ) + m.addOptionalChart(s.Metrics.Cursor.TimedOut, + &chartCursorsTimedOutRate, + ) + m.addOptionalChart(s.Metrics.Cursor.Open.Total, + &chartCursorsOpenCount, + ) + m.addOptionalChart(s.Metrics.Cursor.Open.NoTimeout, + &chartCursorsOpenNoTimeoutCount, + ) + m.addOptionalChart(s.Metrics.Cursor.Lifespan, + &chartCursorsByLifespanCount, + ) + + if s.Transactions != nil { + m.addOptionalChart(s.Transactions, + &chartTransactionsCount, + &chartTransactionsRate, + ) + m.addOptionalChart(s.Transactions.CommitTypes, + &chartTransactionsNoShardsCommitsRate, + &chartTransactionsNoShardsCommitsDurationTime, + &chartTransactionsSingleShardCommitsRate, + &chartTransactionsSingleShardCommitsDurationTime, + &chartTransactionsSingleWriteShardCommitsRate, + &chartTransactionsSingleWriteShardCommitsDurationTime, + &chartTransactionsReadOnlyCommitsRate, + &chartTransactionsReadOnlyCommitsDurationTime, + &chartTransactionsTwoPhaseCommitCommitsRate, + &chartTransactionsTwoPhaseCommitCommitsDurationTime, + &chartTransactionsRecoverWithTokenCommitsRate, + &chartTransactionsRecoverWithTokenCommitsDurationTime, + ) + } + if s.Locks != nil { + m.addOptionalChart(s.Locks.Global, &chartGlobalLockAcquisitionsRate) + m.addOptionalChart(s.Locks.Database, &chartDatabaseLockAcquisitionsRate) + m.addOptionalChart(s.Locks.Collection, &chartCollectionLockAcquisitionsRate) + m.addOptionalChart(s.Locks.Mutex, &chartMutexLockAcquisitionsRate) + m.addOptionalChart(s.Locks.Metadata, &chartMetadataLockAcquisitionsRate) + m.addOptionalChart(s.Locks.Oplog, &chartOpLogLockAcquisitionsRate) + } +} + +func (m *Mongo) addOptionalChart(iface any, charts ...*module.Chart) { + if reflect.ValueOf(iface).IsNil() { + return + } + for _, chart := range charts { + if m.optionalCharts[chart.ID] { + continue + } + m.optionalCharts[chart.ID] = true + + if err := m.charts.Add(chart.Copy()); err != nil { + m.Warning(err) + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/collect_sharding.go b/src/go/collectors/go.d.plugin/modules/mongodb/collect_sharding.go new file mode 100644 index 000000000..175004d34 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/collect_sharding.go @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package mongo + +import ( + "fmt" + "strings" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" +) + +func (m *Mongo) collectSharding(mx map[string]int64) error { + nodes, err := m.conn.shardNodes() + if err != nil { + return err + } + + mx["shard_nodes_aware"] = nodes.ShardAware + mx["shard_nodes_unaware"] = nodes.ShardUnaware + + dbPart, err := m.conn.shardDatabasesPartitioning() + if err != nil { + return err + } + + mx["shard_databases_partitioned"] = dbPart.Partitioned + mx["shard_databases_unpartitioned"] = dbPart.UnPartitioned + + collPart, err := m.conn.shardCollectionsPartitioning() + if err != nil { + return err + } + + mx["shard_collections_partitioned"] = collPart.Partitioned + mx["shard_collections_unpartitioned"] = collPart.UnPartitioned + + chunksPerShard, err := m.conn.shardChunks() + if err != nil { + return err + } + + seen := make(map[string]bool) + + for shard, count := range chunksPerShard { + seen[shard] = true + mx["shard_id_"+shard+"_chunks"] = count + } + + for id := range seen { + if !m.shards[id] { + m.shards[id] = true + m.addShardCharts(id) + } + } + + for id := range m.shards { + if !seen[id] { + delete(m.shards, id) + m.removeShardCharts(id) + } + } + + return nil +} + +func (m *Mongo) addShardCharts(id string) { + charts := chartsTmplShardingShard.Copy() + + for _, chart := range *charts { + chart.ID = fmt.Sprintf(chart.ID, id) + chart.Labels = []module.Label{ + {Key: "shard_id", Value: id}, + } + for _, dim := range chart.Dims { + dim.ID = fmt.Sprintf(dim.ID, id) + } + } + + if err := m.Charts().Add(*charts...); err != nil { + m.Warning(err) + } + +} + +func (m *Mongo) removeShardCharts(id string) { + px := fmt.Sprintf("%s%s_", chartPxShard, id) + + for _, chart := range *m.Charts() { + if strings.HasPrefix(chart.ID, px) { + chart.MarkRemove() + chart.MarkNotCreated() + } + } +} + +func (m *Mongo) addShardingCharts() { + charts := chartsSharding.Copy() + + if err := m.Charts().Add(*charts...); err != nil { + m.Warning(err) + } +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/config_schema.json b/src/go/collectors/go.d.plugin/modules/mongodb/config_schema.json new file mode 100644 index 000000000..406468189 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/config_schema.json @@ -0,0 +1,105 @@ +{ + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MongoDB collector configuration.", + "type": "object", + "properties": { + "update_every": { + "title": "Update every", + "description": "Data collection interval, measured in seconds.", + "type": "integer", + "minimum": 1, + "default": 1 + }, + "uri": { + "title": "URI", + "description": "The MongoDB connection string in the [standard connection string format](https://www.mongodb.com/docs/manual/reference/connection-string/#std-label-connections-standard-connection-string-format).", + "type": "string", + "default": "mongodb://localhost:27017" + }, + "timeout": { + "title": "Timeout", + "description": "Timeout for queries, in seconds.", + "type": "number", + "minimum": 0.5, + "default": 1 + }, + "databases": { + "title": "Database selector", + "description": "Configuration for monitoring specific databases. If left empty, no [database stats](https://docs.mongodb.com/manual/reference/command/dbStats/) will be collected.", + "type": [ + "object", + "null" + ], + "properties": { + "includes": { + "title": "Include", + "description": "Include databases that match any of the specified include [patterns](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/pkg/matcher#readme).", + "type": [ + "array", + "null" + ], + "items": { + "title": "Pattern", + "type": "string" + }, + "uniqueItems": true + }, + "excludes": { + "title": "Exclude", + "description": "Exclude databases that match any of the specified exclude [patterns](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/pkg/matcher#readme).", + "type": [ + "array", + "null" + ], + "items": { + "title": "Pattern", + "type": "string" + }, + "uniqueItems": true + } + } + } + }, + "required": [ + "uri" + ], + "additionalProperties": false, + "patternProperties": { + "^name$": {} + } + }, + "uiSchema": { + "uiOptions": { + "fullPage": true + }, + "uri": { + "ui:placeholder": "mongodb://username:password@host:port" + }, + "timeout": { + "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)." + }, + "databases": { + "ui:help": "The logic for inclusion and exclusion is as follows: `(include1 OR include2) AND !(exclude1 OR exclude2)`." + }, + "ui:flavour": "tabs", + "ui:options": { + "tabs": [ + { + "title": "Base", + "fields": [ + "update_every", + "uri", + "timeout" + ] + }, + { + "title": "Database stats", + "fields": [ + "databases" + ] + } + ] + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/documents.go b/src/go/collectors/go.d.plugin/modules/mongodb/documents.go new file mode 100644 index 000000000..5c95e952e --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/documents.go @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package mongo + +import "time" + +// https://www.mongodb.com/docs/manual/reference/command/serverStatus +type documentServerStatus struct { + Process string `bson:"process"` // mongod|mongos + OpCounters documentOpCounters `bson:"opcounters" stm:"operations"` + OpLatencies *documentOpLatencies `bson:"opLatencies" stm:"operations_latencies"` // mongod only + Connections documentConnections `bson:"connections" stm:"connections"` + Network documentNetwork `bson:"network" stm:"network"` + Memory documentMemory `bson:"mem" stm:"memory"` + Metrics documentMetrics `bson:"metrics" stm:"metrics"` + ExtraInfo documentExtraInfo `bson:"extra_info" stm:"extra_info"` + Asserts documentAsserts `bson:"asserts" stm:"asserts"` + Transactions *documentTransactions `bson:"transactions" stm:"txn"` // mongod in 3.6.3+ and on mongos in 4.2+ + GlobalLock *documentGlobalLock `bson:"globalLock" stm:"global_lock"` + Tcmalloc *documentTCMallocStatus `bson:"tcmalloc" stm:"tcmalloc"` + Locks *documentLocks `bson:"locks" stm:"locks"` + WiredTiger *documentWiredTiger `bson:"wiredTiger" stm:"wiredtiger"` + Repl interface{} `bson:"repl"` +} + +type ( + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#opcounters + documentOpCounters struct { + Insert int64 `bson:"insert" stm:"insert"` + Query int64 `bson:"query" stm:"query"` + Update int64 `bson:"update" stm:"update"` + Delete int64 `bson:"delete" stm:"delete"` + GetMore int64 `bson:"getmore" stm:"getmore"` + Command int64 `bson:"command" stm:"command"` + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#oplatencies + documentOpLatencies struct { + Reads documentLatencyStats `bson:"reads" stm:"reads"` + Writes documentLatencyStats `bson:"writes" stm:"writes"` + Commands documentLatencyStats `bson:"commands" stm:"commands"` + } + // https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/#latencystats-document + documentLatencyStats struct { + Latency int64 `bson:"latency" stm:"latency"` + Ops int64 `bson:"ops" stm:"ops"` + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#connections + documentConnections struct { + Current int64 `bson:"current" stm:"current"` + Available int64 `bson:"available" stm:"available"` + TotalCreated int64 `bson:"totalCreated" stm:"total_created"` + Active *int64 `bson:"active" stm:"active"` + Threaded *int64 `bson:"threaded" stm:"threaded"` + ExhaustIsMaster *int64 `bson:"exhaustIsMaster" stm:"exhaust_is_master"` + ExhaustHello *int64 `bson:"exhaustHello" stm:"exhaust_hello"` + AwaitingTopologyChanges *int64 `bson:"awaitingTopologyChanges" stm:"awaiting_topology_changes"` + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#network + documentNetwork struct { + BytesIn int64 `bson:"bytesIn" stm:"bytes_in"` + BytesOut int64 `bson:"bytesOut" stm:"bytes_out"` + NumRequests int64 `bson:"numRequests" stm:"requests"` + NumSlowDNSOperations *int64 `bson:"numSlowDNSOperations" stm:"slow_dns_operations"` // 4.4+ + NumSlowSSLOperations *int64 `bson:"numSlowSSLOperations" stm:"slow_ssl_operations"` // 4.4+ + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#mem + documentMemory struct { + Resident int64 `bson:"resident" stm:"resident,1048576,1"` + Virtual int64 `bson:"virtual" stm:"virtual,1048576,1"` + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#extra_info + documentExtraInfo struct { + PageFaults int64 `bson:"page_faults" stm:"page_faults"` + } + // Values: + // - mongodb: https://github.com/mongodb/mongo/blob/54e1be7d98aa154e1676d6d652b4d2d1a1073b07/src/mongo/util/tcmalloc_server_status_section.cpp#L88 + // - tcmalloc: https://github.com/google/tcmalloc/blob/927c1433141daa1f0bcf920e6d71bf64795cc2c2/tcmalloc/global_stats.cc#L582 + // formattedString: + // - https://github.com/google/tcmalloc/blob/master/docs/stats.md + // - https://github.com/google/tcmalloc/blob/927c1433141daa1f0bcf920e6d71bf64795cc2c2/tcmalloc/global_stats.cc#L208 + documentTCMallocStatus struct { + Generic *struct { + CurrentAllocatedBytes int64 `bson:"current_allocated_bytes" stm:"current_allocated_bytes"` + HeapSize int64 `bson:"heap_size" stm:"heap_size"` + } `bson:"generic" stm:"generic"` + Tcmalloc *struct { + PageheapFreeBytes int64 `bson:"pageheap_free_bytes" stm:"pageheap_free_bytes"` + PageheapUnmappedBytes int64 `bson:"pageheap_unmapped_bytes" stm:"pageheap_unmapped_bytes"` + MaxTotalThreadCacheBytes int64 `bson:"max_total_thread_cache_bytes" stm:"max_total_thread_cache_bytes"` + CurrentTotalThreadCacheBytes int64 `bson:"current_total_thread_cache_bytes" stm:"current_total_thread_cache_bytes"` + TotalFreeBytes int64 `bson:"total_free_bytes" stm:"total_free_bytes"` + CentralCacheFreeBytes int64 `bson:"central_cache_free_bytes" stm:"central_cache_free_bytes"` + TransferCacheFreeBytes int64 `bson:"transfer_cache_free_bytes" stm:"transfer_cache_free_bytes"` + ThreadCacheFreeBytes int64 `bson:"thread_cache_free_bytes" stm:"thread_cache_free_bytes"` + AggressiveMemoryDecommit int64 `bson:"aggressive_memory_decommit" stm:"aggressive_memory_decommit"` + PageheapCommittedBytes int64 `bson:"pageheap_committed_bytes" stm:"pageheap_committed_bytes"` + PageheapScavengeBytes int64 `bson:"pageheap_scavenge_bytes" stm:"pageheap_scavenge_bytes"` + PageheapCommitCount int64 `bson:"pageheap_commit_count" stm:"pageheap_commit_count"` + PageheapTotalCommitBytes int64 `bson:"pageheap_total_commit_bytes" stm:"pageheap_total_commit_bytes"` + PageheapDecommitCount int64 `bson:"pageheap_decommit_count" stm:"pageheap_decommit_count"` + PageheapTotalDecommitBytes int64 `bson:"pageheap_total_decommit_bytes" stm:"pageheap_total_decommit_bytes"` + PageheapReserveCount int64 `bson:"pageheap_reserve_count" stm:"pageheap_reserve_count"` + PageheapTotalReserveBytes int64 `bson:"pageheap_total_reserve_bytes" stm:"pageheap_total_reserve_bytes"` + SpinlockTotalDelayNs int64 `bson:"spinlock_total_delay_ns" stm:"spinlock_total_delay_ns"` + } `bson:"tcmalloc" stm:""` + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#metrics + documentMetrics struct { + Cursor struct { + TotalOpened *int64 `bson:"totalOpened" stm:"total_opened"` + TimedOut *int64 `bson:"timedOut" stm:"timed_out"` + Open struct { + NoTimeout *int64 `bson:"noTimeout" stm:"no_timeout"` + Total *int64 `bson:"total" stm:"total"` + } `bson:"open" stm:"open"` + Lifespan *struct { + GreaterThanOrEqual10Minutes int64 `bson:"greaterThanOrEqual10Minutes" stm:"greater_than_or_equal_10_minutes"` + LessThan10Minutes int64 `bson:"lessThan10Minutes" stm:"less_than_10_minutes"` + LessThan15Seconds int64 `bson:"lessThan15Seconds" stm:"less_than_15_seconds"` + LessThan1Minute int64 `bson:"lessThan1Minute" stm:"less_than_1_minute"` + LessThan1Second int64 `bson:"lessThan1Second" stm:"less_than_1_second"` + LessThan30Seconds int64 `bson:"lessThan30Seconds" stm:"less_than_30_seconds"` + LessThan5Seconds int64 `bson:"lessThan5Seconds" stm:"less_than_5_seconds"` + } `bson:"lifespan" stm:"lifespan"` + } `bson:"cursor" stm:"cursor"` + Document struct { + Deleted int64 `bson:"deleted" stm:"deleted"` + Inserted int64 `bson:"inserted" stm:"inserted"` + Returned int64 `bson:"returned" stm:"returned"` + Updated int64 `bson:"updated" stm:"updated"` + } `bson:"document" stm:"document"` + QueryExecutor struct { + Scanned int64 `bson:"scanned" stm:"scanned"` + ScannedObjects int64 `bson:"scannedObjects" stm:"scanned_objects"` + } `bson:"queryExecutor" stm:"query_executor"` + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#asserts + documentAsserts struct { + Regular int64 `bson:"regular" stm:"regular"` + Warning int64 `bson:"warning" stm:"warning"` + Msg int64 `bson:"msg" stm:"msg"` + User int64 `bson:"user" stm:"user"` + Tripwire int64 `bson:"tripwire" stm:"tripwire"` + Rollovers int64 `bson:"rollovers" stm:"rollovers"` + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#transactions + documentTransactions struct { + CurrentActive *int64 `bson:"currentActive" stm:"active"` // mongod in 4.0.2+ and mongos in 4.2.1+ + CurrentInactive *int64 `bson:"currentInactive" stm:"inactive"` // mongod in 4.0.2+ and mongos in 4.2.1+ + CurrentOpen *int64 `bson:"currentOpen" stm:"open"` // mongod in 4.0.2+ and mongos in 4.2.1+ + CurrentPrepared *int64 `bson:"currentPrepared" stm:"prepared"` // 4.2+ mongod only + TotalAborted *int64 `bson:"totalAborted" stm:"total_aborted"` // mongod in 4.0.2+ and mongos in 4.2+ + TotalCommitted *int64 `bson:"totalCommitted" stm:"total_committed"` // mongod in 4.0.2+ and mongos in 4.2+ + TotalStarted *int64 `bson:"totalStarted" stm:"total_started"` // mongod in 4.0.2+ and mongos in 4.2+ + TotalPrepared *int64 `bson:"totalPrepared" stm:"total_prepared"` // mongod in 4.0.2+ and mongos in 4.2+ + CommitTypes *documentTransactionsCommitTypes `bson:"commitTypes" stm:"commit_types"` // mongos only + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#mongodb-serverstatus-serverstatus.transactions.commitTypes + documentTransactionsCommitTypes struct { + NoShards documentTransactionsCommitType `bson:"noShards" stm:"no_shards"` + SingleShard documentTransactionsCommitType `bson:"singleShard" stm:"single_shard"` + SingleWriteShard documentTransactionsCommitType `bson:"singleWriteShard" stm:"single_write_shard"` + ReadOnly documentTransactionsCommitType `bson:"readOnly" stm:"read_only"` + TwoPhaseCommit documentTransactionsCommitType `bson:"twoPhaseCommit" stm:"two_phase_commit"` + RecoverWithToken documentTransactionsCommitType `bson:"recoverWithToken" stm:"recover_with_token"` + } + documentTransactionsCommitType struct { + Initiated int64 `json:"initiated" stm:"initiated"` + Successful int64 `json:"successful" stm:"successful"` + SuccessfulDurationMicros int64 `json:"successfulDurationMicros" stm:"successful_duration_micros"` + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#globallock + documentGlobalLock struct { + CurrentQueue *struct { + Readers int64 `bson:"readers" stm:"readers"` + Writers int64 `bson:"writers" stm:"writers"` + } `bson:"currentQueue" stm:"current_queue"` + ActiveClients *struct { + Readers int64 `bson:"readers" stm:"readers"` + Writers int64 `bson:"writers" stm:"writers"` + } `bson:"activeClients" stm:"active_clients"` + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#mongodb-serverstatus-serverstatus.locks + documentLocks struct { + Global *documentLockType `bson:"Global" stm:"global"` + Database *documentLockType `bson:"Database" stm:"database"` + Collection *documentLockType `bson:"Collection" stm:"collection"` + Mutex *documentLockType `bson:"Mutex" stm:"mutex"` + Metadata *documentLockType `bson:"Metadata" stm:"metadata"` + Oplog *documentLockType `bson:"oplog" stm:"oplog"` + } + documentLockType struct { + AcquireCount documentLockModes `bson:"acquireCount" stm:"acquire"` + } + documentLockModes struct { + Shared int64 `bson:"R" stm:"shared"` + Exclusive int64 `bson:"W" stm:"exclusive"` + IntentShared int64 `bson:"r" stm:"intent_shared"` + IntentExclusive int64 `bson:"w" stm:"intent_exclusive"` + } + // https://www.mongodb.com/docs/manual/reference/command/serverStatus/#wiredtiger + documentWiredTiger struct { + ConcurrentTransaction struct { + Write struct { + Out int `bson:"out" stm:"out"` + Available int `bson:"available" stm:"available"` + } `bson:"write" stm:"write"` + Read struct { + Out int `bson:"out" stm:"out"` + Available int `bson:"available" stm:"available"` + } `bson:"read" stm:"read"` + } `bson:"concurrentTransactions" stm:"concurrent_txn"` + Cache struct { + BytesCurrentlyInCache int `bson:"bytes currently in the cache" stm:"currently_in_cache_bytes"` + MaximumBytesConfigured int `bson:"maximum bytes configured" stm:"maximum_configured_bytes"` + TrackedDirtyBytesInCache int `bson:"tracked dirty bytes in the cache" stm:"tracked_dirty_in_the_cache_bytes"` + UnmodifiedPagesEvicted int `bson:"unmodified pages evicted" stm:"unmodified_evicted_pages"` + ModifiedPagesEvicted int `bson:"modified pages evicted" stm:"modified_evicted_pages"` + PagesReadIntoCache int `bson:"pages read into cache" stm:"read_into_cache_pages"` + PagesWrittenFromCache int `bson:"pages written from cache" stm:"written_from_cache_pages"` + } `bson:"cache" stm:"cache"` + } +) + +// https://www.mongodb.com/docs/manual/reference/command/dbStats/ +type documentDBStats struct { + Collections int64 `bson:"collections"` + Views int64 `bson:"views"` + Indexes int64 `bson:"indexes"` + Objects int64 `bson:"objects"` + DataSize int64 `bson:"dataSize"` + IndexSize int64 `bson:"indexSize"` + StorageSize int64 `bson:"storageSize"` +} + +// https://www.mongodb.com/docs/manual/reference/command/replSetGetStatus/ +type documentReplSetStatus struct { + Date time.Time `bson:"date"` + Members []documentReplSetMember `bson:"members"` +} + +type ( + documentReplSetMember struct { + Name string `bson:"name"` + Self *bool `bson:"self"` + State int `bson:"state"` + Health int `bson:"health"` + OptimeDate time.Time `bson:"optimeDate"` + LastHeartbeat *time.Time `bson:"lastHeartbeat"` + LastHeartbeatRecv *time.Time `bson:"lastHeartbeatRecv"` + PingMs *int64 `bson:"pingMs"` + Uptime int64 `bson:"uptime"` + } +) + +type documentAggrResults struct { + Bool bool `bson:"_id"` + Count int64 `bson:"count"` +} + +type ( + documentAggrResult struct { + True int64 + False int64 + } +) + +type documentPartitionedResult struct { + Partitioned int64 + UnPartitioned int64 +} + +type documentShardNodesResult struct { + ShardAware int64 + ShardUnaware int64 +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/init.go b/src/go/collectors/go.d.plugin/modules/mongodb/init.go new file mode 100644 index 000000000..b881e8711 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/init.go @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package mongo + +import ( + "errors" +) + +func (m *Mongo) verifyConfig() error { + if m.URI == "" { + return errors.New("connection URI is empty") + } + + return nil +} + +func (m *Mongo) initDatabaseSelector() error { + if m.Databases.Empty() { + return nil + } + + sr, err := m.Databases.Parse() + if err != nil { + return err + } + m.dbSelector = sr + + return nil +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/integrations/mongodb.md b/src/go/collectors/go.d.plugin/modules/mongodb/integrations/mongodb.md new file mode 100644 index 000000000..ce72671ce --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/integrations/mongodb.md @@ -0,0 +1,356 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/mongodb/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/mongodb/metadata.yaml" +sidebar_label: "MongoDB" +learn_status: "Published" +learn_rel_path: "Collecting Metrics/Databases" +most_popular: False +message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" +endmeta--> + +# MongoDB + + +<img src="https://netdata.cloud/img/mongodb.svg" width="150"/> + + +Plugin: go.d.plugin +Module: mongodb + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +This collector monitors MongoDB servers. + +Executed queries: + +- [serverStatus](https://docs.mongodb.com/manual/reference/command/serverStatus/) +- [dbStats](https://docs.mongodb.com/manual/reference/command/dbStats/) +- [replSetGetStatus](https://www.mongodb.com/docs/manual/reference/command/replSetGetStatus/) + + + + +This collector is supported on all platforms. + +This collector supports collecting metrics from multiple instances of this integration, including remote instances. + + +### Default Behavior + +#### Auto-Detection + +This integration doesn't support auto-detection. + +#### Limits + +The default configuration for this integration does not impose any limits on data collection. + +#### Performance Impact + +The default configuration for this integration is not expected to impose a significant performance impact on the system. + + +## Metrics + +Metrics grouped by *scope*. + +The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels. + +- WireTiger metrics are available only if [WiredTiger](https://docs.mongodb.com/v6.0/core/wiredtiger/) is used as the + storage engine. +- Sharding metrics are available on shards only + for [mongos](https://www.mongodb.com/docs/manual/reference/program/mongos/). + + +### Per MongoDB instance + +These metrics refer to the entire monitored application. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| mongodb.operations_rate | reads, writes, commands | operations/s | +| mongodb.operations_latency_time | reads, writes, commands | milliseconds | +| mongodb.operations_by_type_rate | insert, query, update, delete, getmore, command | operations/s | +| mongodb.document_operations_rate | inserted, deleted, returned, updated | operations/s | +| mongodb.scanned_indexes_rate | scanned | indexes/s | +| mongodb.scanned_documents_rate | scanned | documents/s | +| mongodb.active_clients_count | readers, writers | clients | +| mongodb.queued_operations_count | reads, writes | operations | +| mongodb.cursors_open_count | open | cursors | +| mongodb.cursors_open_no_timeout_count | open_no_timeout | cursors | +| mongodb.cursors_opened_rate | opened | cursors/s | +| mongodb.cursors_timed_out_rate | timed_out | cursors/s | +| mongodb.cursors_by_lifespan_count | le_1s, 1s_5s, 5s_15s, 15s_30s, 30s_1m, 1m_10m, ge_10m | cursors | +| mongodb.transactions_count | active, inactive, open, prepared | transactions | +| mongodb.transactions_rate | started, aborted, committed, prepared | transactions/s | +| mongodb.connections_usage | available, used | connections | +| mongodb.connections_by_state_count | active, threaded, exhaust_is_master, exhaust_hello, awaiting_topology_changes | connections | +| mongodb.connections_rate | created | connections/s | +| mongodb.asserts_rate | regular, warning, msg, user, tripwire, rollovers | asserts/s | +| mongodb.network_traffic_rate | in, out | bytes/s | +| mongodb.network_requests_rate | requests | requests/s | +| mongodb.network_slow_dns_resolutions_rate | slow_dns | resolutions/s | +| mongodb.network_slow_ssl_handshakes_rate | slow_ssl | handshakes/s | +| mongodb.memory_resident_size | used | bytes | +| mongodb.memory_virtual_size | used | bytes | +| mongodb.memory_page_faults_rate | pgfaults | pgfaults/s | +| mongodb.memory_tcmalloc_stats | allocated, central_cache_freelist, transfer_cache_freelist, thread_cache_freelists, pageheap_freelist, pageheap_unmapped | bytes | +| mongodb.wiredtiger_concurrent_read_transactions_usage | available, used | transactions | +| mongodb.wiredtiger_concurrent_write_transactions_usage | available, used | transactions | +| mongodb.wiredtiger_cache_usage | used | bytes | +| mongodb.wiredtiger_cache_dirty_space_size | dirty | bytes | +| mongodb.wiredtiger_cache_io_rate | read, written | pages/s | +| mongodb.wiredtiger_cache_evictions_rate | unmodified, modified | pages/s | +| mongodb.sharding_nodes_count | shard_aware, shard_unaware | nodes | +| mongodb.sharding_sharded_databases_count | partitioned, unpartitioned | databases | +| mongodb.sharding_sharded_collections_count | partitioned, unpartitioned | collections | + +### Per lock type + +These metrics refer to the lock type. + +Labels: + +| Label | Description | +|:-----------|:----------------| +| lock_type | lock type (e.g. global, database, collection, mutex) | + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| mongodb.lock_acquisitions_rate | shared, exclusive, intent_shared, intent_exclusive | acquisitions/s | + +### Per commit type + +These metrics refer to the commit type. + +Labels: + +| Label | Description | +|:-----------|:----------------| +| commit_type | commit type (e.g. noShards, singleShard, singleWriteShard) | + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| mongodb.transactions_commits_rate | success, fail | commits/s | +| mongodb.transactions_commits_duration_time | commits | milliseconds | + +### Per database + +These metrics refer to the database. + +Labels: + +| Label | Description | +|:-----------|:----------------| +| database | database name | + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| mongodb.database_collection_count | collections | collections | +| mongodb.database_indexes_count | indexes | indexes | +| mongodb.database_views_count | views | views | +| mongodb.database_documents_count | documents | documents | +| mongodb.database_data_size | data_size | bytes | +| mongodb.database_storage_size | storage_size | bytes | +| mongodb.database_index_size | index_size | bytes | + +### Per replica set member + +These metrics refer to the replica set member. + +Labels: + +| Label | Description | +|:-----------|:----------------| +| repl_set_member | replica set member name | + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| mongodb.repl_set_member_state | primary, startup, secondary, recovering, startup2, unknown, arbiter, down, rollback, removed | state | +| mongodb.repl_set_member_health_status | up, down | status | +| mongodb.repl_set_member_replication_lag_time | replication_lag | milliseconds | +| mongodb.repl_set_member_heartbeat_latency_time | heartbeat_latency | milliseconds | +| mongodb.repl_set_member_ping_rtt_time | ping_rtt | milliseconds | +| mongodb.repl_set_member_uptime | uptime | seconds | + +### Per shard + +These metrics refer to the shard. + +Labels: + +| Label | Description | +|:-----------|:----------------| +| shard_id | shard id | + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| mongodb.sharding_shard_chunks_count | chunks | chunks | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +#### Create a read-only user + +Create a read-only user for Netdata in the admin database. + +- Authenticate as the admin user: + + ```bash + use admin + db.auth("admin", "<MONGODB_ADMIN_PASSWORD>") + ``` + +- Create a user: + + ```bash + db.createUser({ + "user":"netdata", + "pwd": "<UNIQUE_PASSWORD>", + "roles" : [ + {role: 'read', db: 'admin' }, + {role: 'clusterMonitor', db: 'admin'}, + {role: 'read', db: 'local' } + ] + }) + ``` + + + +### Configuration + +#### File + +The configuration file name for this integration is `go.d/mongodb.conf`. + + +You can edit the configuration file using the `edit-config` script from the +Netdata [config directory](/docs/netdata-agent/configuration/README.md#the-netdata-config-directory). + +```bash +cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata +sudo ./edit-config go.d/mongodb.conf +``` +#### Options + +The following options can be defined globally: update_every, autodetection_retry. + + +<details open><summary>Config options</summary> + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| update_every | Data collection frequency. | 5 | no | +| autodetection_retry | Recheck interval in seconds. Zero means no recheck will be scheduled. | 0 | no | +| uri | MongoDB connection string. See [URI syntax](https://www.mongodb.com/docs/manual/reference/connection-string/). | mongodb://localhost:27017 | yes | +| timeout | Query timeout in seconds. | 1 | no | +| databases | Databases selector. Determines which database metrics will be collected. | | no | + +</details> + +#### Examples + +##### TCP socket + +An example configuration. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + uri: mongodb://netdata:password@localhost:27017 + +``` +</details> + +##### With databases metrics + +An example configuration. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + uri: mongodb://netdata:password@localhost:27017 + databases: + includes: + - "* *" + +``` +</details> + +##### Multi-instance + +> **Note**: When you define multiple jobs, their names must be unique. + +Local and remote instances. + + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + uri: mongodb://netdata:password@localhost:27017 + + - name: remote + uri: mongodb://netdata:password@203.0.113.0:27017 + +``` +</details> + + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `mongodb` collector, run the `go.d.plugin` with the debug option enabled. The output +should give you clues as to why the collector isn't working. + +- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on + your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`. + + ```bash + cd /usr/libexec/netdata/plugins.d/ + ``` + +- Switch to the `netdata` user. + + ```bash + sudo -u netdata -s + ``` + +- Run the `go.d.plugin` to debug the collector: + + ```bash + ./go.d.plugin -d -m mongodb + ``` + + diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/metadata.yaml b/src/go/collectors/go.d.plugin/modules/mongodb/metadata.yaml new file mode 100644 index 000000000..bad65393d --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/metadata.yaml @@ -0,0 +1,580 @@ +plugin_name: go.d.plugin +modules: + - meta: + id: collector-go.d.plugin-mongodb + plugin_name: go.d.plugin + module_name: mongodb + monitored_instance: + name: MongoDB + link: https://www.mongodb.com/ + icon_filename: mongodb.svg + categories: + - data-collection.database-servers + keywords: + - mongodb + - databases + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + most_popular: false + overview: + data_collection: + metrics_description: | + This collector monitors MongoDB servers. + + Executed queries: + + - [serverStatus](https://docs.mongodb.com/manual/reference/command/serverStatus/) + - [dbStats](https://docs.mongodb.com/manual/reference/command/dbStats/) + - [replSetGetStatus](https://www.mongodb.com/docs/manual/reference/command/replSetGetStatus/) + method_description: "" + supported_platforms: + include: [] + exclude: [] + multi_instance: true + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: "" + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: + - title: Create a read-only user + description: | + Create a read-only user for Netdata in the admin database. + + - Authenticate as the admin user: + + ```bash + use admin + db.auth("admin", "<MONGODB_ADMIN_PASSWORD>") + ``` + + - Create a user: + + ```bash + db.createUser({ + "user":"netdata", + "pwd": "<UNIQUE_PASSWORD>", + "roles" : [ + {role: 'read', db: 'admin' }, + {role: 'clusterMonitor', db: 'admin'}, + {role: 'read', db: 'local' } + ] + }) + ``` + configuration: + file: + name: go.d/mongodb.conf + options: + description: | + The following options can be defined globally: update_every, autodetection_retry. + folding: + title: Config options + enabled: true + list: + - name: update_every + description: Data collection frequency. + default_value: 5 + required: false + - name: autodetection_retry + description: Recheck interval in seconds. Zero means no recheck will be scheduled. + default_value: 0 + required: false + - name: uri + description: MongoDB connection string. See [URI syntax](https://www.mongodb.com/docs/manual/reference/connection-string/). + default_value: mongodb://localhost:27017 + required: true + - name: timeout + description: Query timeout in seconds. + default_value: 1 + required: false + - name: databases + description: Databases selector. Determines which database metrics will be collected. + default_value: "" + required: false + details: | + Metrics of databases matching the selector will be collected. + + - Logic: (pattern1 OR pattern2) AND !(pattern3 or pattern4) + - Pattern syntax: [matcher](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/pkg/matcher#supported-format). + - Syntax: + + ```yaml + per_user_stats: + includes: + - pattern1 + - pattern2 + excludes: + - pattern3 + - pattern4 + ``` + examples: + folding: + title: Config + enabled: true + list: + - name: TCP socket + description: An example configuration. + config: | + jobs: + - name: local + uri: mongodb://netdata:password@localhost:27017 + - name: With databases metrics + description: An example configuration. + config: | + jobs: + - name: local + uri: mongodb://netdata:password@localhost:27017 + databases: + includes: + - "* *" + - name: Multi-instance + description: | + > **Note**: When you define multiple jobs, their names must be unique. + + Local and remote instances. + config: | + jobs: + - name: local + uri: mongodb://netdata:password@localhost:27017 + + - name: remote + uri: mongodb://netdata:password@203.0.113.0:27017 + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + availability: [] + description: | + - WireTiger metrics are available only if [WiredTiger](https://docs.mongodb.com/v6.0/core/wiredtiger/) is used as the + storage engine. + - Sharding metrics are available on shards only + for [mongos](https://www.mongodb.com/docs/manual/reference/program/mongos/). + scopes: + - name: global + description: These metrics refer to the entire monitored application. + labels: [] + metrics: + - name: mongodb.operations_rate + description: Operations rate + unit: operations/s + chart_type: line + dimensions: + - name: reads + - name: writes + - name: commands + - name: mongodb.operations_latency_time + description: Operations Latency + unit: milliseconds + chart_type: line + dimensions: + - name: reads + - name: writes + - name: commands + - name: mongodb.operations_by_type_rate + description: Operations by type + unit: operations/s + chart_type: line + dimensions: + - name: insert + - name: query + - name: update + - name: delete + - name: getmore + - name: command + - name: mongodb.document_operations_rate + description: Document operations + unit: operations/s + chart_type: stacked + dimensions: + - name: inserted + - name: deleted + - name: returned + - name: updated + - name: mongodb.scanned_indexes_rate + description: Scanned indexes + unit: indexes/s + chart_type: line + dimensions: + - name: scanned + - name: mongodb.scanned_documents_rate + description: Scanned documents + unit: documents/s + chart_type: line + dimensions: + - name: scanned + - name: mongodb.active_clients_count + description: Connected clients + unit: clients + chart_type: line + dimensions: + - name: readers + - name: writers + - name: mongodb.queued_operations_count + description: Queued operations because of a lock + unit: operations + chart_type: line + dimensions: + - name: reads + - name: writes + - name: mongodb.cursors_open_count + description: Open cursors + unit: cursors + chart_type: line + dimensions: + - name: open + - name: mongodb.cursors_open_no_timeout_count + description: Open cursors with disabled timeout + unit: cursors + chart_type: line + dimensions: + - name: open_no_timeout + - name: mongodb.cursors_opened_rate + description: Opened cursors rate + unit: cursors/s + chart_type: line + dimensions: + - name: opened + - name: mongodb.cursors_timed_out_rate + description: Timed-out cursors + unit: cursors/s + chart_type: line + dimensions: + - name: timed_out + - name: mongodb.cursors_by_lifespan_count + description: Cursors lifespan + unit: cursors + chart_type: stacked + dimensions: + - name: le_1s + - name: 1s_5s + - name: 5s_15s + - name: 15s_30s + - name: 30s_1m + - name: 1m_10m + - name: ge_10m + - name: mongodb.transactions_count + description: Current transactions + unit: transactions + chart_type: line + dimensions: + - name: active + - name: inactive + - name: open + - name: prepared + - name: mongodb.transactions_rate + description: Transactions rate + unit: transactions/s + chart_type: line + dimensions: + - name: started + - name: aborted + - name: committed + - name: prepared + - name: mongodb.connections_usage + description: Connections usage + unit: connections + chart_type: stacked + dimensions: + - name: available + - name: used + - name: mongodb.connections_by_state_count + description: Connections By State + unit: connections + chart_type: line + dimensions: + - name: active + - name: threaded + - name: exhaust_is_master + - name: exhaust_hello + - name: awaiting_topology_changes + - name: mongodb.connections_rate + description: Connections Rate + unit: connections/s + chart_type: line + dimensions: + - name: created + - name: mongodb.asserts_rate + description: Raised assertions + unit: asserts/s + chart_type: stacked + dimensions: + - name: regular + - name: warning + - name: msg + - name: user + - name: tripwire + - name: rollovers + - name: mongodb.network_traffic_rate + description: Network traffic + unit: bytes/s + chart_type: stacked + dimensions: + - name: in + - name: out + - name: mongodb.network_requests_rate + description: Network Requests + unit: requests/s + chart_type: line + dimensions: + - name: requests + - name: mongodb.network_slow_dns_resolutions_rate + description: Slow DNS resolution operations + unit: resolutions/s + chart_type: line + dimensions: + - name: slow_dns + - name: mongodb.network_slow_ssl_handshakes_rate + description: Slow SSL handshake operations + unit: handshakes/s + chart_type: line + dimensions: + - name: slow_ssl + - name: mongodb.memory_resident_size + description: Used resident memory + unit: bytes + chart_type: line + dimensions: + - name: used + - name: mongodb.memory_virtual_size + description: Used virtual memory + unit: bytes + chart_type: line + dimensions: + - name: used + - name: mongodb.memory_page_faults_rate + description: Memory page faults + unit: pgfaults/s + chart_type: line + dimensions: + - name: pgfaults + - name: mongodb.memory_tcmalloc_stats + description: TCMalloc statistics + unit: bytes + chart_type: line + dimensions: + - name: allocated + - name: central_cache_freelist + - name: transfer_cache_freelist + - name: thread_cache_freelists + - name: pageheap_freelist + - name: pageheap_unmapped + - name: mongodb.wiredtiger_concurrent_read_transactions_usage + description: Wired Tiger concurrent read transactions usage + unit: transactions + chart_type: stacked + dimensions: + - name: available + - name: used + - name: mongodb.wiredtiger_concurrent_write_transactions_usage + description: Wired Tiger concurrent write transactions usage + unit: transactions + chart_type: stacked + dimensions: + - name: available + - name: used + - name: mongodb.wiredtiger_cache_usage + description: Wired Tiger cache usage + unit: bytes + chart_type: line + dimensions: + - name: used + - name: mongodb.wiredtiger_cache_dirty_space_size + description: Wired Tiger cache dirty space size + unit: bytes + chart_type: line + dimensions: + - name: dirty + - name: mongodb.wiredtiger_cache_io_rate + description: Wired Tiger IO activity + unit: pages/s + chart_type: line + dimensions: + - name: read + - name: written + - name: mongodb.wiredtiger_cache_evictions_rate + description: Wired Tiger cache evictions + unit: pages/s + chart_type: stacked + dimensions: + - name: unmodified + - name: modified + - name: mongodb.sharding_nodes_count + description: Sharding Nodes + unit: nodes + chart_type: stacked + dimensions: + - name: shard_aware + - name: shard_unaware + - name: mongodb.sharding_sharded_databases_count + description: Sharded databases + unit: databases + chart_type: stacked + dimensions: + - name: partitioned + - name: unpartitioned + - name: mongodb.sharding_sharded_collections_count + description: Sharded collections + unit: collections + chart_type: stacked + dimensions: + - name: partitioned + - name: unpartitioned + - name: lock type + description: These metrics refer to the lock type. + labels: + - name: lock_type + description: lock type (e.g. global, database, collection, mutex) + metrics: + - name: mongodb.lock_acquisitions_rate + description: Lock acquisitions + unit: acquisitions/s + chart_type: line + dimensions: + - name: shared + - name: exclusive + - name: intent_shared + - name: intent_exclusive + - name: commit type + description: These metrics refer to the commit type. + labels: + - name: commit_type + description: commit type (e.g. noShards, singleShard, singleWriteShard) + metrics: + - name: mongodb.transactions_commits_rate + description: Transactions commits + unit: commits/s + chart_type: line + dimensions: + - name: success + - name: fail + - name: mongodb.transactions_commits_duration_time + description: Transactions successful commits duration + unit: milliseconds + chart_type: line + dimensions: + - name: commits + - name: database + description: These metrics refer to the database. + labels: + - name: database + description: database name + metrics: + - name: mongodb.database_collection_count + description: Database collections + unit: collections + chart_type: line + dimensions: + - name: collections + - name: mongodb.database_indexes_count + description: Database indexes + unit: indexes + chart_type: line + dimensions: + - name: indexes + - name: mongodb.database_views_count + description: Database views + unit: views + chart_type: line + dimensions: + - name: views + - name: mongodb.database_documents_count + description: Database documents + unit: documents + chart_type: line + dimensions: + - name: documents + - name: mongodb.database_data_size + description: Database data size + unit: bytes + chart_type: line + dimensions: + - name: data_size + - name: mongodb.database_storage_size + description: Database storage size + unit: bytes + chart_type: line + dimensions: + - name: storage_size + - name: mongodb.database_index_size + description: Database index size + unit: bytes + chart_type: line + dimensions: + - name: index_size + - name: replica set member + description: These metrics refer to the replica set member. + labels: + - name: repl_set_member + description: replica set member name + metrics: + - name: mongodb.repl_set_member_state + description: Replica Set member state + unit: state + chart_type: line + dimensions: + - name: primary + - name: startup + - name: secondary + - name: recovering + - name: startup2 + - name: unknown + - name: arbiter + - name: down + - name: rollback + - name: removed + - name: mongodb.repl_set_member_health_status + description: Replica Set member health status + unit: status + chart_type: line + dimensions: + - name: up + - name: down + - name: mongodb.repl_set_member_replication_lag_time + description: Replica Set member replication lag + unit: milliseconds + chart_type: line + dimensions: + - name: replication_lag + - name: mongodb.repl_set_member_heartbeat_latency_time + description: Replica Set member heartbeat latency + unit: milliseconds + chart_type: line + dimensions: + - name: heartbeat_latency + - name: mongodb.repl_set_member_ping_rtt_time + description: Replica Set member ping RTT + unit: milliseconds + chart_type: line + dimensions: + - name: ping_rtt + - name: mongodb.repl_set_member_uptime + description: Replica Set member uptime + unit: seconds + chart_type: line + dimensions: + - name: uptime + - name: shard + description: These metrics refer to the shard. + labels: + - name: shard_id + description: shard id + metrics: + - name: mongodb.sharding_shard_chunks_count + description: Shard chunks + unit: chunks + chart_type: line + dimensions: + - name: chunks diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/mongodb.go b/src/go/collectors/go.d.plugin/modules/mongodb/mongodb.go new file mode 100644 index 000000000..edc73f96a --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/mongodb.go @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package mongo + +import ( + _ "embed" + "errors" + "sync" + "time" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/matcher" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" +) + +//go:embed "config_schema.json" +var configSchema string + +func init() { + module.Register("mongodb", module.Creator{ + JobConfigSchema: configSchema, + Create: func() module.Module { return New() }, + Config: func() any { return &Config{} }, + }) +} + +func New() *Mongo { + return &Mongo{ + Config: Config{ + URI: "mongodb://localhost:27017", + Timeout: web.Duration(time.Second), + Databases: matcher.SimpleExpr{ + Includes: []string{}, + Excludes: []string{}, + }, + }, + + conn: &mongoClient{}, + + charts: chartsServerStatus.Copy(), + addShardingChartsOnce: &sync.Once{}, + + optionalCharts: make(map[string]bool), + replSetMembers: make(map[string]bool), + databases: make(map[string]bool), + shards: make(map[string]bool), + } +} + +type Config struct { + UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"` + URI string `yaml:"uri" json:"uri"` + Timeout web.Duration `yaml:"timeout,omitempty" json:"timeout"` + Databases matcher.SimpleExpr `yaml:"databases,omitempty" json:"databases"` +} + +type Mongo struct { + module.Base + Config `yaml:",inline" json:""` + + charts *module.Charts + addShardingChartsOnce *sync.Once + + conn mongoConn + + dbSelector matcher.Matcher + optionalCharts map[string]bool + databases map[string]bool + replSetMembers map[string]bool + shards map[string]bool +} + +func (m *Mongo) Configuration() any { + return m.Config +} + +func (m *Mongo) Init() error { + if err := m.verifyConfig(); err != nil { + m.Errorf("config validation: %v", err) + return err + } + + if err := m.initDatabaseSelector(); err != nil { + m.Errorf("init database selector: %v", err) + return err + } + + return nil +} + +func (m *Mongo) Check() error { + mx, err := m.collect() + if err != nil { + m.Error(err) + return err + } + if len(mx) == 0 { + return errors.New("no metrics collected") + } + return nil +} + +func (m *Mongo) Charts() *module.Charts { + return m.charts +} + +func (m *Mongo) Collect() map[string]int64 { + mx, err := m.collect() + if err != nil { + m.Error(err) + } + + if len(mx) == 0 { + m.Warning("no values collected") + return nil + } + + return mx +} + +func (m *Mongo) Cleanup() { + if m.conn == nil { + return + } + if err := m.conn.close(); err != nil { + m.Warningf("cleanup: error on closing mongo conn: %v", err) + } +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/mongodb_test.go b/src/go/collectors/go.d.plugin/modules/mongodb/mongodb_test.go new file mode 100644 index 000000000..c7cf0f42b --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/mongodb_test.go @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package mongo + +import ( + "encoding/json" + "errors" + "os" + "testing" + "time" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/matcher" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + dataConfigJSON, _ = os.ReadFile("testdata/config.json") + dataConfigYAML, _ = os.ReadFile("testdata/config.yaml") + + dataVer6MongodServerStatus, _ = os.ReadFile("testdata/v6.0.3/mongod-serverStatus.json") + dataVer6MongosServerStatus, _ = os.ReadFile("testdata/v6.0.3/mongos-serverStatus.json") + dataVer6DbStats, _ = os.ReadFile("testdata/v6.0.3/dbStats.json") + dataVer6ReplSetGetStatus, _ = os.ReadFile("testdata/v6.0.3/replSetGetStatus.json") +) + +func Test_testDataIsValid(t *testing.T) { + for name, data := range map[string][]byte{ + "dataConfigJSON": dataConfigJSON, + "dataConfigYAML": dataConfigYAML, + "dataVer6MongodServerStatus": dataVer6MongodServerStatus, + "dataVer6MongosServerStatus": dataVer6MongosServerStatus, + "dataVer6DbStats": dataVer6DbStats, + "dataVer6ReplSetGetStatus": dataVer6ReplSetGetStatus, + } { + require.NotNil(t, data, name) + } +} + +func TestMongo_ConfigurationSerialize(t *testing.T) { + module.TestConfigurationSerialize(t, &Mongo{}, dataConfigJSON, dataConfigYAML) +} + +func TestMongo_Init(t *testing.T) { + tests := map[string]struct { + config Config + wantFail bool + }{ + "success on default config": { + wantFail: false, + config: New().Config, + }, + "fails on unset 'address'": { + wantFail: true, + config: Config{ + URI: "", + }, + }, + "fails on invalid database selector": { + wantFail: true, + config: Config{ + URI: "mongodb://localhost:27017", + Databases: matcher.SimpleExpr{ + Includes: []string{"!@#"}, + }, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + mongo := New() + mongo.Config = test.config + + if test.wantFail { + assert.Error(t, mongo.Init()) + } else { + assert.NoError(t, mongo.Init()) + } + }) + } +} + +func TestMongo_Charts(t *testing.T) { + assert.NotNil(t, New().Charts()) +} + +func TestMongo_Cleanup(t *testing.T) { + tests := map[string]struct { + prepare func(t *testing.T) *Mongo + wantClose bool + }{ + "client not initialized": { + wantClose: false, + prepare: func(t *testing.T) *Mongo { + return New() + }, + }, + "client initialized": { + wantClose: true, + prepare: func(t *testing.T) *Mongo { + mongo := New() + mongo.conn = caseMongod() + _ = mongo.conn.initClient("", 0) + + return mongo + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + mongo := test.prepare(t) + + require.NotPanics(t, mongo.Cleanup) + if test.wantClose { + mock, ok := mongo.conn.(*mockMongoClient) + require.True(t, ok) + assert.True(t, mock.closeCalled) + } + }) + } +} + +func TestMongo_Check(t *testing.T) { + tests := map[string]struct { + prepare func() *mockMongoClient + wantFail bool + }{ + "success on Mongod (v6)": { + wantFail: false, + prepare: caseMongod, + }, + "success on Mongod Replicas Set(v6)": { + wantFail: false, + prepare: caseMongodReplicaSet, + }, + "success on Mongos (v6)": { + wantFail: false, + prepare: caseMongos, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + mongo := prepareMongo() + defer mongo.Cleanup() + mongo.conn = test.prepare() + + require.NoError(t, mongo.Init()) + + if test.wantFail { + assert.Error(t, mongo.Check()) + } else { + assert.NoError(t, mongo.Check()) + } + }) + } +} + +func TestMongo_Collect(t *testing.T) { + tests := map[string]struct { + prepare func() *mockMongoClient + wantCollected map[string]int64 + }{ + "success on Mongod (v6)": { + prepare: caseMongod, + wantCollected: map[string]int64{ + "asserts_msg": 0, + "asserts_regular": 0, + "asserts_rollovers": 0, + "asserts_tripwire": 0, + "asserts_user": 246, + "asserts_warning": 0, + "connections_active": 7, + "connections_available": 838841, + "connections_awaiting_topology_changes": 5, + "connections_current": 19, + "connections_exhaust_hello": 2, + "connections_exhaust_is_master": 1, + "connections_threaded": 19, + "connections_total_created": 77, + "database_admin_collections": 3, + "database_admin_data_size": 796, + "database_admin_documents": 5, + "database_admin_index_size": 81920, + "database_admin_indexes": 4, + "database_admin_storage_size": 61440, + "database_admin_views": 0, + "database_config_collections": 3, + "database_config_data_size": 796, + "database_config_documents": 5, + "database_config_index_size": 81920, + "database_config_indexes": 4, + "database_config_storage_size": 61440, + "database_config_views": 0, + "database_local_collections": 3, + "database_local_data_size": 796, + "database_local_documents": 5, + "database_local_index_size": 81920, + "database_local_indexes": 4, + "database_local_storage_size": 61440, + "database_local_views": 0, + "extra_info_page_faults": 0, + "global_lock_active_clients_readers": 0, + "global_lock_active_clients_writers": 0, + "global_lock_current_queue_readers": 0, + "global_lock_current_queue_writers": 0, + "locks_collection_acquire_exclusive": 6, + "locks_collection_acquire_intent_exclusive": 172523, + "locks_collection_acquire_intent_shared": 336370, + "locks_collection_acquire_shared": 0, + "locks_database_acquire_exclusive": 3, + "locks_database_acquire_intent_exclusive": 172539, + "locks_database_acquire_intent_shared": 50971, + "locks_database_acquire_shared": 0, + "locks_global_acquire_exclusive": 6, + "locks_global_acquire_intent_exclusive": 174228, + "locks_global_acquire_intent_shared": 437905, + "locks_global_acquire_shared": 0, + "locks_mutex_acquire_exclusive": 0, + "locks_mutex_acquire_intent_exclusive": 0, + "locks_mutex_acquire_intent_shared": 245077, + "locks_mutex_acquire_shared": 0, + "locks_oplog_acquire_exclusive": 0, + "locks_oplog_acquire_intent_exclusive": 1, + "locks_oplog_acquire_intent_shared": 16788, + "locks_oplog_acquire_shared": 0, + "memory_resident": 193986560, + "memory_virtual": 3023044608, + "metrics_cursor_lifespan_greater_than_or_equal_10_minutes": 0, + "metrics_cursor_lifespan_less_than_10_minutes": 0, + "metrics_cursor_lifespan_less_than_15_seconds": 0, + "metrics_cursor_lifespan_less_than_1_minute": 0, + "metrics_cursor_lifespan_less_than_1_second": 0, + "metrics_cursor_lifespan_less_than_30_seconds": 0, + "metrics_cursor_lifespan_less_than_5_seconds": 0, + "metrics_cursor_open_no_timeout": 0, + "metrics_cursor_open_total": 1, + "metrics_cursor_timed_out": 0, + "metrics_cursor_total_opened": 1, + "metrics_document_deleted": 7, + "metrics_document_inserted": 0, + "metrics_document_returned": 1699, + "metrics_document_updated": 52, + "metrics_query_executor_scanned": 61, + "metrics_query_executor_scanned_objects": 1760, + "network_bytes_in": 38851356, + "network_bytes_out": 706335836, + "network_requests": 130530, + "network_slow_dns_operations": 0, + "network_slow_ssl_operations": 0, + "operations_command": 125531, + "operations_delete": 7, + "operations_getmore": 5110, + "operations_insert": 0, + "operations_latencies_commands_latency": 46432082, + "operations_latencies_commands_ops": 125412, + "operations_latencies_reads_latency": 1009868, + "operations_latencies_reads_ops": 5111, + "operations_latencies_writes_latency": 0, + "operations_latencies_writes_ops": 0, + "operations_query": 76, + "operations_update": 59, + "tcmalloc_aggressive_memory_decommit": 0, + "tcmalloc_central_cache_free_bytes": 406680, + "tcmalloc_current_total_thread_cache_bytes": 2490832, + "tcmalloc_generic_current_allocated_bytes": 109050648, + "tcmalloc_generic_heap_size": 127213568, + "tcmalloc_max_total_thread_cache_bytes": 1073741824, + "tcmalloc_pageheap_commit_count": 376, + "tcmalloc_pageheap_committed_bytes": 127086592, + "tcmalloc_pageheap_decommit_count": 122, + "tcmalloc_pageheap_free_bytes": 13959168, + "tcmalloc_pageheap_reserve_count": 60, + "tcmalloc_pageheap_scavenge_bytes": 0, + "tcmalloc_pageheap_total_commit_bytes": 229060608, + "tcmalloc_pageheap_total_decommit_bytes": 101974016, + "tcmalloc_pageheap_total_reserve_bytes": 127213568, + "tcmalloc_pageheap_unmapped_bytes": 126976, + "tcmalloc_spinlock_total_delay_ns": 33426251, + "tcmalloc_thread_cache_free_bytes": 2490832, + "tcmalloc_total_free_bytes": 4076776, + "tcmalloc_transfer_cache_free_bytes": 1179264, + "txn_active": 0, + "txn_inactive": 0, + "txn_open": 0, + "txn_prepared": 0, + "txn_total_aborted": 0, + "txn_total_committed": 0, + "txn_total_prepared": 0, + "txn_total_started": 0, + "wiredtiger_cache_currently_in_cache_bytes": 814375, + "wiredtiger_cache_maximum_configured_bytes": 7854882816, + "wiredtiger_cache_modified_evicted_pages": 0, + "wiredtiger_cache_read_into_cache_pages": 108, + "wiredtiger_cache_tracked_dirty_in_the_cache_bytes": 456446, + "wiredtiger_cache_unmodified_evicted_pages": 0, + "wiredtiger_cache_written_from_cache_pages": 3177, + "wiredtiger_concurrent_txn_read_available": 128, + "wiredtiger_concurrent_txn_read_out": 0, + "wiredtiger_concurrent_txn_write_available": 128, + "wiredtiger_concurrent_txn_write_out": 0, + }, + }, + "success on Mongod Replica Set (v6)": { + prepare: caseMongodReplicaSet, + wantCollected: map[string]int64{ + "asserts_msg": 0, + "asserts_regular": 0, + "asserts_rollovers": 0, + "asserts_tripwire": 0, + "asserts_user": 246, + "asserts_warning": 0, + "connections_active": 7, + "connections_available": 838841, + "connections_awaiting_topology_changes": 5, + "connections_current": 19, + "connections_exhaust_hello": 2, + "connections_exhaust_is_master": 1, + "connections_threaded": 19, + "connections_total_created": 77, + "database_admin_collections": 3, + "database_admin_data_size": 796, + "database_admin_documents": 5, + "database_admin_index_size": 81920, + "database_admin_indexes": 4, + "database_admin_storage_size": 61440, + "database_admin_views": 0, + "database_config_collections": 3, + "database_config_data_size": 796, + "database_config_documents": 5, + "database_config_index_size": 81920, + "database_config_indexes": 4, + "database_config_storage_size": 61440, + "database_config_views": 0, + "database_local_collections": 3, + "database_local_data_size": 796, + "database_local_documents": 5, + "database_local_index_size": 81920, + "database_local_indexes": 4, + "database_local_storage_size": 61440, + "database_local_views": 0, + "extra_info_page_faults": 0, + "global_lock_active_clients_readers": 0, + "global_lock_active_clients_writers": 0, + "global_lock_current_queue_readers": 0, + "global_lock_current_queue_writers": 0, + "locks_collection_acquire_exclusive": 6, + "locks_collection_acquire_intent_exclusive": 172523, + "locks_collection_acquire_intent_shared": 336370, + "locks_collection_acquire_shared": 0, + "locks_database_acquire_exclusive": 3, + "locks_database_acquire_intent_exclusive": 172539, + "locks_database_acquire_intent_shared": 50971, + "locks_database_acquire_shared": 0, + "locks_global_acquire_exclusive": 6, + "locks_global_acquire_intent_exclusive": 174228, + "locks_global_acquire_intent_shared": 437905, + "locks_global_acquire_shared": 0, + "locks_mutex_acquire_exclusive": 0, + "locks_mutex_acquire_intent_exclusive": 0, + "locks_mutex_acquire_intent_shared": 245077, + "locks_mutex_acquire_shared": 0, + "locks_oplog_acquire_exclusive": 0, + "locks_oplog_acquire_intent_exclusive": 1, + "locks_oplog_acquire_intent_shared": 16788, + "locks_oplog_acquire_shared": 0, + "memory_resident": 193986560, + "memory_virtual": 3023044608, + "metrics_cursor_lifespan_greater_than_or_equal_10_minutes": 0, + "metrics_cursor_lifespan_less_than_10_minutes": 0, + "metrics_cursor_lifespan_less_than_15_seconds": 0, + "metrics_cursor_lifespan_less_than_1_minute": 0, + "metrics_cursor_lifespan_less_than_1_second": 0, + "metrics_cursor_lifespan_less_than_30_seconds": 0, + "metrics_cursor_lifespan_less_than_5_seconds": 0, + "metrics_cursor_open_no_timeout": 0, + "metrics_cursor_open_total": 1, + "metrics_cursor_timed_out": 0, + "metrics_cursor_total_opened": 1, + "metrics_document_deleted": 7, + "metrics_document_inserted": 0, + "metrics_document_returned": 1699, + "metrics_document_updated": 52, + "metrics_query_executor_scanned": 61, + "metrics_query_executor_scanned_objects": 1760, + "network_bytes_in": 38851356, + "network_bytes_out": 706335836, + "network_requests": 130530, + "network_slow_dns_operations": 0, + "network_slow_ssl_operations": 0, + "operations_command": 125531, + "operations_delete": 7, + "operations_getmore": 5110, + "operations_insert": 0, + "operations_latencies_commands_latency": 46432082, + "operations_latencies_commands_ops": 125412, + "operations_latencies_reads_latency": 1009868, + "operations_latencies_reads_ops": 5111, + "operations_latencies_writes_latency": 0, + "operations_latencies_writes_ops": 0, + "operations_query": 76, + "operations_update": 59, + "repl_set_member_mongodb-primary:27017_health_status_down": 0, + "repl_set_member_mongodb-primary:27017_health_status_up": 1, + "repl_set_member_mongodb-primary:27017_replication_lag": 4572, + "repl_set_member_mongodb-primary:27017_state_arbiter": 0, + "repl_set_member_mongodb-primary:27017_state_down": 0, + "repl_set_member_mongodb-primary:27017_state_primary": 1, + "repl_set_member_mongodb-primary:27017_state_recovering": 0, + "repl_set_member_mongodb-primary:27017_state_removed": 0, + "repl_set_member_mongodb-primary:27017_state_rollback": 0, + "repl_set_member_mongodb-primary:27017_state_secondary": 0, + "repl_set_member_mongodb-primary:27017_state_startup": 0, + "repl_set_member_mongodb-primary:27017_state_startup2": 0, + "repl_set_member_mongodb-primary:27017_state_unknown": 0, + "repl_set_member_mongodb-secondary:27017_health_status_down": 0, + "repl_set_member_mongodb-secondary:27017_health_status_up": 1, + "repl_set_member_mongodb-secondary:27017_heartbeat_latency": 1359, + "repl_set_member_mongodb-secondary:27017_ping_rtt": 0, + "repl_set_member_mongodb-secondary:27017_replication_lag": 4572, + "repl_set_member_mongodb-secondary:27017_state_arbiter": 0, + "repl_set_member_mongodb-secondary:27017_state_down": 0, + "repl_set_member_mongodb-secondary:27017_state_primary": 0, + "repl_set_member_mongodb-secondary:27017_state_recovering": 0, + "repl_set_member_mongodb-secondary:27017_state_removed": 0, + "repl_set_member_mongodb-secondary:27017_state_rollback": 0, + "repl_set_member_mongodb-secondary:27017_state_secondary": 1, + "repl_set_member_mongodb-secondary:27017_state_startup": 0, + "repl_set_member_mongodb-secondary:27017_state_startup2": 0, + "repl_set_member_mongodb-secondary:27017_state_unknown": 0, + "repl_set_member_mongodb-secondary:27017_uptime": 192370, + "tcmalloc_aggressive_memory_decommit": 0, + "tcmalloc_central_cache_free_bytes": 406680, + "tcmalloc_current_total_thread_cache_bytes": 2490832, + "tcmalloc_generic_current_allocated_bytes": 109050648, + "tcmalloc_generic_heap_size": 127213568, + "tcmalloc_max_total_thread_cache_bytes": 1073741824, + "tcmalloc_pageheap_commit_count": 376, + "tcmalloc_pageheap_committed_bytes": 127086592, + "tcmalloc_pageheap_decommit_count": 122, + "tcmalloc_pageheap_free_bytes": 13959168, + "tcmalloc_pageheap_reserve_count": 60, + "tcmalloc_pageheap_scavenge_bytes": 0, + "tcmalloc_pageheap_total_commit_bytes": 229060608, + "tcmalloc_pageheap_total_decommit_bytes": 101974016, + "tcmalloc_pageheap_total_reserve_bytes": 127213568, + "tcmalloc_pageheap_unmapped_bytes": 126976, + "tcmalloc_spinlock_total_delay_ns": 33426251, + "tcmalloc_thread_cache_free_bytes": 2490832, + "tcmalloc_total_free_bytes": 4076776, + "tcmalloc_transfer_cache_free_bytes": 1179264, + "txn_active": 0, + "txn_inactive": 0, + "txn_open": 0, + "txn_prepared": 0, + "txn_total_aborted": 0, + "txn_total_committed": 0, + "txn_total_prepared": 0, + "txn_total_started": 0, + "wiredtiger_cache_currently_in_cache_bytes": 814375, + "wiredtiger_cache_maximum_configured_bytes": 7854882816, + "wiredtiger_cache_modified_evicted_pages": 0, + "wiredtiger_cache_read_into_cache_pages": 108, + "wiredtiger_cache_tracked_dirty_in_the_cache_bytes": 456446, + "wiredtiger_cache_unmodified_evicted_pages": 0, + "wiredtiger_cache_written_from_cache_pages": 3177, + "wiredtiger_concurrent_txn_read_available": 128, + "wiredtiger_concurrent_txn_read_out": 0, + "wiredtiger_concurrent_txn_write_available": 128, + "wiredtiger_concurrent_txn_write_out": 0, + }, + }, + "success on Mongos (v6)": { + prepare: caseMongos, + wantCollected: map[string]int64{ + "asserts_msg": 0, + "asserts_regular": 0, + "asserts_rollovers": 0, + "asserts_tripwire": 0, + "asserts_user": 352, + "asserts_warning": 0, + "connections_active": 5, + "connections_available": 838842, + "connections_awaiting_topology_changes": 4, + "connections_current": 18, + "connections_exhaust_hello": 3, + "connections_exhaust_is_master": 0, + "connections_threaded": 18, + "connections_total_created": 89, + "database_admin_collections": 3, + "database_admin_data_size": 796, + "database_admin_documents": 5, + "database_admin_index_size": 81920, + "database_admin_indexes": 4, + "database_admin_storage_size": 61440, + "database_admin_views": 0, + "database_config_collections": 3, + "database_config_data_size": 796, + "database_config_documents": 5, + "database_config_index_size": 81920, + "database_config_indexes": 4, + "database_config_storage_size": 61440, + "database_config_views": 0, + "database_local_collections": 3, + "database_local_data_size": 796, + "database_local_documents": 5, + "database_local_index_size": 81920, + "database_local_indexes": 4, + "database_local_storage_size": 61440, + "database_local_views": 0, + "extra_info_page_faults": 526, + "memory_resident": 84934656, + "memory_virtual": 2596274176, + "metrics_document_deleted": 0, + "metrics_document_inserted": 0, + "metrics_document_returned": 0, + "metrics_document_updated": 0, + "metrics_query_executor_scanned": 0, + "metrics_query_executor_scanned_objects": 0, + "network_bytes_in": 57943348, + "network_bytes_out": 247343709, + "network_requests": 227310, + "network_slow_dns_operations": 0, + "network_slow_ssl_operations": 0, + "operations_command": 227283, + "operations_delete": 0, + "operations_getmore": 0, + "operations_insert": 0, + "operations_query": 10, + "operations_update": 0, + "shard_collections_partitioned": 1, + "shard_collections_unpartitioned": 1, + "shard_databases_partitioned": 1, + "shard_databases_unpartitioned": 1, + "shard_id_shard0_chunks": 1, + "shard_id_shard1_chunks": 1, + "shard_nodes_aware": 1, + "shard_nodes_unaware": 1, + "tcmalloc_aggressive_memory_decommit": 0, + "tcmalloc_central_cache_free_bytes": 736960, + "tcmalloc_current_total_thread_cache_bytes": 1638104, + "tcmalloc_generic_current_allocated_bytes": 13519784, + "tcmalloc_generic_heap_size": 24576000, + "tcmalloc_max_total_thread_cache_bytes": 1042284544, + "tcmalloc_pageheap_commit_count": 480, + "tcmalloc_pageheap_committed_bytes": 24518656, + "tcmalloc_pageheap_decommit_count": 127, + "tcmalloc_pageheap_free_bytes": 5697536, + "tcmalloc_pageheap_reserve_count": 15, + "tcmalloc_pageheap_scavenge_bytes": 0, + "tcmalloc_pageheap_total_commit_bytes": 84799488, + "tcmalloc_pageheap_total_decommit_bytes": 60280832, + "tcmalloc_pageheap_total_reserve_bytes": 24576000, + "tcmalloc_pageheap_unmapped_bytes": 57344, + "tcmalloc_spinlock_total_delay_ns": 96785212, + "tcmalloc_thread_cache_free_bytes": 1638104, + "tcmalloc_total_free_bytes": 5301336, + "tcmalloc_transfer_cache_free_bytes": 2926272, + "txn_active": 0, + "txn_commit_types_no_shards_initiated": 0, + "txn_commit_types_no_shards_successful": 0, + "txn_commit_types_no_shards_successful_duration_micros": 0, + "txn_commit_types_no_shards_unsuccessful": 0, + "txn_commit_types_read_only_initiated": 0, + "txn_commit_types_read_only_successful": 0, + "txn_commit_types_read_only_successful_duration_micros": 0, + "txn_commit_types_read_only_unsuccessful": 0, + "txn_commit_types_recover_with_token_initiated": 0, + "txn_commit_types_recover_with_token_successful": 0, + "txn_commit_types_recover_with_token_successful_duration_micros": 0, + "txn_commit_types_recover_with_token_unsuccessful": 0, + "txn_commit_types_single_shard_initiated": 0, + "txn_commit_types_single_shard_successful": 0, + "txn_commit_types_single_shard_successful_duration_micros": 0, + "txn_commit_types_single_shard_unsuccessful": 0, + "txn_commit_types_single_write_shard_initiated": 0, + "txn_commit_types_single_write_shard_successful": 0, + "txn_commit_types_single_write_shard_successful_duration_micros": 0, + "txn_commit_types_single_write_shard_unsuccessful": 0, + "txn_commit_types_two_phase_commit_initiated": 0, + "txn_commit_types_two_phase_commit_successful": 0, + "txn_commit_types_two_phase_commit_successful_duration_micros": 0, + "txn_commit_types_two_phase_commit_unsuccessful": 0, + "txn_inactive": 0, + "txn_open": 0, + "txn_total_aborted": 0, + "txn_total_committed": 0, + "txn_total_started": 0, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + mongo := prepareMongo() + defer mongo.Cleanup() + mongo.conn = test.prepare() + + require.NoError(t, mongo.Init()) + + mx := mongo.Collect() + + assert.Equal(t, test.wantCollected, mx) + }) + } +} + +func prepareMongo() *Mongo { + m := New() + m.Databases = matcher.SimpleExpr{Includes: []string{"* *"}} + return m +} + +func caseMongodReplicaSet() *mockMongoClient { + return &mockMongoClient{replicaSet: true} +} + +func caseMongod() *mockMongoClient { + return &mockMongoClient{} +} + +func caseMongos() *mockMongoClient { + return &mockMongoClient{mongos: true} +} + +type mockMongoClient struct { + replicaSet bool + mongos bool + errOnServerStatus bool + errOnListDatabaseNames bool + errOnDbStats bool + errOnReplSetGetStatus bool + errOnShardNodes bool + errOnShardDatabasesPartitioning bool + errOnShardCollectionsPartitioning bool + errOnShardChunks bool + errOnInitClient bool + clientInited bool + closeCalled bool +} + +func (m *mockMongoClient) serverStatus() (*documentServerStatus, error) { + if !m.clientInited { + return nil, errors.New("mock.serverStatus() error: mongo client not inited") + } + if m.errOnServerStatus { + return nil, errors.New("mock.serverStatus() error") + } + + data := dataVer6MongodServerStatus + if m.mongos { + data = dataVer6MongosServerStatus + } + + var s documentServerStatus + if err := json.Unmarshal(data, &s); err != nil { + return nil, err + } + + return &s, nil +} + +func (m *mockMongoClient) listDatabaseNames() ([]string, error) { + if !m.clientInited { + return nil, errors.New("mock.listDatabaseNames() error: mongo client not inited") + } + if m.errOnListDatabaseNames { + return nil, errors.New("mock.listDatabaseNames() error") + } + return []string{"admin", "config", "local"}, nil +} + +func (m *mockMongoClient) dbStats(_ string) (*documentDBStats, error) { + if !m.clientInited { + return nil, errors.New("mock.dbStats() error: mongo client not inited") + } + if m.errOnDbStats { + return nil, errors.New("mock.dbStats() error") + } + + var s documentDBStats + if err := json.Unmarshal(dataVer6DbStats, &s); err != nil { + return nil, err + } + + return &s, nil +} + +func (m *mockMongoClient) isReplicaSet() bool { + return m.replicaSet +} + +func (m *mockMongoClient) isMongos() bool { + return m.mongos +} + +func (m *mockMongoClient) replSetGetStatus() (*documentReplSetStatus, error) { + if !m.clientInited { + return nil, errors.New("mock.replSetGetStatus() error: mongo client not inited") + } + if m.mongos { + return nil, errors.New("mock.replSetGetStatus() error: shouldn't be called for mongos") + } + if !m.replicaSet { + return nil, errors.New("mock.replSetGetStatus() error: should be called for replica set") + } + if m.errOnReplSetGetStatus { + return nil, errors.New("mock.replSetGetStatus() error") + } + + var s documentReplSetStatus + if err := json.Unmarshal(dataVer6ReplSetGetStatus, &s); err != nil { + return nil, err + } + + return &s, nil +} + +func (m *mockMongoClient) shardNodes() (*documentShardNodesResult, error) { + if !m.clientInited { + return nil, errors.New("mock.shardNodes() error: mongo client not inited") + } + if m.replicaSet { + return nil, errors.New("mock.replSetGetStatus() error: shouldn't be called for replica set") + } + if !m.mongos { + return nil, errors.New("mock.shardNodes() error: should be called for mongos") + } + if m.errOnShardNodes { + return nil, errors.New("mock.shardNodes() error") + } + + return &documentShardNodesResult{ + ShardAware: 1, + ShardUnaware: 1, + }, nil +} + +func (m *mockMongoClient) shardDatabasesPartitioning() (*documentPartitionedResult, error) { + if !m.clientInited { + return nil, errors.New("mock.shardDatabasesPartitioning() error: mongo client not inited") + } + if m.replicaSet { + return nil, errors.New("mock.shardDatabasesPartitioning() error: shouldn't be called for replica set") + } + if !m.mongos { + return nil, errors.New("mock.shardDatabasesPartitioning() error: should be called for mongos") + } + if m.errOnShardDatabasesPartitioning { + return nil, errors.New("mock.shardDatabasesPartitioning() error") + } + + return &documentPartitionedResult{ + Partitioned: 1, + UnPartitioned: 1, + }, nil +} + +func (m *mockMongoClient) shardCollectionsPartitioning() (*documentPartitionedResult, error) { + if !m.clientInited { + return nil, errors.New("mock.shardCollectionsPartitioning() error: mongo client not inited") + } + if m.replicaSet { + return nil, errors.New("mock.shardCollectionsPartitioning() error: shouldn't be called for replica set") + } + if !m.mongos { + return nil, errors.New("mock.shardCollectionsPartitioning() error: should be called for mongos") + } + if m.errOnShardCollectionsPartitioning { + return nil, errors.New("mock.shardCollectionsPartitioning() error") + } + + return &documentPartitionedResult{ + Partitioned: 1, + UnPartitioned: 1, + }, nil +} + +func (m *mockMongoClient) shardChunks() (map[string]int64, error) { + if !m.clientInited { + return nil, errors.New("mock.shardChunks() error: mongo client not inited") + } + if m.replicaSet { + return nil, errors.New("mock.shardChunks() error: shouldn't be called for replica set") + } + if !m.mongos { + return nil, errors.New("mock.shardChunks() error: should be called for mongos") + } + if m.errOnShardChunks { + return nil, errors.New("mock.shardChunks() error") + } + + return map[string]int64{ + "shard0": 1, + "shard1": 1, + }, nil +} + +func (m *mockMongoClient) initClient(_ string, _ time.Duration) error { + if m.errOnInitClient { + return errors.New("mock.initClient() error") + } + m.clientInited = true + return nil +} + +func (m *mockMongoClient) close() error { + if m.clientInited { + m.closeCalled = true + } + return nil +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/testdata/config.json b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/config.json new file mode 100644 index 000000000..bc3f94d81 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/config.json @@ -0,0 +1,13 @@ +{ + "update_every": 1, + "uri": "ok", + "timeout": 123.123, + "databases": { + "includes": [ + "ok" + ], + "excludes": [ + "ok" + ] + } +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/testdata/config.yaml b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/config.yaml new file mode 100644 index 000000000..03a11029c --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/config.yaml @@ -0,0 +1,8 @@ +update_every: 1 +uri: "ok" +timeout: 123.123 +databases: + includes: + - "ok" + excludes: + - "ok" diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/dbStats.json b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/dbStats.json new file mode 100644 index 000000000..52a513203 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/dbStats.json @@ -0,0 +1,9 @@ +{ + "Collections": 3, + "Views": 0, + "Indexes": 4, + "Objects": 5, + "DataSize": 796, + "IndexSize": 81920, + "StorageSize": 61440 +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/mongod-serverStatus.json b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/mongod-serverStatus.json new file mode 100644 index 000000000..77f083923 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/mongod-serverStatus.json @@ -0,0 +1,497 @@ +{ + "Process": "mongod", + "OpCounters": { + "Insert": 0, + "Query": 76, + "Update": 59, + "Delete": 7, + "GetMore": 5110, + "Command": 125531 + }, + "OpLatencies": { + "Reads": { + "Latency": 1009868, + "Ops": 5111 + }, + "Writes": { + "Latency": 0, + "Ops": 0 + }, + "Commands": { + "Latency": 46432082, + "Ops": 125412 + } + }, + "Connections": { + "Current": 19, + "Available": 838841, + "TotalCreated": 77, + "Active": 7, + "Threaded": 19, + "ExhaustIsMaster": 1, + "ExhaustHello": 2, + "AwaitingTopologyChanges": 5 + }, + "Network": { + "BytesIn": 38851356, + "BytesOut": 706335836, + "NumRequests": 130530, + "NumSlowDNSOperations": 0, + "NumSlowSSLOperations": 0 + }, + "Memory": { + "Resident": 185, + "Virtual": 2883 + }, + "Metrics": { + "Cursor": { + "TotalOpened": 1, + "TimedOut": 0, + "Open": { + "NoTimeout": 0, + "Total": 1 + }, + "Lifespan": { + "GreaterThanOrEqual10Minutes": 0, + "LessThan10Minutes": 0, + "LessThan15Seconds": 0, + "LessThan1Minute": 0, + "LessThan1Second": 0, + "LessThan30Seconds": 0, + "LessThan5Seconds": 0 + } + }, + "Document": { + "Deleted": 7, + "Inserted": 0, + "Returned": 1699, + "Updated": 52 + }, + "QueryExecutor": { + "Scanned": 61, + "ScannedObjects": 1760 + } + }, + "ExtraInfo": { + "PageFaults": 0 + }, + "Asserts": { + "Regular": 0, + "Warning": 0, + "Msg": 0, + "User": 246, + "Tripwire": 0, + "Rollovers": 0 + }, + "Transactions": { + "CurrentActive": 0, + "CurrentInactive": 0, + "CurrentOpen": 0, + "CurrentPrepared": 0, + "TotalAborted": 0, + "TotalCommitted": 0, + "TotalStarted": 0, + "TotalPrepared": 0, + "CommitTypes": null + }, + "GlobalLock": { + "CurrentQueue": { + "Readers": 0, + "Writers": 0 + }, + "ActiveClients": { + "Readers": 0, + "Writers": 0 + } + }, + "Tcmalloc": { + "Generic": { + "CurrentAllocatedBytes": 109050648, + "HeapSize": 127213568 + }, + "Tcmalloc": { + "PageheapFreeBytes": 13959168, + "PageheapUnmappedBytes": 126976, + "MaxTotalThreadCacheBytes": 1073741824, + "CurrentTotalThreadCacheBytes": 2490832, + "TotalFreeBytes": 4076776, + "CentralCacheFreeBytes": 406680, + "TransferCacheFreeBytes": 1179264, + "ThreadCacheFreeBytes": 2490832, + "AggressiveMemoryDecommit": 0, + "PageheapCommittedBytes": 127086592, + "PageheapScavengeBytes": 0, + "PageheapCommitCount": 376, + "PageheapTotalCommitBytes": 229060608, + "PageheapDecommitCount": 122, + "PageheapTotalDecommitBytes": 101974016, + "PageheapReserveCount": 60, + "PageheapTotalReserveBytes": 127213568, + "SpinlockTotalDelayNs": 33426251 + } + }, + "Locks": { + "Global": { + "AcquireCount": { + "Shared": 0, + "Exclusive": 6, + "IntentShared": 437905, + "IntentExclusive": 174228 + } + }, + "Database": { + "AcquireCount": { + "Shared": 0, + "Exclusive": 3, + "IntentShared": 50971, + "IntentExclusive": 172539 + } + }, + "Collection": { + "AcquireCount": { + "Shared": 0, + "Exclusive": 6, + "IntentShared": 336370, + "IntentExclusive": 172523 + } + }, + "Mutex": { + "AcquireCount": { + "Shared": 0, + "Exclusive": 0, + "IntentShared": 245077, + "IntentExclusive": 0 + } + }, + "Metadata": null, + "Oplog": { + "AcquireCount": { + "Shared": 0, + "Exclusive": 0, + "IntentShared": 16788, + "IntentExclusive": 1 + } + } + }, + "WiredTiger": { + "ConcurrentTransaction": { + "Write": { + "Out": 0, + "Available": 128 + }, + "Read": { + "Out": 0, + "Available": 128 + } + }, + "Cache": { + "BytesCurrentlyInCache": 814375, + "MaximumBytesConfigured": 7854882816, + "TrackedDirtyBytesInCache": 456446, + "UnmodifiedPagesEvicted": 0, + "ModifiedPagesEvicted": 0, + "PagesReadIntoCache": 108, + "PagesWrittenFromCache": 3177 + } + }, + "Repl": [ + { + "Key": "topologyVersion", + "Value": [ + { + "Key": "processId", + "Value": "63b043be562288304ad3b4fe" + }, + { + "Key": "counter", + "Value": 7 + } + ] + }, + { + "Key": "hosts", + "Value": [ + "mongodb-primary:27017", + "mongodb-secondary:27017" + ] + }, + { + "Key": "setName", + "Value": "replicaset" + }, + { + "Key": "setVersion", + "Value": 4 + }, + { + "Key": "isWritablePrimary", + "Value": true + }, + { + "Key": "secondary", + "Value": false + }, + { + "Key": "primary", + "Value": "mongodb-primary:27017" + }, + { + "Key": "me", + "Value": "mongodb-primary:27017" + }, + { + "Key": "electionId", + "Value": "7fffffff0000000000000006" + }, + { + "Key": "lastWrite", + "Value": [ + { + "Key": "opTime", + "Value": [ + { + "Key": "ts", + "Value": { + "T": 1672512884, + "I": 1 + } + }, + { + "Key": "t", + "Value": 6 + } + ] + }, + { + "Key": "lastWriteDate", + "Value": "2022-12-31T20:54:44+02:00" + }, + { + "Key": "majorityOpTime", + "Value": [ + { + "Key": "ts", + "Value": { + "T": 1672512884, + "I": 1 + } + }, + { + "Key": "t", + "Value": 6 + } + ] + }, + { + "Key": "majorityWriteDate", + "Value": "2022-12-31T20:54:44+02:00" + } + ] + }, + { + "Key": "replicationProgress", + "Value": [ + [ + { + "Key": "host", + "Value": "mongodb-primary:27017" + }, + { + "Key": "optime", + "Value": [ + { + "Key": "ts", + "Value": { + "T": 1672512884, + "I": 1 + } + }, + { + "Key": "t", + "Value": 6 + } + ] + }, + { + "Key": "lastAppliedOpTime", + "Value": [ + { + "Key": "ts", + "Value": { + "T": 1672512884, + "I": 1 + } + }, + { + "Key": "t", + "Value": 6 + } + ] + }, + { + "Key": "heartbeatAppliedOpTime", + "Value": [ + { + "Key": "ts", + "Value": { + "T": 0, + "I": 0 + } + }, + { + "Key": "t", + "Value": -1 + } + ] + }, + { + "Key": "heartbeatDurableOpTime", + "Value": [ + { + "Key": "ts", + "Value": { + "T": 0, + "I": 0 + } + }, + { + "Key": "t", + "Value": -1 + } + ] + }, + { + "Key": "memberId", + "Value": 0 + } + ], + [ + { + "Key": "host", + "Value": "mongodb-secondary:27017" + }, + { + "Key": "optime", + "Value": [ + { + "Key": "ts", + "Value": { + "T": 1672512884, + "I": 1 + } + }, + { + "Key": "t", + "Value": 6 + } + ] + }, + { + "Key": "lastAppliedOpTime", + "Value": [ + { + "Key": "ts", + "Value": { + "T": 1672512884, + "I": 1 + } + }, + { + "Key": "t", + "Value": 6 + } + ] + }, + { + "Key": "heartbeatAppliedOpTime", + "Value": [ + { + "Key": "ts", + "Value": { + "T": 1672512884, + "I": 1 + } + }, + { + "Key": "t", + "Value": 6 + } + ] + }, + { + "Key": "heartbeatDurableOpTime", + "Value": [ + { + "Key": "ts", + "Value": { + "T": 1672512884, + "I": 1 + } + }, + { + "Key": "t", + "Value": 6 + } + ] + }, + { + "Key": "memberId", + "Value": 1 + } + ] + ] + }, + { + "Key": "primaryOnlyServices", + "Value": [ + { + "Key": "ShardSplitDonorService", + "Value": [ + { + "Key": "state", + "Value": "running" + }, + { + "Key": "numInstances", + "Value": 0 + } + ] + }, + { + "Key": "TenantMigrationRecipientService", + "Value": [ + { + "Key": "state", + "Value": "running" + }, + { + "Key": "numInstances", + "Value": 0 + } + ] + }, + { + "Key": "TenantMigrationDonorService", + "Value": [ + { + "Key": "state", + "Value": "running" + }, + { + "Key": "numInstances", + "Value": 0 + } + ] + } + ] + }, + { + "Key": "rbid", + "Value": 2 + }, + { + "Key": "userWriteBlockMode", + "Value": 1 + } + ] +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/mongos-serverStatus.json b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/mongos-serverStatus.json new file mode 100644 index 000000000..ecf766715 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/mongos-serverStatus.json @@ -0,0 +1,129 @@ +{ + "Process": "mongos", + "OpCounters": { + "Insert": 0, + "Query": 10, + "Update": 0, + "Delete": 0, + "GetMore": 0, + "Command": 227283 + }, + "OpLatencies": null, + "Connections": { + "Current": 18, + "Available": 838842, + "TotalCreated": 89, + "Active": 5, + "Threaded": 18, + "ExhaustIsMaster": 0, + "ExhaustHello": 3, + "AwaitingTopologyChanges": 4 + }, + "Network": { + "BytesIn": 57943348, + "BytesOut": 247343709, + "NumRequests": 227310, + "NumSlowDNSOperations": 0, + "NumSlowSSLOperations": 0 + }, + "Memory": { + "Resident": 81, + "Virtual": 2476 + }, + "Metrics": { + "Cursor": {}, + "Document": { + "Deleted": 0, + "Inserted": 0, + "Returned": 0, + "Updated": 0 + }, + "QueryExecutor": { + "Scanned": 0, + "ScannedObjects": 0 + } + }, + "ExtraInfo": { + "PageFaults": 526 + }, + "Asserts": { + "Regular": 0, + "Warning": 0, + "Msg": 0, + "User": 352, + "Tripwire": 0, + "Rollovers": 0 + }, + "Transactions": { + "CurrentActive": 0, + "CurrentInactive": 0, + "CurrentOpen": 0, + "CurrentPrepared": null, + "TotalAborted": 0, + "TotalCommitted": 0, + "TotalStarted": 0, + "TotalPrepared": null, + "CommitTypes": { + "NoShards": { + "initiated": 0, + "successful": 0, + "successfulDurationMicros": 0 + }, + "SingleShard": { + "initiated": 0, + "successful": 0, + "successfulDurationMicros": 0 + }, + "SingleWriteShard": { + "initiated": 0, + "successful": 0, + "successfulDurationMicros": 0 + }, + "ReadOnly": { + "initiated": 0, + "successful": 0, + "successfulDurationMicros": 0 + }, + "TwoPhaseCommit": { + "initiated": 0, + "successful": 0, + "successfulDurationMicros": 0 + }, + "RecoverWithToken": { + "initiated": 0, + "successful": 0, + "successfulDurationMicros": 0 + } + } + }, + "GlobalLock": null, + "Tcmalloc": { + "Generic": { + "CurrentAllocatedBytes": 13519784, + "HeapSize": 24576000 + }, + "Tcmalloc": { + "PageheapFreeBytes": 5697536, + "PageheapUnmappedBytes": 57344, + "MaxTotalThreadCacheBytes": 1042284544, + "CurrentTotalThreadCacheBytes": 1638104, + "TotalFreeBytes": 5301336, + "CentralCacheFreeBytes": 736960, + "TransferCacheFreeBytes": 2926272, + "ThreadCacheFreeBytes": 1638104, + "AggressiveMemoryDecommit": 0, + "PageheapCommittedBytes": 24518656, + "PageheapScavengeBytes": 0, + "PageheapCommitCount": 480, + "PageheapTotalCommitBytes": 84799488, + "PageheapDecommitCount": 127, + "PageheapTotalDecommitBytes": 60280832, + "PageheapReserveCount": 15, + "PageheapTotalReserveBytes": 24576000, + "SpinlockTotalDelayNs": 96785212 + } + }, + "Locks": null, + "WiredTiger": null, + "Repl": null +} diff --git a/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/replSetGetStatus.json b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/replSetGetStatus.json new file mode 100644 index 000000000..c97a77f31 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/mongodb/testdata/v6.0.3/replSetGetStatus.json @@ -0,0 +1,27 @@ +{ + "Date": "2022-12-30T22:19:29.572Z", + "Members": [ + { + "Name": "mongodb-primary:27017", + "Self": true, + "State": 1, + "Health": 1, + "OptimeDate": "2022-12-30T22:19:25Z", + "LastHeartbeat": null, + "LastHeartbeatRecv": null, + "PingMs": null, + "Uptime": 192588 + }, + { + "Name": "mongodb-secondary:27017", + "Self": null, + "State": 2, + "Health": 1, + "OptimeDate": "2022-12-30T22:19:25Z", + "LastHeartbeat": "2022-12-30T22:19:28.214Z", + "LastHeartbeatRecv": "2022-12-30T22:19:28.213Z", + "PingMs": 0, + "Uptime": 192370 + } + ] +} |