sensu-plugins-mongodb-boutetnico 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ shell_script_path = File.join(__dir__, File.basename($PROGRAM_NAME, '.rb') + '.py')
4
+
5
+ exec shell_script_path, *ARGV
@@ -0,0 +1,269 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # metrics-mongodb-replication.rb
4
+ #
5
+ # DESCRIPTION:
6
+ #
7
+ # OUTPUT:
8
+ # metric data
9
+ #
10
+ # PLATFORMS:
11
+ # Linux
12
+ #
13
+ # DEPENDENCIES:
14
+ # gem: sensu-plugin
15
+ # gem: mongo
16
+ # gem: bson
17
+ # gem: bson_ext
18
+ #
19
+ # USAGE:
20
+ # #YELLOW
21
+ #
22
+ # NOTES::
23
+ # Basics from github.com/sensu-plugins/sensu-plugins-mongodb/bin/metrics-mongodb
24
+ #
25
+ # Replication lag is calculated by obtaining the last optime from primary and
26
+ # secondary members. The last optime of the secondary is subtracted from the
27
+ # last optime of the primary to produce the difference in seconds, minutes and hours
28
+ #
29
+ # LICENSE:
30
+ # Copyright 2016 Rycroft Solutions
31
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
32
+ # for details.
33
+ #
34
+
35
+ require 'sensu-plugin/metric/cli'
36
+ require 'mongo'
37
+ require 'date'
38
+ include Mongo
39
+
40
+ #
41
+ # Mongodb
42
+ #
43
+
44
+ class MongoDB < Sensu::Plugin::Metric::CLI::Graphite
45
+ option :host,
46
+ description: 'MongoDB host',
47
+ long: '--host HOST',
48
+ default: 'localhost'
49
+
50
+ option :port,
51
+ description: 'MongoDB port',
52
+ long: '--port PORT',
53
+ default: 27_017
54
+
55
+ option :user,
56
+ description: 'MongoDB user',
57
+ long: '--user USER',
58
+ default: nil
59
+
60
+ option :password,
61
+ description: 'MongoDB password',
62
+ long: '--password PASSWORD',
63
+ default: nil
64
+
65
+ option :ssl,
66
+ description: 'Connect using SSL',
67
+ long: '--ssl',
68
+ default: false
69
+
70
+ option :ssl_cert,
71
+ description: 'The certificate file used to identify the local connection against mongod',
72
+ long: '--ssl-cert SSL_CERT',
73
+ default: ''
74
+
75
+ option :ssl_key,
76
+ description: 'The private key used to identify the local connection against mongod',
77
+ long: '--ssl-key SSL_KEY',
78
+ default: ''
79
+
80
+ option :ssl_ca_cert,
81
+ description: 'The set of concatenated CA certificates, which are used to validate certificates passed from the other end of the connection',
82
+ long: '--ssl-ca-cert SSL_CA_CERT',
83
+ default: ''
84
+
85
+ option :ssl_verify,
86
+ description: 'Whether or not to do peer certification validation',
87
+ long: '--ssl-verify',
88
+ default: false
89
+
90
+ option :scheme,
91
+ description: 'Metric naming scheme',
92
+ long: '--scheme SCHEME',
93
+ short: '-s SCHEME',
94
+ default: "#{Socket.gethostname}.mongodb"
95
+
96
+ option :password,
97
+ description: 'MongoDB password',
98
+ long: '--password PASSWORD',
99
+ default: nil
100
+
101
+ option :debug,
102
+ description: 'Enable debug',
103
+ long: '--debug',
104
+ default: false
105
+
106
+ def get_mongo_doc(command)
107
+ rs = @db.command(command)
108
+ unless rs.successful?
109
+ return nil
110
+ end
111
+
112
+ rs.documents[0]
113
+ end
114
+
115
+ # connects to mongo and sets @db, works with MongoClient < 2.0.0
116
+ def connect_mongo_db
117
+ if Gem.loaded_specs['mongo'].version < Gem::Version.new('2.0.0')
118
+ mongo_client = MongoClient.new(host, port)
119
+ @db = mongo_client.db(db_name)
120
+ @db.authenticate(db_user, db_password) unless db_user.nil?
121
+ else
122
+ address_str = "#{config[:host]}:#{config[:port]}"
123
+ client_opts = {}
124
+ client_opts[:database] = 'admin'
125
+ unless config[:user].nil?
126
+ client_opts[:user] = config[:user]
127
+ client_opts[:password] = config[:password]
128
+ end
129
+ if config[:ssl]
130
+ client_opts[:ssl] = true
131
+ client_opts[:ssl_cert] = config[:ssl_cert]
132
+ client_opts[:ssl_key] = config[:ssl_key]
133
+ client_opts[:ssl_ca_cert] = config[:ssl_ca_cert]
134
+ client_opts[:ssl_verify] = config[:ssl_verify]
135
+ end
136
+ mongo_client = Mongo::Client.new([address_str], client_opts)
137
+ @db = mongo_client.database
138
+ end
139
+ end
140
+
141
+ def run
142
+ Mongo::Logger.logger.level = Logger::FATAL
143
+ @debug = config[:debug]
144
+ if @debug
145
+ Mongo::Logger.logger.level = Logger::DEBUG
146
+ config_debug = config.clone
147
+ config_debug[:password] = '***'
148
+ puts 'arguments:' + config_debug.inspect
149
+ end
150
+
151
+ connect_mongo_db
152
+
153
+ _result = false
154
+ # check if master
155
+ begin
156
+ @is_master = get_mongo_doc('isMaster' => 1)
157
+ unless @is_master.nil?
158
+ _result = @is_master['ok'] == 1
159
+ end
160
+ rescue StandardError => e
161
+ if @debug
162
+ puts 'Error checking isMaster:' + e.message
163
+ puts e.backtrace.inspect
164
+ end
165
+ exit(1)
166
+ end
167
+
168
+ replication_status = get_mongo_doc('replSetGetStatus' => 1)
169
+
170
+ # get the replication metrics
171
+ begin
172
+ metrics = {}
173
+ if !replication_status.nil? && replication_status['ok'] == 1
174
+ metrics.update(gather_replication_metrics(replication_status))
175
+ timestamp = Time.now.to_i
176
+ metrics.each do |k, v|
177
+ unless v.nil?
178
+ output [config[:scheme], 'replication', k].join('.'), v, timestamp
179
+ end
180
+ end
181
+ end
182
+ rescue StandardError => e
183
+ if @debug
184
+ puts 'Error checking replicationStatus:' + e.message
185
+ puts e.backtrace.inspect
186
+ end
187
+ exit(2)
188
+ end
189
+
190
+ # Get the repllication member metrics
191
+ begin
192
+ metrics = {}
193
+ replication_members = replication_status['members']
194
+ unless replication_members.nil?
195
+ state_map = {
196
+ 'PRIMARY' => 1,
197
+ 'SECONDARY' => 2
198
+ }
199
+ state_map.default = 3
200
+ replication_members.sort! { |x, y| state_map[x['stateStr']] <=> state_map[y['stateStr']] }
201
+
202
+ replication_members.each do |replication_member_details|
203
+ metrics.update(gather_replication_member_metrics(replication_member_details))
204
+ member_id = replication_member_details['_id']
205
+ timestamp = Time.now.to_i
206
+ metrics.each do |k, v|
207
+ unless v.nil?
208
+ output [config[:scheme], "member_#{member_id}", k].join('.'), v, timestamp
209
+ end
210
+ end
211
+ end
212
+ end
213
+ rescue StandardError => e
214
+ if @debug
215
+ puts 'Error checking replicationMemberStatus:' + e.message
216
+ puts e.backtrace.inspect
217
+ end
218
+ exit(2)
219
+ end
220
+
221
+ # done!
222
+ ok
223
+ end
224
+
225
+ def gather_replication_metrics(replication_status)
226
+ replication_metrics = {}
227
+
228
+ replication_metrics['replica_set'] = replication_status['set']
229
+ replication_metrics['date'] = replication_status['date']
230
+ replication_metrics['myState'] = replication_status['myState']
231
+ replication_metrics['term'] = replication_status['term']
232
+ replication_metrics['heartbeatIntervalMillis'] = replication_status['heartbeatIntervalMillis']
233
+
234
+ replication_metrics
235
+ end
236
+
237
+ def gather_replication_member_metrics(replication_member_details)
238
+ replication_member_metrics = {}
239
+
240
+ replication_member_metrics['id'] = replication_member_details['_id']
241
+ replication_member_metrics['name'] = replication_member_details['name']
242
+ replication_member_metrics['health'] = replication_member_details['health']
243
+ replication_member_metrics['state'] = replication_member_details['state']
244
+ replication_member_metrics['stateStr'] = replication_member_details['stateStr']
245
+ member_hierarchy = replication_member_details['stateStr']
246
+ if member_hierarchy == 'PRIMARY'
247
+ @primary_optime_date = replication_member_details['optimeDate']
248
+ replication_member_metrics['primary.startOptimeDate'] = @primary_optime_date
249
+ end
250
+ if member_hierarchy == 'SECONDARY'
251
+ @secondary_optime_date = replication_member_details['optimeDate']
252
+ difference_in_seconds = (@primary_optime_date - @secondary_optime_date).to_i
253
+ difference_in_minutes = ((@primary_optime_date - @secondary_optime_date) / 60).to_i
254
+ difference_in_hours = ((@primary_optime_date - @secondary_optime_date) / 3600).to_i
255
+ replication_member_metrics['secondsBehindPrimary'] = difference_in_seconds
256
+ replication_member_metrics['minutesBehindPrimary'] = difference_in_minutes
257
+ replication_member_metrics['hoursBehindPrimary'] = difference_in_hours
258
+ end
259
+ replication_member_metrics['optimeDate'] = replication_member_details['optimeDate']
260
+ replication_member_metrics['uptime'] = replication_member_details['uptime']
261
+ replication_member_metrics['lastHeartbeat'] = replication_member_details['lastHeartbeat']
262
+ replication_member_metrics['lastHeartbeatRecv'] = replication_member_details['lastHeartbeatiRecv']
263
+ replication_member_metrics['pingMs'] = replication_member_details['pingMs']
264
+ replication_member_metrics['syncingTo'] = replication_member_details['syncingTo']
265
+ replication_member_metrics['configVersion'] = replication_member_details['configVersion']
266
+
267
+ replication_member_metrics
268
+ end
269
+ end
@@ -0,0 +1,133 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # metrics-mongodb.rb
4
+ #
5
+ # DESCRIPTION:
6
+ #
7
+ # OUTPUT:
8
+ # metric data
9
+ #
10
+ # PLATFORMS:
11
+ # Linux
12
+ #
13
+ # DEPENDENCIES:
14
+ # gem: sensu-plugin
15
+ # gem: mongo
16
+ # gem: bson
17
+ # gem: bson_ext
18
+ #
19
+ # USAGE:
20
+ # #YELLOW
21
+ #
22
+ # NOTES:
23
+ # Basics from github.com/mantree/mongodb-graphite-metrics
24
+ #
25
+ # LICENSE:
26
+ # Copyright 2013 github.com/foomatty
27
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
28
+ # for details.
29
+ #
30
+
31
+ require 'sensu-plugin/metric/cli'
32
+ require 'sensu-plugins-mongodb/metrics'
33
+ require 'mongo'
34
+ include Mongo
35
+
36
+ #
37
+ # Mongodb
38
+ #
39
+
40
+ class MongoDB < Sensu::Plugin::Metric::CLI::Graphite
41
+ option :host,
42
+ description: 'MongoDB host',
43
+ long: '--host HOST',
44
+ default: 'localhost'
45
+
46
+ option :port,
47
+ description: 'MongoDB port',
48
+ long: '--port PORT',
49
+ default: 27_017
50
+
51
+ option :user,
52
+ description: 'MongoDB user',
53
+ long: '--user USER',
54
+ default: nil
55
+
56
+ option :password,
57
+ description: 'MongoDB password',
58
+ long: '--password PASSWORD',
59
+ default: nil
60
+
61
+ option :ssl,
62
+ description: 'Connect using SSL',
63
+ long: '--ssl',
64
+ default: false
65
+
66
+ option :ssl_cert,
67
+ description: 'The certificate file used to identify the local connection against mongod',
68
+ long: '--ssl-cert SSL_CERT',
69
+ default: ''
70
+
71
+ option :ssl_key,
72
+ description: 'The private key used to identify the local connection against mongod',
73
+ long: '--ssl-key SSL_KEY',
74
+ default: ''
75
+
76
+ option :ssl_ca_cert,
77
+ description: 'The set of concatenated CA certificates, which are used to validate certificates passed from the other end of the connection',
78
+ long: '--ssl-ca-cert SSL_CA_CERT',
79
+ default: ''
80
+
81
+ option :ssl_verify,
82
+ description: 'Whether or not to do peer certification validation',
83
+ long: '--ssl-verify',
84
+ default: false
85
+
86
+ option :debug,
87
+ description: 'Enable debug',
88
+ long: '--debug',
89
+ default: false
90
+
91
+ option :scheme,
92
+ description: 'Metric naming scheme',
93
+ long: '--scheme SCHEME',
94
+ short: '-s SCHEME',
95
+ default: "#{Socket.gethostname}.mongodb"
96
+
97
+ option :require_master,
98
+ description: 'Require the node to be a master node',
99
+ long: '--require-master',
100
+ default: false
101
+
102
+ option :exclude_db_sizes,
103
+ description: 'Exclude database sizes',
104
+ long: '--exclude-db-sizes',
105
+ default: false
106
+
107
+ def run
108
+ Mongo::Logger.logger.level = Logger::FATAL
109
+ @debug = config[:debug]
110
+ if @debug
111
+ Mongo::Logger.logger.level = Logger::DEBUG
112
+ config_debug = config.clone
113
+ config_debug[:password] = '***'
114
+ puts 'Arguments: ' + config_debug.inspect
115
+ end
116
+
117
+ # Get the metrics.
118
+ collector = SensuPluginsMongoDB::Metrics.new(config)
119
+ collector.connect_mongo_db('admin')
120
+ exit(1) if config[:require_master] && !collector.master?
121
+ metrics = collector.server_metrics
122
+ metrics = metrics.reject { |k, _v| k[/databaseSizes/] } if config[:exclude_db_sizes]
123
+
124
+ # Print them in graphite format.
125
+ timestamp = Time.now.to_i
126
+ metrics.each do |k, v|
127
+ output [config[:scheme], k].join('.'), v, timestamp
128
+ end
129
+
130
+ # done!
131
+ ok
132
+ end
133
+ end
@@ -0,0 +1 @@
1
+ require 'sensu-plugins-mongodb/version'
@@ -0,0 +1,399 @@
1
+ require 'mongo'
2
+ include Mongo
3
+
4
+ module SensuPluginsMongoDB
5
+ class Metrics
6
+ # Initializes a Metrics collector.
7
+ #
8
+ # @param config [Mesh]
9
+ # the config object parsed from the command line.
10
+ # Must include: :host, :port, :user, :password, :debug
11
+ def initialize(config)
12
+ @config = config
13
+ @connected = false
14
+ @db = nil
15
+ @mongo_client = nil
16
+ end
17
+
18
+ # Connects to a mongo database.
19
+ #
20
+ # @param db_name [String] the name of the db to connect to.
21
+ def connect_mongo_db(db_name)
22
+ if @connected
23
+ raise 'Already connected to a database'
24
+ end
25
+
26
+ db_user = @config[:user]
27
+ db_password = @config[:password]
28
+ @mongo_client = get_mongo_client(db_name)
29
+
30
+ if Gem.loaded_specs['mongo'].version < Gem::Version.new('2.0.0')
31
+ @db = @mongo_client.db(db_name)
32
+ @db.authenticate(db_user, db_password) unless db_user.nil?
33
+ else
34
+ @db = @mongo_client.database
35
+ end
36
+ end
37
+
38
+ # Fetches a document from the mongo db.
39
+ #
40
+ # @param command [Mesh] the command to search documents with.
41
+ # @return [Mesh, nil] the first document or nil.
42
+ def get_mongo_doc(command)
43
+ unless @connected
44
+ raise 'Cannot fetch documents before connecting.'
45
+ end
46
+ unless @db
47
+ raise 'Cannot fetch documents without a db.'
48
+ end
49
+
50
+ rs = @db.command(command)
51
+ unless rs.successful?
52
+ return nil
53
+ end
54
+
55
+ rs.documents[0]
56
+ end
57
+
58
+ # Checks if the connected node is the master node.
59
+ #
60
+ # @return [true, false] true when the node is a master node.
61
+ def master?
62
+ result = false
63
+ begin
64
+ @is_master = get_mongo_doc('isMaster' => 1)
65
+ unless @is_master.nil?
66
+ result = @is_master['ok'] == 1 && @is_master['ismaster']
67
+ end
68
+ rescue StandardError => e
69
+ if @config[:debug]
70
+ puts 'Error checking isMaster: ' + e.message
71
+ puts e.backtrace.inspect
72
+ end
73
+ end
74
+ result
75
+ end
76
+
77
+ # Fetches the status of the server (which includes the metrics).
78
+ #
79
+ # @return [Mash, nil] the document showing the server status or nil.
80
+ def server_status
81
+ status = get_mongo_doc('serverStatus' => 1)
82
+ return nil if status.nil? || status['ok'] != 1
83
+
84
+ status
85
+ rescue StandardError => e
86
+ if @debug
87
+ puts 'Error checking serverStatus: ' + e.message
88
+ puts e.backtrace.inspect
89
+ end
90
+ end
91
+
92
+ # Fetches the replicaset status of the server (which includes the metrics).
93
+ #
94
+ # @return [Mash, nil] the document showing the replicaset status or nil.
95
+ def replicaset_status
96
+ status = get_mongo_doc('replSetGetStatus' => 1)
97
+ return nil if status.nil?
98
+
99
+ status
100
+ rescue StandardError => e
101
+ if @debug
102
+ puts 'Error checking replSetGetStatus: ' + e.message
103
+ puts e.backtrace.inspect
104
+ end
105
+ end
106
+
107
+ # Fetches metrics for the server we are connected to.
108
+ #
109
+ # @return [Mash] the metrics for the server.
110
+ # rubocop:disable Metrics/AbcSize
111
+ def server_metrics
112
+ server_status = self.server_status
113
+ replicaset_status = self.replicaset_status
114
+ server_metrics = {}
115
+ # Handle versions like "2.6.11-pre" etc
116
+ mongo_version = server_status['version'].gsub(/[^0-9\.]/i, '')
117
+
118
+ server_metrics['lock.ratio'] = sprintf('%.5f', server_status['globalLock']['ratio']).to_s unless server_status['globalLock']['ratio'].nil?
119
+
120
+ # Asserts
121
+ asserts = server_status['asserts']
122
+ server_metrics['asserts.warnings'] = asserts['warning']
123
+ server_metrics['asserts.errors'] = asserts['msg']
124
+ server_metrics['asserts.regular'] = asserts['regular']
125
+ server_metrics['asserts.user'] = asserts['user']
126
+ server_metrics['asserts.rollovers'] = asserts['rollovers']
127
+
128
+ # Background flushing
129
+ if server_status.key?('backgroundFlushing')
130
+ bg_flushing = server_status['backgroundFlushing']
131
+ server_metrics['backgroundFlushing.flushes'] = bg_flushing['flushes']
132
+ server_metrics['backgroundFlushing.total_ms'] = bg_flushing['total_ms']
133
+ server_metrics['backgroundFlushing.average_ms'] = bg_flushing['average_ms']
134
+ server_metrics['backgroundFlushing.last_ms'] = bg_flushing['last_ms']
135
+ end
136
+
137
+ # Connections
138
+ connections = server_status['connections']
139
+ server_metrics['connections.current'] = connections['current']
140
+ server_metrics['connections.available'] = connections['available']
141
+ server_metrics['connections.totalCreated'] = connections['totalCreated']
142
+
143
+ # Cursors (use new metrics.cursor from mongo 2.6+)
144
+ if Gem::Version.new(mongo_version) < Gem::Version.new('2.6.0')
145
+ cursors = server_status['cursors']
146
+ server_metrics['clientCursors.size'] = cursors['clientCursors_size']
147
+ server_metrics['cursors.timedOut'] = cursors['timedOut']
148
+
149
+ # Metric names match the version 2.6+ format for standardization!
150
+ server_metrics['cursors.open.NoTimeout'] = cursors['totalNoTimeout']
151
+ server_metrics['cursors.open.pinned'] = cursors['pinned']
152
+ server_metrics['cursors.open.total'] = cursors['totalOpen']
153
+ else
154
+ cursors = server_status['metrics']['cursor']
155
+ server_metrics['cursors.timedOut'] = cursors['timedOut']
156
+ # clientCursors.size has been replaced by cursors.open.total
157
+
158
+ open = cursors['open']
159
+ server_metrics['cursors.open.noTimeout'] = open['noTimeout']
160
+ server_metrics['cursors.open.pinned'] = open['pinned']
161
+ server_metrics['cursors.open.total'] = open['total']
162
+
163
+ unless Gem::Version.new(mongo_version) < Gem::Version.new('3.0.0')
164
+ server_metrics['cursors.open.multiTarget'] = open['multiTarget']
165
+ server_metrics['cursors.open.singleTarget'] = open['singleTarget']
166
+ end
167
+ end
168
+
169
+ # Database Sizes
170
+ @mongo_client.database_names.each do |name|
171
+ @mongo_client = @mongo_client.use(name)
172
+ db = @mongo_client.database
173
+ result = db.command(dbstats: 1).documents.first
174
+ server_metrics["databaseSizes.#{name}.collections"] = result['collections']
175
+ server_metrics["databaseSizes.#{name}.objects"] = result['objects']
176
+ server_metrics["databaseSizes.#{name}.avgObjSize"] = result['avgObjSize']
177
+ server_metrics["databaseSizes.#{name}.dataSize"] = result['dataSize']
178
+ server_metrics["databaseSizes.#{name}.storageSize"] = result['storageSize']
179
+ server_metrics["databaseSizes.#{name}.numExtents"] = result['numExtents']
180
+ server_metrics["databaseSizes.#{name}.indexes"] = result['indexes']
181
+ server_metrics["databaseSizes.#{name}.indexSize"] = result['indexSize']
182
+ server_metrics["databaseSizes.#{name}.fileSize"] = result['fileSize']
183
+ server_metrics["databaseSizes.#{name}.nsSizeMB"] = result['nsSizeMB']
184
+ end
185
+ # Reset back to previous database
186
+ @mongo_client = @mongo_client.use(@db.name)
187
+
188
+ # Journaling (durability)
189
+ if server_status.key?('dur')
190
+ dur = server_status['dur']
191
+ server_metrics['journal.commits'] = dur['commits']
192
+ server_metrics['journaled_MB'] = dur['journaledMB']
193
+ server_metrics['journal.timeMs.writeToDataFiles'] = dur['timeMs']['writeToDataFiles']
194
+ server_metrics['journal.writeToDataFilesMB'] = dur['writeToDataFilesMB']
195
+ server_metrics['journal.compression'] = dur['compression']
196
+ server_metrics['journal.commitsInWriteLock'] = dur['commitsInWriteLock']
197
+ server_metrics['journal.timeMs.dt'] = dur['timeMs']['dt']
198
+ server_metrics['journal.timeMs.prepLogBuffer'] = dur['timeMs']['prepLogBuffer']
199
+ server_metrics['journal.timeMs.writeToJournal'] = dur['timeMs']['writeToJournal']
200
+ server_metrics['journal.timeMs.remapPrivateView'] = dur['timeMs']['remapPrivateView']
201
+ end
202
+
203
+ # Extra info
204
+ extra_info = server_status['extra_info']
205
+ server_metrics['mem.heap_usage_bytes'] = extra_info['heap_usage_bytes']
206
+ server_metrics['mem.pageFaults'] = extra_info['page_faults']
207
+
208
+ # Global Lock
209
+ global_lock = server_status['globalLock']
210
+ server_metrics['lock.totalTime'] = global_lock['totalTime']
211
+ server_metrics['lock.queue_total'] = global_lock['currentQueue']['total']
212
+ server_metrics['lock.queue_readers'] = global_lock['currentQueue']['readers']
213
+ server_metrics['lock.queue_writers'] = global_lock['currentQueue']['writers']
214
+ server_metrics['lock.clients_total'] = global_lock['activeClients']['total']
215
+ server_metrics['lock.clients_readers'] = global_lock['activeClients']['readers']
216
+ server_metrics['lock.clients_writers'] = global_lock['activeClients']['writers']
217
+
218
+ # Index counters
219
+ if Gem::Version.new(mongo_version) < Gem::Version.new('3.0.0')
220
+ index_counters = server_status['indexCounters']
221
+ index_counters = server_status['indexCounters']['btree'] unless server_status['indexCounters']['btree'].nil?
222
+
223
+ server_metrics['indexes.missRatio'] = sprintf('%.5f', index_counters['missRatio']).to_s
224
+ server_metrics['indexes.hits'] = index_counters['hits']
225
+ server_metrics['indexes.misses'] = index_counters['misses']
226
+ server_metrics['indexes.accesses'] = index_counters['accesses']
227
+ server_metrics['indexes.resets'] = index_counters['resets']
228
+ end
229
+
230
+ # Locks (from mongo 3.0+ only)
231
+ unless Gem::Version.new(mongo_version) < Gem::Version.new('3.0.0')
232
+ locks = server_status['locks']
233
+ lock_namespaces = %w[
234
+ Collection Global Database Metadata
235
+ MMAPV1Journal oplog
236
+ ]
237
+ lock_dimentions = %w[
238
+ acquireCount acquireWaitCount
239
+ timeAcquiringMicros deadlockCount
240
+ ]
241
+
242
+ lock_namespaces.each do |ns|
243
+ lock_dimentions.each do |dm|
244
+ next unless locks.key?(ns) && locks[ns].key?(dm)
245
+
246
+ lock = locks[ns][dm]
247
+ server_metrics["locks.#{ns}.#{dm}_r"] = lock['r'] if lock.key?('r')
248
+ server_metrics["locks.#{ns}.#{dm}_w"] = lock['r'] if lock.key?('w')
249
+ server_metrics["locks.#{ns}.#{dm}_R"] = lock['r'] if lock.key?('R')
250
+ server_metrics["locks.#{ns}.#{dm}_W"] = lock['r'] if lock.key?('W')
251
+ end
252
+ end
253
+ end
254
+
255
+ # Network
256
+ network = server_status['network']
257
+ server_metrics['network.bytesIn'] = network['bytesIn']
258
+ server_metrics['network.bytesOut'] = network['bytesOut']
259
+ server_metrics['network.numRequests'] = network['numRequests']
260
+
261
+ # Opcounters
262
+ opcounters = server_status['opcounters']
263
+ server_metrics['opcounters.insert'] = opcounters['insert']
264
+ server_metrics['opcounters.query'] = opcounters['query']
265
+ server_metrics['opcounters.update'] = opcounters['update']
266
+ server_metrics['opcounters.delete'] = opcounters['delete']
267
+ server_metrics['opcounters.getmore'] = opcounters['getmore']
268
+ server_metrics['opcounters.command'] = opcounters['command']
269
+
270
+ # Opcounters Replication
271
+ opcounters_repl = server_status['opcountersRepl']
272
+ server_metrics['opcountersRepl.insert'] = opcounters_repl['insert']
273
+ server_metrics['opcountersRepl.query'] = opcounters_repl['query']
274
+ server_metrics['opcountersRepl.update'] = opcounters_repl['update']
275
+ server_metrics['opcountersRepl.delete'] = opcounters_repl['delete']
276
+ server_metrics['opcountersRepl.getmore'] = opcounters_repl['getmore']
277
+ server_metrics['opcountersRepl.command'] = opcounters_repl['command']
278
+
279
+ # Memory
280
+ mem = server_status['mem']
281
+ server_metrics['mem.residentMb'] = mem['resident']
282
+ server_metrics['mem.virtualMb'] = mem['virtual']
283
+ server_metrics['mem.mapped'] = mem['mapped']
284
+ server_metrics['mem.mappedWithJournal'] = mem['mappedWithJournal']
285
+
286
+ # Metrics (documents)
287
+ document = server_status['metrics']['document']
288
+ server_metrics['metrics.document.deleted'] = document['deleted']
289
+ server_metrics['metrics.document.inserted'] = document['inserted']
290
+ server_metrics['metrics.document.returned'] = document['returned']
291
+ server_metrics['metrics.document.updated'] = document['updated']
292
+
293
+ # Metrics (getLastError)
294
+ get_last_error = server_status['metrics']['getLastError']
295
+ server_metrics['metrics.getLastError.wtime_num'] = get_last_error['wtime']['num']
296
+ server_metrics['metrics.getLastError.wtime_totalMillis'] = get_last_error['wtime']['totalMillis']
297
+ server_metrics['metrics.getLastError.wtimeouts'] = get_last_error['wtimeouts']
298
+
299
+ # Metrics (operation)
300
+ operation = server_status['metrics']['operation']
301
+ server_metrics['metrics.operation.fastmod'] = operation['fastmod']
302
+ server_metrics['metrics.operation.idhack'] = operation['idhack']
303
+ server_metrics['metrics.operation.scanAndOrder'] = operation['scanAndOrder']
304
+
305
+ # Metrics (operation)
306
+ query_executor = server_status['metrics']['queryExecutor']
307
+ server_metrics['metrics.queryExecutor.scanned'] = query_executor['scanned']
308
+ server_metrics['metrics.queryExecutor.scannedObjects'] = query_executor['scannedObjects']
309
+ server_metrics['metrics.record.moves'] = server_status['metrics']['record']['moves']
310
+
311
+ # Metrics (repl)
312
+ repl = server_status['metrics']['repl']
313
+ server_metrics['metrics.repl.apply.batches_num'] = repl['apply']['batches']['num']
314
+ server_metrics['metrics.repl.apply.batches_totalMillis'] = repl['apply']['batches']['totalMillis']
315
+ server_metrics['metrics.repl.apply.ops'] = repl['apply']['ops']
316
+ server_metrics['metrics.repl.buffer.count'] = repl['buffer']['count']
317
+ server_metrics['metrics.repl.buffer.maxSizeBytes'] = repl['buffer']['maxSizeBytes']
318
+ server_metrics['metrics.repl.buffer.sizeBytes'] = repl['buffer']['sizeBytes']
319
+ server_metrics['metrics.repl.network.bytes'] = repl['network']['bytes']
320
+ server_metrics['metrics.repl.network.getmores_num'] = repl['network']['getmores']['num']
321
+ server_metrics['metrics.repl.network.getmores_totalMillis'] = repl['network']['getmores']['totalMillis']
322
+ server_metrics['metrics.repl.network.ops'] = repl['network']['ops']
323
+ server_metrics['metrics.repl.network.readersCreated'] = repl['network']['readersCreated']
324
+ server_metrics['metrics.repl.preload.docs_num'] = repl['preload']['docs']['num']
325
+ server_metrics['metrics.repl.preload.docs_totalMillis'] = repl['preload']['docs']['totalMillis']
326
+ server_metrics['metrics.repl.preload.indexes_num'] = repl['preload']['indexes']['num']
327
+ server_metrics['metrics.repl.preload.indexes_totalMillis'] = repl['preload']['indexes']['totalMillis']
328
+
329
+ # Metrics (replicaset status)
330
+ # MongoDB will fail if not running with --replSet, hence the check for nil
331
+ unless replicaset_status.nil?
332
+ server_metrics['metrics.replicaset.state'] = replicaset_status['myState']
333
+ end
334
+
335
+ # Metrics (storage)
336
+ if Gem::Version.new(mongo_version) >= Gem::Version.new('2.6.0')
337
+ freelist = server_status['metrics']['storage']['freelist']
338
+ server_metrics['metrics.storage.freelist.search_bucketExhauseted'] = freelist['search']['bucketExhausted']
339
+ server_metrics['metrics.storage.freelist.search_requests'] = freelist['search']['requests']
340
+ server_metrics['metrics.storage.freelist.search_scanned'] = freelist['search']['scanned']
341
+ end
342
+
343
+ # Metrics (ttl)
344
+ ttl = server_status['metrics']['ttl']
345
+ server_metrics['metrics.ttl.deletedDocuments'] = ttl['deletedDocuments']
346
+ server_metrics['metrics.ttl.passes'] = ttl['passes']
347
+
348
+ # Return metrics map.
349
+ # MongoDB returns occasional nils and floats as {"floatApprox": x}.
350
+ # Clean up the results once here to avoid per-metric logic.
351
+ clean_metrics = {}
352
+ server_metrics.each do |k, v|
353
+ next if v.nil?
354
+
355
+ if v.is_a?(Hash) && v.key?('floatApprox')
356
+ v = v['floatApprox']
357
+ end
358
+ clean_metrics[k] = v
359
+ end
360
+ clean_metrics
361
+ end
362
+ # rubocop:enable Metrics/AbcSize
363
+
364
+ private
365
+
366
+ def get_mongo_client(db_name)
367
+ @connected = true
368
+ host = @config[:host]
369
+ port = @config[:port]
370
+ db_user = @config[:user]
371
+ db_password = @config[:password]
372
+ ssl = @config[:ssl]
373
+ ssl_cert = @config[:ssl_cert]
374
+ ssl_key = @config[:ssl_key]
375
+ ssl_ca_cert = @config[:ssl_ca_cert]
376
+ ssl_verify = @config[:ssl_verify]
377
+
378
+ if Gem.loaded_specs['mongo'].version < Gem::Version.new('2.0.0')
379
+ MongoClient.new(host, port)
380
+ else
381
+ address_str = "#{host}:#{port}"
382
+ client_opts = {}
383
+ client_opts[:database] = db_name
384
+ unless db_user.nil?
385
+ client_opts[:user] = db_user
386
+ client_opts[:password] = db_password
387
+ end
388
+ if ssl
389
+ client_opts[:ssl] = true
390
+ client_opts[:ssl_cert] = ssl_cert
391
+ client_opts[:ssl_key] = ssl_key
392
+ client_opts[:ssl_ca_cert] = ssl_ca_cert
393
+ client_opts[:ssl_verify] = ssl_verify
394
+ end
395
+ Mongo::Client.new([address_str], client_opts)
396
+ end
397
+ end
398
+ end
399
+ end