mongodb 3.0.4 → 3.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/apm.js CHANGED
@@ -1,639 +1,31 @@
1
1
  'use strict';
2
+ const EventEmitter = require('events').EventEmitter;
2
3
 
3
- var EventEmitter = require('events').EventEmitter,
4
- inherits = require('util').inherits;
5
-
6
- // Get prototypes
7
- var AggregationCursor = require('./aggregation_cursor'),
8
- CommandCursor = require('./command_cursor'),
9
- OrderedBulkOperation = require('./bulk/ordered').OrderedBulkOperation,
10
- UnorderedBulkOperation = require('./bulk/unordered').UnorderedBulkOperation,
11
- GridStore = require('./gridfs/grid_store'),
12
- Cursor = require('./cursor'),
13
- Collection = require('./collection'),
14
- Db = require('./db');
15
-
16
- var basicOperationIdGenerator = {
17
- operationId: 1,
18
-
19
- next: function() {
20
- return this.operationId++;
21
- }
22
- };
23
-
24
- var basicTimestampGenerator = {
25
- current: function() {
26
- return new Date().getTime();
27
- },
28
-
29
- duration: function(start, end) {
30
- return end - start;
4
+ class Instrumentation extends EventEmitter {
5
+ constructor() {
6
+ super();
31
7
  }
32
- };
33
-
34
- var senstiveCommands = [
35
- 'authenticate',
36
- 'saslStart',
37
- 'saslContinue',
38
- 'getnonce',
39
- 'createUser',
40
- 'updateUser',
41
- 'copydbgetnonce',
42
- 'copydbsaslstart',
43
- 'copydb'
44
- ];
45
-
46
- var Instrumentation = function(core, options, callback) {
47
- options = options || {};
48
-
49
- // Optional id generators
50
- var operationIdGenerator = options.operationIdGenerator || basicOperationIdGenerator;
51
- // Optional timestamp generator
52
- var timestampGenerator = options.timestampGenerator || basicTimestampGenerator;
53
- // Extend with event emitter functionality
54
- EventEmitter.call(this);
55
-
56
- // Contains all the instrumentation overloads
57
- this.overloads = [];
58
-
59
- // ---------------------------------------------------------
60
- //
61
- // Instrument prototype
62
- //
63
- // ---------------------------------------------------------
64
-
65
- var instrumentPrototype = function(callback) {
66
- var instrumentations = [];
67
-
68
- // Classes to support
69
- var classes = [
70
- GridStore,
71
- OrderedBulkOperation,
72
- UnorderedBulkOperation,
73
- CommandCursor,
74
- AggregationCursor,
75
- Cursor,
76
- Collection,
77
- Db
78
- ];
79
-
80
- // Add instrumentations to the available list
81
- for (var i = 0; i < classes.length; i++) {
82
- if (classes[i].define) {
83
- instrumentations.push(classes[i].define.generate());
84
- }
85
- }
86
-
87
- // Return the list of instrumentation points
88
- callback(null, instrumentations);
89
- };
90
-
91
- // Did the user want to instrument the prototype
92
- if (typeof callback === 'function') {
93
- instrumentPrototype(callback);
94
- }
95
-
96
- // ---------------------------------------------------------
97
- //
98
- // Server
99
- //
100
- // ---------------------------------------------------------
101
-
102
- // Reference
103
- var self = this;
104
- // Names of methods we need to wrap
105
- var methods = ['command', 'insert', 'update', 'remove'];
106
- // Prototype
107
- var proto = core.Server.prototype;
108
- // Core server method we are going to wrap
109
- methods.forEach(function(x) {
110
- var func = proto[x];
111
-
112
- // Add to overloaded methods
113
- self.overloads.push({ proto: proto, name: x, func: func });
114
-
115
- // The actual prototype
116
- proto[x] = function() {
117
- var requestId = core.Query.nextRequestId();
118
- // Get the aruments
119
- var args = Array.prototype.slice.call(arguments, 0);
120
- var ns = args[0];
121
- var commandObj = args[1];
122
- var options = args[2] || {};
123
- var keys = Object.keys(commandObj);
124
- var commandName = keys[0];
125
- var db = ns.split('.')[0];
126
-
127
- // Get the collection
128
- var col = ns.split('.');
129
- col.shift();
130
- col = col.join('.');
131
-
132
- // Do we have a legacy insert/update/remove command
133
- if (x === 'insert') {
134
- //} && !this.lastIsMaster().maxWireVersion) {
135
- commandName = 'insert';
136
8
 
137
- // Re-write the command
138
- commandObj = {
139
- insert: col,
140
- documents: commandObj
141
- };
142
-
143
- if (options.writeConcern && Object.keys(options.writeConcern).length > 0) {
144
- commandObj.writeConcern = options.writeConcern;
145
- }
146
-
147
- commandObj.ordered = options.ordered !== undefined ? options.ordered : true;
148
- } else if (x === 'update') {
149
- // && !this.lastIsMaster().maxWireVersion) {
150
- commandName = 'update';
151
-
152
- // Re-write the command
153
- commandObj = {
154
- update: col,
155
- updates: commandObj
156
- };
157
-
158
- if (options.writeConcern && Object.keys(options.writeConcern).length > 0) {
159
- commandObj.writeConcern = options.writeConcern;
160
- }
161
-
162
- commandObj.ordered = options.ordered !== undefined ? options.ordered : true;
163
- } else if (x === 'remove') {
164
- //&& !this.lastIsMaster().maxWireVersion) {
165
- commandName = 'delete';
166
-
167
- // Re-write the command
168
- commandObj = {
169
- delete: col,
170
- deletes: commandObj
171
- };
172
-
173
- if (options.writeConcern && Object.keys(options.writeConcern).length > 0) {
174
- commandObj.writeConcern = options.writeConcern;
175
- }
176
-
177
- commandObj.ordered = options.ordered !== undefined ? options.ordered : true;
178
- }
179
-
180
- // Get the callback
181
- var callback = args.pop();
182
- // Set current callback operation id from the current context or create
183
- // a new one
184
- var ourOpId = callback.operationId || operationIdGenerator.next();
185
-
186
- // Get a connection reference for this server instance
187
- var connection = this.s.pool.get();
188
-
189
- // Emit the start event for the command
190
- var command = {
191
- // Returns the command.
192
- command: commandObj,
193
- // Returns the database name.
194
- databaseName: db,
195
- // Returns the command name.
196
- commandName: commandName,
197
- // Returns the driver generated request id.
198
- requestId: requestId,
199
- // Returns the driver generated operation id.
200
- // This is used to link events together such as bulk write operations. OPTIONAL.
201
- operationId: ourOpId,
202
- // Returns the connection id for the command. For languages that do not have this,
203
- // this MUST return the driver equivalent which MUST include the server address and port.
204
- // The name of this field is flexible to match the object that is returned from the driver.
205
- connectionId: connection
206
- };
207
-
208
- // Filter out any sensitive commands
209
- if (senstiveCommands.indexOf(commandName.toLowerCase()) !== -1) {
210
- command.commandObj = {};
211
- command.commandObj[commandName] = true;
212
- }
213
-
214
- // Emit the started event
215
- self.emit('started', command);
216
-
217
- // Start time
218
- var startTime = timestampGenerator.current();
219
-
220
- // Push our handler callback
221
- args.push(function(err, r) {
222
- var endTime = timestampGenerator.current();
223
- var command = {
224
- duration: timestampGenerator.duration(startTime, endTime),
225
- commandName: commandName,
226
- requestId: requestId,
227
- operationId: ourOpId,
228
- connectionId: connection
229
- };
230
-
231
- // If we have an error
232
- if (err || (r && r.result && r.result.ok === 0)) {
233
- command.failure = err || r.result.writeErrors || r.result;
234
-
235
- // Filter out any sensitive commands
236
- if (senstiveCommands.indexOf(commandName.toLowerCase()) !== -1) {
237
- command.failure = {};
238
- }
239
-
240
- self.emit('failed', command);
241
- } else if (commandObj && commandObj.writeConcern && commandObj.writeConcern.w === 0) {
242
- // If we have write concern 0
243
- command.reply = { ok: 1 };
244
- self.emit('succeeded', command);
245
- } else {
246
- command.reply = r && r.result ? r.result : r;
247
-
248
- // Filter out any sensitive commands
249
- if (senstiveCommands.indexOf(commandName.toLowerCase()) !== -1) {
250
- command.reply = {};
251
- }
252
-
253
- self.emit('succeeded', command);
254
- }
255
-
256
- // Return to caller
257
- callback(err, r);
258
- });
259
-
260
- // Apply the call
261
- func.apply(this, args);
9
+ instrument(MongoClient, callback) {
10
+ // store a reference to the original functions
11
+ this.$MongoClient = MongoClient;
12
+ const $prototypeConnect = this.$prototypeConnect = MongoClient.prototype.connect; // eslint-disable-line
13
+
14
+ const instrumentation = this;
15
+ MongoClient.prototype.connect = function(callback) {
16
+ this.s.options.monitorCommands = true;
17
+ this.on('commandStarted', event => instrumentation.emit('started', event));
18
+ this.on('commandSucceeded', event => instrumentation.emit('succeeded', event));
19
+ this.on('commandFailed', event => instrumentation.emit('failed', event));
20
+ return $prototypeConnect.call(this, callback);
262
21
  };
263
- });
264
-
265
- // ---------------------------------------------------------
266
- //
267
- // Bulk Operations
268
- //
269
- // ---------------------------------------------------------
270
22
 
271
- // Inject ourselves into the Bulk methods
272
- methods = ['execute'];
273
- var prototypes = [
274
- require('./bulk/ordered').Bulk.prototype,
275
- require('./bulk/unordered').Bulk.prototype
276
- ];
277
-
278
- prototypes.forEach(function(proto) {
279
- // Core server method we are going to wrap
280
- methods.forEach(function(x) {
281
- var func = proto[x];
282
-
283
- // Add to overloaded methods
284
- self.overloads.push({ proto: proto, name: x, func: func });
285
-
286
- // The actual prototype
287
- proto[x] = function() {
288
- // Get the aruments
289
- var args = Array.prototype.slice.call(arguments, 0);
290
- // Set an operation Id on the bulk object
291
- this.operationId = operationIdGenerator.next();
292
-
293
- // Get the callback
294
- var callback = args.pop();
295
- // If we have a callback use this
296
- if (typeof callback === 'function') {
297
- args.push(function(err, r) {
298
- // Return to caller
299
- callback(err, r);
300
- });
301
-
302
- // Apply the call
303
- func.apply(this, args);
304
- } else {
305
- return func.apply(this, args);
306
- }
307
- };
308
- });
309
- });
310
-
311
- // ---------------------------------------------------------
312
- //
313
- // Cursor
314
- //
315
- // ---------------------------------------------------------
316
-
317
- // Inject ourselves into the Cursor methods
318
- methods = ['_find', '_getmore', '_killcursor'];
319
- prototypes = [
320
- require('./cursor').prototype,
321
- require('./command_cursor').prototype,
322
- require('./aggregation_cursor').prototype
323
- ];
324
-
325
- // Command name translation
326
- var commandTranslation = {
327
- _find: 'find',
328
- _getmore: 'getMore',
329
- _killcursor: 'killCursors',
330
- _explain: 'explain'
331
- };
332
-
333
- prototypes.forEach(function(proto) {
334
- // Core server method we are going to wrap
335
- methods.forEach(function(x) {
336
- var func = proto[x];
337
-
338
- // Add to overloaded methods
339
- self.overloads.push({ proto: proto, name: x, func: func });
340
-
341
- // The actual prototype
342
- proto[x] = function() {
343
- var cursor = this;
344
- var requestId = core.Query.nextRequestId();
345
- var ourOpId = operationIdGenerator.next();
346
- var parts = this.ns.split('.');
347
- var db = parts[0];
348
-
349
- // Get the collection
350
- parts.shift();
351
- var collection = parts.join('.');
352
-
353
- // Set the command
354
- var command = this.query;
355
- var cmd = this.s.cmd;
356
-
357
- // If we have a find method, set the operationId on the cursor
358
- if (x === '_find') {
359
- cursor.operationId = ourOpId;
360
- }
361
-
362
- // Do we have a find command rewrite it
363
- if (x === '_getmore') {
364
- command = {
365
- getMore: this.cursorState.cursorId,
366
- collection: collection,
367
- batchSize: cmd.batchSize
368
- };
369
-
370
- if (cmd.maxTimeMS) command.maxTimeMS = cmd.maxTimeMS;
371
- } else if (x === '_killcursor') {
372
- command = {
373
- killCursors: collection,
374
- cursors: [this.cursorState.cursorId]
375
- };
376
- } else if (cmd.find) {
377
- command = {
378
- find: collection,
379
- filter: cmd.query
380
- };
381
-
382
- if (cmd.sort) command.sort = cmd.sort;
383
- if (cmd.fields) command.projection = cmd.fields;
384
- if (cmd.limit && cmd.limit < 0) {
385
- command.limit = Math.abs(cmd.limit);
386
- command.singleBatch = true;
387
- } else if (cmd.limit) {
388
- command.limit = Math.abs(cmd.limit);
389
- }
390
-
391
- // Options
392
- if (cmd.skip) command.skip = cmd.skip;
393
- if (cmd.hint) command.hint = cmd.hint;
394
- if (cmd.batchSize) command.batchSize = cmd.batchSize;
395
- if (typeof cmd.returnKey === 'boolean') command.returnKey = cmd.returnKey;
396
- if (cmd.comment) command.comment = cmd.comment;
397
- if (cmd.min) command.min = cmd.min;
398
- if (cmd.max) command.max = cmd.max;
399
- if (cmd.maxScan) command.maxScan = cmd.maxScan;
400
- if (cmd.maxTimeMS) command.maxTimeMS = cmd.maxTimeMS;
401
-
402
- // Flags
403
- if (typeof cmd.awaitData === 'boolean') command.awaitData = cmd.awaitData;
404
- if (typeof cmd.snapshot === 'boolean') command.snapshot = cmd.snapshot;
405
- if (typeof cmd.tailable === 'boolean') command.tailable = cmd.tailable;
406
- if (typeof cmd.oplogReplay === 'boolean') command.oplogReplay = cmd.oplogReplay;
407
- if (typeof cmd.noCursorTimeout === 'boolean')
408
- command.noCursorTimeout = cmd.noCursorTimeout;
409
- if (typeof cmd.partial === 'boolean') command.partial = cmd.partial;
410
- if (typeof cmd.showDiskLoc === 'boolean') command.showRecordId = cmd.showDiskLoc;
411
-
412
- // Read Concern
413
- if (cmd.readConcern) command.readConcern = cmd.readConcern;
414
-
415
- // Override method
416
- if (cmd.explain) command.explain = cmd.explain;
417
- if (cmd.exhaust) command.exhaust = cmd.exhaust;
418
-
419
- // If we have a explain flag
420
- if (cmd.explain) {
421
- // Create fake explain command
422
- command = {
423
- explain: command,
424
- verbosity: 'allPlansExecution'
425
- };
426
-
427
- // Set readConcern on the command if available
428
- if (cmd.readConcern) command.readConcern = cmd.readConcern;
429
-
430
- // Set up the _explain name for the command
431
- x = '_explain';
432
- }
433
- } else {
434
- command = cmd;
435
- }
436
-
437
- // Set up the connection
438
- var connectionId = null;
439
-
440
- // Set local connection
441
- if (this.connection) connectionId = this.connection;
442
- if (!connectionId && this.topology && this.topology.getConnection)
443
- connectionId = this.topology.getConnection();
444
-
445
- // Get the command Name
446
- var commandName = x === '_find' ? Object.keys(command)[0] : commandTranslation[x];
447
-
448
- // Emit the start event for the command
449
- command = {
450
- // Returns the command.
451
- command: command,
452
- // Returns the database name.
453
- databaseName: db,
454
- // Returns the command name.
455
- commandName: commandName,
456
- // Returns the driver generated request id.
457
- requestId: requestId,
458
- // Returns the driver generated operation id.
459
- // This is used to link events together such as bulk write operations. OPTIONAL.
460
- operationId: this.operationId,
461
- // Returns the connection id for the command. For languages that do not have this,
462
- // this MUST return the driver equivalent which MUST include the server address and port.
463
- // The name of this field is flexible to match the object that is returned from the driver.
464
- connectionId: connectionId
465
- };
466
-
467
- // Get the aruments
468
- var args = Array.prototype.slice.call(arguments, 0);
469
-
470
- // Get the callback
471
- var callback = args.pop();
472
-
473
- // We do not have a callback but a Promise
474
- if (typeof callback === 'function' || command.commandName === 'killCursors') {
475
- var startTime = timestampGenerator.current();
476
- // Emit the started event
477
- self.emit('started', command);
478
-
479
- // Emit succeeded event with killcursor if we have a legacy protocol
480
- if (
481
- command.commandName === 'killCursors' &&
482
- this.topology.lastIsMaster() &&
483
- this.topology.lastIsMaster().maxWireVersion < 4
484
- ) {
485
- // Emit the succeeded command
486
- command = {
487
- duration: timestampGenerator.duration(startTime, timestampGenerator.current()),
488
- commandName: commandName,
489
- requestId: requestId,
490
- operationId: cursor.operationId,
491
- connectionId: cursor.topology.getConnection(),
492
- reply: [{ ok: 1 }]
493
- };
494
-
495
- // Apply callback to the list of args
496
- args.push(callback);
497
- // Apply the call
498
- func.apply(this, args);
499
- // Emit the command
500
- return self.emit('succeeded', command);
501
- }
502
-
503
- // Add our callback handler
504
- args.push(function(err, r) {
505
- if (err) {
506
- // Command
507
- var command = {
508
- duration: timestampGenerator.duration(startTime, timestampGenerator.current()),
509
- commandName: commandName,
510
- requestId: requestId,
511
- operationId: ourOpId,
512
- connectionId: cursor.topology.getConnection(),
513
- failure: err
514
- };
515
-
516
- // Emit the command
517
- self.emit('failed', command);
518
- } else {
519
- if (r && r.documents) {
520
- r = r.documents[0];
521
- }
522
-
523
- if (commandName.toLowerCase() === 'getmore' && (r == null || r.cursor == null)) {
524
- r = {
525
- cursor: {
526
- id: cursor.cursorState.cursorId,
527
- ns: cursor.ns,
528
- nextBatch: cursor.cursorState.documents
529
- },
530
- ok: 1
531
- };
532
- } else if (
533
- (commandName.toLowerCase() === 'find' ||
534
- commandName.toLowerCase() === 'aggregate' ||
535
- commandName.toLowerCase() === 'listcollections') &&
536
- (r == null || r.cursor == null)
537
- ) {
538
- r = {
539
- cursor: {
540
- id: cursor.cursorState.cursorId,
541
- ns: cursor.ns,
542
- firstBatch: cursor.cursorState.documents
543
- },
544
- ok: 1
545
- };
546
- } else if (commandName.toLowerCase() === 'killcursors' && r == null) {
547
- r = {
548
- cursorsUnknown: [cursor.cursorState.lastCursorId],
549
- ok: 1
550
- };
551
- }
552
-
553
- // cursor id is zero, we can issue success command
554
- command = {
555
- duration: timestampGenerator.duration(startTime, timestampGenerator.current()),
556
- commandName: commandName,
557
- requestId: requestId,
558
- operationId: cursor.operationId,
559
- connectionId: cursor.topology.getConnection(),
560
- reply: r && r.result ? r.result : r
561
- };
562
-
563
- // Emit the command
564
- self.emit('succeeded', command);
565
- }
566
-
567
- // Return
568
- if (!callback) return;
569
-
570
- // Return to caller
571
- callback(err, r);
572
- });
573
-
574
- // Apply the call
575
- func.apply(this, args);
576
- } else {
577
- // Assume promise, push back the missing value
578
- args.push(callback);
579
- // Get the promise
580
- var promise = func.apply(this, args);
581
- // Return a new promise
582
- return new cursor.s.promiseLibrary(function(resolve, reject) {
583
- var startTime = timestampGenerator.current();
584
- // Emit the started event
585
- self.emit('started', command);
586
- // Execute the function
587
- promise
588
- .then(function() {
589
- // cursor id is zero, we can issue success command
590
- var command = {
591
- duration: timestampGenerator.duration(startTime, timestampGenerator.current()),
592
- commandName: commandName,
593
- requestId: requestId,
594
- operationId: cursor.operationId,
595
- connectionId: cursor.topology.getConnection(),
596
- reply: cursor.cursorState.documents
597
- };
598
-
599
- // Emit the command
600
- self.emit('succeeded', command);
601
- })
602
- .catch(function(err) {
603
- // Command
604
- var command = {
605
- duration: timestampGenerator.duration(startTime, timestampGenerator.current()),
606
- commandName: commandName,
607
- requestId: requestId,
608
- operationId: ourOpId,
609
- connectionId: cursor.topology.getConnection(),
610
- failure: err
611
- };
612
-
613
- // Emit the command
614
- self.emit('failed', command);
615
- // reject the promise
616
- reject(err);
617
- });
618
- });
619
- }
620
- };
621
- });
622
- });
623
- };
624
-
625
- inherits(Instrumentation, EventEmitter);
626
-
627
- Instrumentation.prototype.uninstrument = function() {
628
- for (var i = 0; i < this.overloads.length; i++) {
629
- var obj = this.overloads[i];
630
- obj.proto[obj.name] = obj.func;
23
+ if (typeof callback === 'function') callback(null, this);
631
24
  }
632
25
 
633
- // Remove all listeners
634
- this.removeAllListeners('started');
635
- this.removeAllListeners('succeeded');
636
- this.removeAllListeners('failed');
637
- };
26
+ uninstrument() {
27
+ this.$MongoClient.prototype.connect = this.$prototypeConnect;
28
+ }
29
+ }
638
30
 
639
31
  module.exports = Instrumentation;
@@ -15,28 +15,6 @@ var INSERT = 1;
15
15
  var UPDATE = 2;
16
16
  var REMOVE = 3;
17
17
 
18
- // Get write concern
19
- var writeConcern = function(target, col, options) {
20
- var writeConcern = {};
21
-
22
- // Collection level write concern
23
- if (col.writeConcern && col.writeConcern.w != null) writeConcern.w = col.writeConcern.w;
24
- if (col.writeConcern && col.writeConcern.j != null) writeConcern.j = col.writeConcern.j;
25
- if (col.writeConcern && col.writeConcern.fsync != null)
26
- writeConcern.fsync = col.writeConcern.fsync;
27
- if (col.writeConcern && col.writeConcern.wtimeout != null)
28
- writeConcern.wtimeout = col.writeConcern.wtimeout;
29
-
30
- // Options level write concern
31
- if (options && options.w != null) writeConcern.w = options.w;
32
- if (options && options.wtimeout != null) writeConcern.wtimeout = options.wtimeout;
33
- if (options && options.j != null) writeConcern.j = options.j;
34
- if (options && options.fsync != null) writeConcern.fsync = options.fsync;
35
-
36
- // Return write concern
37
- return writeConcern;
38
- };
39
-
40
18
  /**
41
19
  * Helper function to define properties
42
20
  * @ignore
@@ -455,7 +433,6 @@ exports.Batch = Batch;
455
433
  exports.LegacyOp = LegacyOp;
456
434
  exports.mergeBatchResults = mergeBatchResults;
457
435
  exports.cloneOptions = cloneOptions;
458
- exports.writeConcern = writeConcern;
459
436
  exports.INVALID_BSON_ERROR = INVALID_BSON_ERROR;
460
437
  exports.WRITE_CONCERN_ERROR = WRITE_CONCERN_ERROR;
461
438
  exports.MULTIPLE_ERROR = MULTIPLE_ERROR;