or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

aws-lambda.mdbrowser-monitoring.mdcustom-attributes.mdcustom-instrumentation.mddistributed-tracing.mderror-handling.mdindex.mdllm-monitoring.mdmetrics-events.mdsegments-timing.mdtransaction-management.mdurl-naming-rules.mdutilities.md

custom-instrumentation.mddocs/

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

```