0
# Custom Instrumentation
1
2
Register custom instrumentation for third-party modules and application-specific code patterns.
3
4
## Capabilities
5
6
### Basic Instrumentation
7
8
Register generic instrumentation for any module.
9
10
```javascript { .api }
11
/**
12
* Register generic instrumentation for a module
13
* @param {string|object} moduleName - Module name or instrumentation options
14
* @param {Function} onRequire - Function called when module is loaded
15
* @param {Function} [onError] - Error handler for instrumentation failures
16
*/
17
function instrument(moduleName, onRequire, onError);
18
```
19
20
### Specialized Instrumentation
21
22
```javascript { .api }
23
/**
24
* Register datastore-specific instrumentation
25
*/
26
function instrumentDatastore(moduleName, onRequire, onError);
27
28
/**
29
* Register web framework instrumentation
30
*/
31
function instrumentWebframework(moduleName, onRequire, onError);
32
33
/**
34
* Register message broker instrumentation
35
*/
36
function instrumentMessages(moduleName, onRequire, onError);
37
38
/**
39
* Register conglomerate (multi-module) instrumentation
40
*/
41
function instrumentConglomerate(moduleName, onRequire, onError);
42
43
/**
44
* Apply instrumentation to already loaded modules
45
* @param {string} moduleName - Module name
46
* @param {object} module - Already loaded module
47
* @returns {boolean} Success status
48
*/
49
function instrumentLoadedModule(moduleName, module);
50
```
51
52
## Instrumentation Specification Object
53
54
For advanced instrumentation scenarios, pass an object instead of a string:
55
56
```javascript { .api }
57
interface InstrumentationSpec {
58
/** Module name to instrument */
59
moduleName: string;
60
/** Absolute path for specific module location */
61
absolutePath?: string;
62
/** Function called when module loads */
63
onRequire: Function;
64
/** Error handler for instrumentation failures */
65
onError?: Function;
66
/** Mark as ESM module (for ES Modules) */
67
isEsm?: boolean;
68
}
69
```
70
71
## Shim Types and Methods
72
73
The shim object provides different recording methods based on the instrumentation type:
74
75
### Generic Shim Methods
76
77
```javascript { .api }
78
// Record a function call with timing
79
shim.record(target, methodName, recordSpec);
80
81
// Wrap a function with custom logic
82
shim.wrap(target, methodName, wrapperFunction);
83
84
// Segment timing without recording
85
shim.createSegment(name, recorder, parentSegment);
86
```
87
88
### Datastore Shim Methods
89
90
```javascript { .api }
91
// Record database operations
92
shim.recordOperation(target, methodNames, operationSpec);
93
94
// Record query operations with SQL
95
shim.recordQuery(target, methodNames, querySpec);
96
97
// Record batch operations
98
shim.recordBatchQuery(target, methodNames, batchSpec);
99
```
100
101
### Web Framework Shim Methods
102
103
```javascript { .api }
104
// Record middleware functions
105
shim.recordMiddleware(target, methodName, middlewareSpec);
106
107
// Record route handlers
108
shim.recordRender(target, methodName, renderSpec);
109
110
// Wrap request/response handling
111
shim.wrapMiddlewareMounter(target, methodName, spec);
112
```
113
114
### Message Shim Methods
115
116
```javascript { .api }
117
// Record message producer operations
118
shim.recordProduce(target, methodName, produceSpec);
119
120
// Record message consumer operations
121
shim.recordConsume(target, methodName, consumeSpec);
122
123
// Record subscription operations
124
shim.recordSubscribedConsume(target, methodName, subscribeSpec);
125
```
126
127
**Usage Examples:**
128
129
```javascript
130
const newrelic = require('newrelic');
131
132
// Basic instrumentation with error handling
133
newrelic.instrument('my-custom-db', function(shim, module, moduleName) {
134
// Record all query methods
135
shim.record(module.prototype, ['query', 'find', 'update'], function(shim, fn, name, args) {
136
return {
137
name: `Database/${moduleName}/${name}`,
138
callback: shim.LAST,
139
parameters: {
140
query: args[0]
141
}
142
};
143
});
144
145
// Wrap connection method for additional context
146
shim.wrap(module.prototype, 'connect', function(shim, original) {
147
return function wrappedConnect() {
148
const result = original.apply(this, arguments);
149
shim.logger.debug('Database connection established');
150
return result;
151
};
152
});
153
}, function onError(error) {
154
console.error('Failed to instrument my-custom-db:', error);
155
});
156
157
// Advanced datastore instrumentation
158
newrelic.instrumentDatastore('redis-custom', function(shim, redis, moduleName) {
159
// Record standard operations
160
shim.recordOperation(redis.RedisClient.prototype, ['get', 'set', 'del'], {
161
name: function(shim, fn, fnName, args) {
162
return fnName.toUpperCase(); // GET, SET, DEL
163
},
164
parameters: {
165
key: shim.FIRST,
166
database_name: function(shim, fn, fnName, args, client) {
167
return client.selectedDb || 0;
168
}
169
},
170
callback: function(shim, fn, fnName, args) {
171
// Find callback in arguments
172
for (let i = args.length - 1; i >= 0; i--) {
173
if (typeof args[i] === 'function') {
174
return i;
175
}
176
}
177
return null;
178
}
179
});
180
181
// Record complex query operations
182
shim.recordQuery(redis.RedisClient.prototype, ['eval', 'evalsha'], {
183
name: 'EVAL',
184
parameters: {
185
script: shim.FIRST,
186
key_count: function(shim, fn, fnName, args) {
187
return args[1] || 0;
188
}
189
},
190
callback: shim.LAST
191
});
192
});
193
194
// Web framework instrumentation
195
newrelic.instrumentWebframework('my-web-framework', function(shim, framework, moduleName) {
196
// Record middleware registration
197
shim.recordMiddleware(framework.prototype, 'use', {
198
name: function(shim, fn, fnName, args) {
199
if (typeof args[0] === 'string') {
200
return `Middleware/${args[0]}`;
201
}
202
return 'Middleware/anonymous';
203
},
204
type: shim.MIDDLEWARE,
205
route: function(shim, fn, fnName, args) {
206
return typeof args[0] === 'string' ? args[0] : null;
207
},
208
wrapper: function(shim, middleware, name) {
209
return shim.recordMiddleware(middleware, {
210
name: name,
211
type: shim.MIDDLEWARE
212
});
213
}
214
});
215
216
// Record route handlers
217
shim.recordRender(framework.prototype, ['get', 'post', 'put', 'delete'], {
218
name: function(shim, fn, fnName, args) {
219
const route = args[0] || '/';
220
return `Route/${fnName.toUpperCase()} ${route}`;
221
},
222
callback: function(shim, fn, fnName, args) {
223
// Last argument is typically the handler
224
return args.length - 1;
225
}
226
});
227
});
228
229
// Message queue instrumentation
230
newrelic.instrumentMessages('my-queue-lib', function(shim, queueLib, moduleName) {
231
// Record message publishing
232
shim.recordProduce(queueLib.prototype, 'publish', {
233
name: function(shim, fn, fnName, args) {
234
const queueName = args[0] || 'unknown';
235
return `MessageBroker/${moduleName}/Queue/Produce/Named/${queueName}`;
236
},
237
parameters: {
238
routing_key: shim.FIRST,
239
reply_to: function(shim, fn, fnName, args) {
240
return args[1] && args[1].replyTo;
241
}
242
},
243
callback: shim.LAST
244
});
245
246
// Record message consumption
247
shim.recordConsume(queueLib.prototype, 'subscribe', {
248
name: function(shim, fn, fnName, args) {
249
const queueName = args[0] || 'unknown';
250
return `MessageBroker/${moduleName}/Queue/Consume/Named/${queueName}`;
251
},
252
parameters: {
253
routing_key: shim.FIRST
254
},
255
messageHandler: function(shim, fn, fnName, args) {
256
// Find the message handler function
257
for (let i = 1; i < args.length; i++) {
258
if (typeof args[i] === 'function') {
259
return i;
260
}
261
}
262
return null;
263
}
264
});
265
});
266
267
// Already loaded module instrumentation
268
const customModule = require('my-module');
269
const success = newrelic.instrumentLoadedModule('my-module', customModule);
270
if (success) {
271
console.log('Successfully instrumented already-loaded module');
272
} else {
273
console.warn('Failed to instrument already-loaded module');
274
}
275
276
// ESM module instrumentation
277
newrelic.instrument({
278
moduleName: 'esm-module',
279
isEsm: true,
280
onRequire: function(shim, module, moduleName) {
281
shim.record(module.default, 'process', {
282
name: `Custom/${moduleName}/process`,
283
callback: shim.LAST
284
});
285
},
286
onError: function(error) {
287
console.error('ESM instrumentation failed:', error);
288
}
289
});
290
```
291
292
## Recording Specifications
293
294
### Basic Record Spec
295
296
```javascript { .api }
297
interface RecordSpec {
298
/** Segment/metric name (string or function) */
299
name: string | Function;
300
/** Callback argument index or function to find it */
301
callback?: number | Function;
302
/** Additional parameters to capture */
303
parameters?: object;
304
/** Whether to record as a metric */
305
record?: boolean;
306
/** Segment recorder function */
307
recorder?: Function;
308
}
309
```
310
311
### Datastore Operation Spec
312
313
```javascript { .api }
314
interface DatastoreOperationSpec {
315
/** Operation name */
316
name: string | Function;
317
/** Callback position */
318
callback?: number | Function;
319
/** Parameters to capture (database_name, collection_name, etc.) */
320
parameters?: object;
321
/** Promise handling */
322
promise?: boolean;
323
/** Query/command to capture */
324
query?: Function;
325
}
326
```
327
328
### Middleware Spec
329
330
```javascript { .api }
331
interface MiddlewareSpec {
332
/** Middleware name */
333
name: string | Function;
334
/** Middleware type (shim.MIDDLEWARE, shim.ROUTER, etc.) */
335
type: string;
336
/** Route pattern */
337
route?: string | Function;
338
/** Request object position */
339
req?: number | Function;
340
/** Response object position */
341
res?: number | Function;
342
/** Next function position */
343
next?: number | Function;
344
/** Wrapper function for the middleware */
345
wrapper?: Function;
346
}
347
```
348
349
## Common Patterns and Best Practices
350
351
### Error Handling
352
353
```javascript
354
newrelic.instrument('unreliable-module', function(shim, module) {
355
try {
356
shim.record(module, 'riskyMethod', {
357
name: 'RiskyOperation',
358
callback: shim.LAST
359
});
360
} catch (error) {
361
shim.logger.warn('Could not instrument riskyMethod:', error);
362
}
363
}, function onError(error) {
364
console.error('Module instrumentation failed completely:', error);
365
});
366
```
367
368
### Conditional Instrumentation
369
370
```javascript
371
newrelic.instrument('optional-module', function(shim, module) {
372
if (module.version && parseFloat(module.version) >= 2.0) {
373
// Use v2+ API
374
shim.record(module, 'newMethod', {
375
name: 'NewAPI/newMethod',
376
callback: shim.LAST
377
});
378
} else {
379
// Use legacy API
380
shim.record(module, 'oldMethod', {
381
name: 'LegacyAPI/oldMethod',
382
callback: shim.LAST
383
});
384
}
385
});
386
```
387
388
### Promise-Based Libraries
389
390
```javascript
391
newrelic.instrument('promise-based-lib', function(shim, lib) {
392
shim.record(lib.prototype, 'asyncOperation', {
393
name: 'AsyncOperation',
394
promise: true, // Handle promise-based methods
395
parameters: {
396
input: shim.FIRST
397
}
398
});
399
});
400
```
401
402
### Class Method Instrumentation
403
404
```javascript
405
newrelic.instrument('class-based-lib', function(shim, lib) {
406
// Instrument instance methods
407
shim.record(lib.MyClass.prototype, ['method1', 'method2'], {
408
name: function(shim, fn, fnName) {
409
return `MyClass/${fnName}`;
410
},
411
callback: shim.LAST
412
});
413
414
// Instrument static methods
415
shim.record(lib.MyClass, 'staticMethod', {
416
name: 'MyClass/staticMethod',
417
callback: shim.LAST
418
});
419
});
420
```
421
422
## Troubleshooting
423
424
### Common Issues
425
426
1. **Module not found**: Ensure the module name matches exactly (case-sensitive)
427
2. **Methods not instrumented**: Check if methods exist on the target object
428
3. **Callback not found**: Verify callback position or provide callback finder function
429
4. **ESM modules**: Use `isEsm: true` and ensure proper import handling
430
5. **Already loaded modules**: Use `instrumentLoadedModule` for modules loaded before instrumentation
431
432
### Debugging Instrumentation
433
434
```javascript
435
newrelic.instrument('debug-module', function(shim, module, moduleName) {
436
shim.logger.debug('Instrumenting module:', moduleName);
437
shim.logger.debug('Module methods:', Object.getOwnPropertyNames(module));
438
439
shim.record(module, 'targetMethod', {
440
name: 'DebugOperation',
441
callback: function(shim, fn, fnName, args) {
442
shim.logger.debug('Callback search in args:', args.map(arg => typeof arg));
443
return shim.LAST;
444
}
445
});
446
});
447
```