Skip to content

Commit 8297289

Browse files
committed
Reworked APM to allow custom operationId generator as well as timestamp generator
1 parent 6263225 commit 8297289

File tree

3 files changed

+186
-82
lines changed

3 files changed

+186
-82
lines changed

index.js

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -37,39 +37,10 @@ connect.Timestamp = core.BSON.Timestamp;
3737
// Add connect method
3838
connect.connect = connect;
3939

40-
// Instrumentation instance
41-
var instrumentation = null;
42-
4340
// Set up the instrumentation method
44-
connect.instrument = function(options) {
45-
if(!instrumentation) instrumentation = new Instrumentation(core, options)
46-
return instrumentation;
47-
}
48-
// Get prototype
49-
var AggregationCursor = require('./lib/aggregation_cursor'),
50-
CommandCursor = require('./lib/command_cursor'),
51-
OrderedBulkOperation = require('./lib/bulk/ordered').OrderedBulkOperation,
52-
UnorderedBulkOperation = require('./lib/bulk/unordered').UnorderedBulkOperation,
53-
Admin = require('./lib/admin');
54-
55-
// Instrument Hook
56-
connect.instrument = function(callback) {
57-
var instrumentations = []
58-
59-
// Classes to support
60-
var classes = [connect.GridStore, connect.Server, connect.ReplSet, connect.Mongos,
61-
OrderedBulkOperation, UnorderedBulkOperation, CommandCursor, AggregationCursor,
62-
connect.Cursor, connect.Collection, connect.Db];
63-
64-
// Add instrumentations to the available list
65-
for(var i = 0; i < classes.length; i++) {
66-
if(classes[i].define) {
67-
instrumentations.push(classes[i].define.generate());
68-
}
69-
}
70-
71-
// Return the list of instrumentation points
72-
callback(null, instrumentations);
41+
connect.instrument = function(options, callback) {
42+
if(typeof options == 'function') callback = options, options = {};
43+
return new Instrumentation(core, options, callback);
7344
}
7445

7546
// Set our exports to be the connect function

lib/apm.js

Lines changed: 89 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
var EventEmitter = require('events').EventEmitter,
22
inherits = require('util').inherits;
33

4+
// Get prototypes
5+
var AggregationCursor = require('./aggregation_cursor'),
6+
CommandCursor = require('./command_cursor'),
7+
OrderedBulkOperation = require('./bulk/ordered').OrderedBulkOperation,
8+
UnorderedBulkOperation = require('./bulk/unordered').UnorderedBulkOperation,
9+
GridStore = require('./gridfs/grid_store'),
10+
Server = require('./server'),
11+
ReplSet = require('./replset'),
12+
Mongos = require('./mongos'),
13+
Cursor = require('./cursor'),
14+
Collection = require('./collection'),
15+
Db = require('./db'),
16+
Admin = require('./admin');
17+
418
var basicOperationIdGenerator = {
519
operationId: 1,
620

@@ -9,12 +23,58 @@ var basicOperationIdGenerator = {
923
}
1024
}
1125

12-
var Instrumentation = function(core, options) {
26+
var basicTimestampGenerator = {
27+
current: function() {
28+
return new Date().getTime();
29+
},
30+
duration: function(start, end) {
31+
return end - start;
32+
}
33+
}
34+
35+
var Instrumentation = function(core, options, callback) {
1336
options = options || {};
37+
38+
// Optional id generators
1439
var operationIdGenerator = options.operationIdGenerator || basicOperationIdGenerator;
40+
// Optional timestamp generator
41+
var timestampGenerator = options.timestampGenerator || basicTimestampGenerator;
1542
// Extend with event emitter functionality
1643
EventEmitter.call(this);
1744

45+
// Contains all the instrumentation overloads
46+
this.overloads = [];
47+
48+
// ---------------------------------------------------------
49+
//
50+
// Instrument prototype
51+
//
52+
// ---------------------------------------------------------
53+
54+
var instrumentPrototype = function(callback) {
55+
var instrumentations = []
56+
57+
// Classes to support
58+
var classes = [GridStore, Server, ReplSet, Mongos,
59+
OrderedBulkOperation, UnorderedBulkOperation, CommandCursor, AggregationCursor,
60+
Cursor, Collection, Db];
61+
62+
// Add instrumentations to the available list
63+
for(var i = 0; i < classes.length; i++) {
64+
if(classes[i].define) {
65+
instrumentations.push(classes[i].define.generate());
66+
}
67+
}
68+
69+
// Return the list of instrumentation points
70+
callback(null, instrumentations);
71+
}
72+
73+
// Did the user want to instrument the prototype
74+
if(typeof callback == 'function') {
75+
instrumentPrototype(callback);
76+
}
77+
1878
// ---------------------------------------------------------
1979
//
2080
// Server
@@ -31,6 +91,9 @@ var Instrumentation = function(core, options) {
3191
methods.forEach(function(x) {
3292
var func = proto[x];
3393

94+
// Add to overloaded methods
95+
self.overloads.push({proto: proto, name:x, func:func});
96+
3497
// The actual prototype
3598
proto[x] = function() {
3699
var requestId = core.Query.nextRequestId();
@@ -50,6 +113,7 @@ var Instrumentation = function(core, options) {
50113

51114
// Get a connection reference for this server instance
52115
var connection = this.s.pool.get()
116+
53117
// Emit the start event for the command
54118
var command = {
55119
// Returns the command.
@@ -73,13 +137,13 @@ var Instrumentation = function(core, options) {
73137
self.emit('started', command)
74138

75139
// Start time
76-
var startTime = new Date().getTime();
140+
var startTime = timestampGenerator.current();
77141

78142
// Push our handler callback
79143
args.push(function(err, r) {
80-
var endTime = new Date().getTime();
144+
var endTime = timestampGenerator.current();
81145
var command = {
82-
duration: (endTime - startTime),
146+
duration: timestampGenerator.duration(startTime, endTime),
83147
commandName: commandName,
84148
requestId: requestId,
85149
operationId: ourOpId,
@@ -122,6 +186,9 @@ var Instrumentation = function(core, options) {
122186
methods.forEach(function(x) {
123187
var func = proto[x];
124188

189+
// Add to overloaded methods
190+
self.overloads.push({proto: proto, name:x, func:func});
191+
125192
// The actual prototype
126193
proto[x] = function() {
127194
var bulk = this;
@@ -168,10 +235,14 @@ var Instrumentation = function(core, options) {
168235
}
169236

170237
prototypes.forEach(function(proto) {
238+
171239
// Core server method we are going to wrap
172240
methods.forEach(function(x) {
173241
var func = proto[x];
174242

243+
// Add to overloaded methods
244+
self.overloads.push({proto: proto, name:x, func:func});
245+
175246
// The actual prototype
176247
proto[x] = function() {
177248
var cursor = this;
@@ -247,18 +318,12 @@ var Instrumentation = function(core, options) {
247318
}
248319
}
249320

250-
// console.log("#############################################################")
251-
// console.dir(this)
252-
253321
// Set up the connection
254322
var connectionId = null;
255323
// Set local connection
256324
if(this.connection) connectionId = this.connection;
257325
if(!connectionId && this.server && this.server.getConnection) connectionId = this.server.getConnection();
258326

259-
// var connections = this.connections();
260-
// var connectionId = connections.length > 0 ? connections[0] : null;
261-
262327
// Emit the start event for the command
263328
var command = {
264329
// Returns the command.
@@ -286,15 +351,15 @@ var Instrumentation = function(core, options) {
286351

287352
// We do not have a callback but a Promise
288353
if(typeof callback == 'function') {
289-
var startTime = new Date();
354+
var startTime = timestampGenerator.current();
290355
// Emit the started event
291356
self.emit('started', command)
292357
// Add our callback handler
293358
args.push(function(err, r) {
294359
if(err) {
295360
// Command
296361
var command = {
297-
duration: (new Date().getTime() - startTime.getTime()),
362+
duration: timestampGenerator.duration(startTime, timestampGenerator.current()),
298363
commandName: commandTranslation[x],
299364
requestId: requestId,
300365
operationId: ourOpId,
@@ -306,7 +371,7 @@ var Instrumentation = function(core, options) {
306371
} else {
307372
// cursor id is zero, we can issue success command
308373
var command = {
309-
duration: (new Date().getTime() - startTime.getTime()),
374+
duration: timestampGenerator.duration(startTime, timestampGenerator.current()),
310375
commandName: commandTranslation[x],
311376
requestId: requestId,
312377
operationId: cursor.operationId,
@@ -331,14 +396,14 @@ var Instrumentation = function(core, options) {
331396
var promise = func.apply(this, args);
332397
// Return a new promise
333398
return new cursor.s.promiseLibrary(function(resolve, reject) {
334-
var startTime = new Date();
399+
var startTime = timestampGenerator.current();
335400
// Emit the started event
336401
self.emit('started', command)
337402
// Execute the function
338403
promise.then(function(r) {
339404
// cursor id is zero, we can issue success command
340405
var command = {
341-
duration: (new Date().getTime() - startTime.getTime()),
406+
duration: timestampGenerator.duration(startTime, timestampGenerator.current()),
342407
commandName: commandTranslation[x],
343408
requestId: requestId,
344409
operationId: cursor.operationId,
@@ -351,7 +416,7 @@ var Instrumentation = function(core, options) {
351416
}).catch(function(err) {
352417
// Command
353418
var command = {
354-
duration: (new Date().getTime() - startTime.getTime()),
419+
duration: timestampGenerator.duration(startTime, timestampGenerator.current()),
355420
commandName: commandTranslation[x],
356421
requestId: requestId,
357422
operationId: ourOpId,
@@ -372,4 +437,11 @@ var Instrumentation = function(core, options) {
372437

373438
inherits(Instrumentation, EventEmitter);
374439

375-
module.exports = Instrumentation;
440+
Instrumentation.prototype.uninstrument = function() {
441+
for(var i = 0; i < this.overloads.length; i++) {
442+
var obj = this.overloads[i];
443+
obj.proto[obj.name] = obj.func;
444+
}
445+
}
446+
447+
module.exports = Instrumentation;

0 commit comments

Comments
 (0)