Skip to content

Commit

Permalink
chore: Added instrumentation tracking to support issue 2033
Browse files Browse the repository at this point in the history
  • Loading branch information
jsumners-nr committed Mar 19, 2024
1 parent f4586d7 commit c7e3602
Show file tree
Hide file tree
Showing 5 changed files with 606 additions and 0 deletions.
186 changes: 186 additions & 0 deletions lib/instrumentation-descriptor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

const IdGen = require('./util/idgen')
const idGen = new IdGen()

/**
* @typedef {function} InstrumentationOnRequire
* @param {Shim} shim The shim instance to use for the instrumentation.
* @param {object} resolvedNodule The module being instrumented as returned by
* Node's `require` function.
* @param {string} moduleName The simple name of the module, i.e. the value
* passed to the `require` function.
* @throws {Error|object}
*/

/**
* @typedef {function} InstrumentationOnError
* @param {Error|object} error The error thrown by `onRequire` when there was
* an issue registering the instrumentation.
*/

/* eslint-disable jsdoc/require-property-description */
/**
* @typedef {object} InstrumentationDescriptorParams
* @property {string} absolutePath
* @property {string} module
* @property {string} moduleName
* @property {InstrumentationOnError} onError
* @property {InstrumentationOnRequire} onRequire
* @property {string} resolvedName
* @property {string} type
*/

/**
* Describes the configuration for an instrumentation. An instrumentation
* is what `newrelic` uses to wrap Node.js modules. In particular, a description
* details the name of the module, the path on disk to the module, and the
* hooks (`onRequire` and `onError`) to apply to the module.
*/
class InstrumentationDescriptor {
/**
* Utility/generic module.
* @type {string}
*/
static TYPE_GENERIC = 'generic'

/**
* @private
* @type {string}
*/
static TYPE_CONGLOMERATE = 'conglomerate'

/**
* Database module, such as the MongoDB or MySQL drivers.
* @type {string}
*/
static TYPE_DATASTORE = 'datastore'

/**
* Messaging module, such as AMQP.
* @type {string}
*/
static TYPE_MESSAGE = 'message'

/**
* Promise module, such as Bluebird.
* @type {string}
*/
static TYPE_PROMISE = 'promise'

/**
* @private
* @type {string}
*/
static TYPE_TRANSACTION = 'transaction'

/**
* Web server framework module, such as Express or Fastify.
* @type {string}
*/
static TYPE_WEB_FRAMEWORK = 'web-framework'

/**
* Used to load supportability metrics on installed versions of packages
* that the Node.js agent does not instrument (e.g. OTEL instrumentation or
* top logging libraries).
* @type {string}
*/
static TYPE_TRACKING = 'tracking'

/**
* The type of the module being instrumented. See the static `TYPE_` fields.
* @type {string|null}
*/
type

/**
* The name of the module being instrumented, i.e. the string used to require
* the module. This must map to a directory in `lib/instrumentations` which
* contains an `nr-hooks.js` file.
*
* This takes precedence over `moduleName`.
* @type {string}
*/
module

/**
* The name of the module being instrumented, i.e. the string used to require
* the module. This must map to a JavaScript file of the same name in the
* `lib/instrumentations` directory.
* @type {string}
*/
moduleName

/**
* The absolute path to the module to instrument. This should only be set
* when the module being instrumented does not reside in a `node_modules`
* directory; for example, when someone is instrumenting a module of their
* own through the public API.
*
* The `moduleName` property still needs to be set to the simple name, i.e.
* the string passed to `require`, for instrumentation tracking purposes.
*
* Note: this value takes precedence over `moduleName`.
*/
absolutePath

/**
* The fully resolved path to the module, e.g. `/opt/app/node_modules/foo`.
* If the module is a core module, the special value `.` should be used.
* @type {string}
*/
resolvedName

/**
* Hook to invoke when the module is required. This is the actual
* implementation of the instrumentation.
* @type {InstrumentationOnRequire}
*/
onRequire

/**
* Hook to invoke when the `onRequire` hook throws an error.
* @type {InstrumentationOnError}
*/
onError

/**
* @type {number}
*/
#id

/* eslint-disable jsdoc/require-param-description */
/**
* @param {InstrumentationDescriptorParams} params
*/
constructor(params) {
this.absolutePath = params.absolutePath
this.module = params.module
this.moduleName = params.moduleName
this.onError = params.onError
this.onRequire = params.onRequire
this.resolvedName = params.resolvedName
this.type = params.type

this.#id = idGen.idFor(this.moduleName)
}

/**
* Identifier for the instrumentation. Used by the internal instrumentation
* tracker to distinguish between different instrumentations targeting the
* same module.
*
* @returns {number} The identifier.
*/
get instrumentationId() {
return this.#id
}
}

module.exports = InstrumentationDescriptor
Loading

0 comments on commit c7e3602

Please sign in to comment.