Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added pkgVersion to shim instances to facilitate semver checking without having to re-parse the package.json #1883

Merged
merged 1 commit into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/instrumentation/@elastic/elasticsearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const { isNotEmpty } = require('../../util/objects')
* @returns {void}
*/
module.exports = function initialize(_agent, elastic, _moduleName, shim) {
const pkgVersion = shim.require('./package.json').version
const pkgVersion = shim.pkgVersion
if (semver.lt(pkgVersion, '7.13.0')) {
shim &&
shim.logger.debug(
Expand Down
2 changes: 1 addition & 1 deletion lib/instrumentation/@nestjs/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const logger = require('../../logger').child({ component: 'nestjs' })
const semver = require('semver')

module.exports = function initialize(agent, core, moduleName, shim) {
const nestJsVersion = shim.require('./package.json').version
const nestJsVersion = shim.pkgVersion
shim.setFramework(shim.NEST)
// Earliest version that runs in the tests
if (semver.lt(nestJsVersion, '8.0.0')) {
Expand Down
2 changes: 1 addition & 1 deletion lib/instrumentation/@prisma/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ function extractPrismaDatasource(client) {
* @param {object} shim New Relic shim
*/
module.exports = async function initialize(_agent, prisma, _moduleName, shim) {
const pkgVersion = shim.require('./package.json').version
const pkgVersion = shim.pkgVersion
if (semver.lt(pkgVersion, '4.0.0')) {
logger.warn(
'Skipping instrumentation of @prisma/client. Minimum supported version of library is 4.0.0, actual version %s',
Expand Down
2 changes: 1 addition & 1 deletion lib/instrumentation/cassandra-driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const semver = require('semver')
* @param {object} shim - shim for instrumentation
*/
module.exports = function initialize(_agent, cassandra, _moduleName, shim) {
const cassandraVersion = shim.require('./package.json').version
const cassandraVersion = shim.pkgVersion

shim.setDatastore(shim.CASSANDRA)

Expand Down
2 changes: 1 addition & 1 deletion lib/instrumentation/fastify.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const setupRouteHandler = (shim, fastify) => {
module.exports = function initialize(agent, fastify, moduleName, shim) {
shim.setFramework(shim.FASTIFY)

const fastifyVersion = shim.require('./package.json').version
const fastifyVersion = shim.pkgVersion
const isv3Plus = semver.satisfies(fastifyVersion, '>=3.0.0')

/**
Expand Down
2 changes: 1 addition & 1 deletion lib/instrumentation/grpc-js/grpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports.wrapStartCall = function wrappedClient(shim, callStream) {
}

module.exports.wrapServer = function wrapServer(shim, server) {
const grpcVersion = shim.require('./package.json').version
const grpcVersion = shim.pkgVersion
if (semver.lt(grpcVersion, '1.4.0')) {
shim.logger.debug('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
return
Expand Down
2 changes: 1 addition & 1 deletion lib/instrumentation/mongodb.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function initialize(agent, mongodb, moduleName, shim) {

shim.setDatastore(shim.MONGODB)

const mongoVersion = shim.require('./package.json').version
const mongoVersion = shim.pkgVersion
if (semver.satisfies(mongoVersion, '>=4.0.0')) {
instrumentV4(shim, mongodb)
} else if (semver.satisfies(mongoVersion, '>=3.0.6')) {
Expand Down
2 changes: 1 addition & 1 deletion lib/instrumentation/openai.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function shouldSkipInstrumentation(config, shim) {
return true
}

const { version: pkgVersion } = shim.require('./package.json')
const { pkgVersion } = shim
return semver.lt(pkgVersion, MIN_VERSION)
}

Expand Down
2 changes: 1 addition & 1 deletion lib/instrumentation/pino/pino.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const {
const semver = require('semver')

module.exports = function instrument(shim, tools) {
const pinoVersion = shim.require('./package.json').version
const pinoVersion = shim.pkgVersion

if (semver.lt(pinoVersion, '7.0.0')) {
shim.logger.debug('Instrumentation only supported on pino >=7.0.0.')
Expand Down
1 change: 0 additions & 1 deletion lib/metrics/names.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,6 @@ const FEATURES = {
SOURCE_MAPS: `${SUPPORTABILITY.FEATURES}/EnableSourceMaps`,
CERTIFICATES: SUPPORTABILITY.FEATURES + '/Certificates',
INSTRUMENTATION: {
ON_RESOLVED: SUPPORTABILITY.FEATURES + '/Instrumentation/OnResolved',
ON_REQUIRE: SUPPORTABILITY.FEATURES + '/Instrumentation/OnRequire'
}
}
Expand Down
5 changes: 3 additions & 2 deletions lib/shim/conglomerate-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ const SHIM_CLASSES = {
* @param {string} moduleName The name of the module being instrumented.
* @param {string} resolvedName The full path to the loaded module.
* @param {string} shimName Used to persist shim ids across different shim instances.
* @param {string} pkgVersion version of module
*/
class ConglomerateShim extends Shim {
constructor(agent, moduleName, resolvedName, shimName) {
super(agent, moduleName, resolvedName, shimName)
constructor(agent, moduleName, resolvedName, shimName, pkgVersion) {
super(agent, moduleName, resolvedName, shimName, pkgVersion)
this._logger = logger.child({ module: moduleName })
this._resolvedName = resolvedName
}
Expand Down
5 changes: 3 additions & 2 deletions lib/shim/datastore-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ const QUERY_PARSERS = {
* @param {string} moduleName The name of the module being instrumented.
* @param {string} resolvedName The full path to the loaded module.
* @param {string} shimName Used to persist shim ids across different shim instances.
* @param {string} pkgVersion version of module
* @see Shim
* @see DatastoreShim.DATASTORE_NAMES
*/
function DatastoreShim(agent, moduleName, resolvedName, shimName) {
Shim.call(this, agent, moduleName, resolvedName, shimName)
function DatastoreShim(agent, moduleName, resolvedName, shimName, pkgVersion) {
Shim.call(this, agent, moduleName, resolvedName, shimName, pkgVersion)
this._logger = logger.child({ module: moduleName })
this.queryParser = defaultParsers[this.SQL_PARSER]
}
Expand Down
19 changes: 11 additions & 8 deletions lib/shim/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,22 @@ SHIM_TYPE_MAP[constants.MODULE_TYPE.WEB_FRAMEWORK] = WebFrameworkShim

/**
*
* @param type
* @param agent
* @param moduleName
* @param resolvedName
* @param shimName
* @param {object} params input params
* @param {string} params.type shim type
* @param {Agent} params.agent instance of agent
* @param {string} params.moduleName module name
* @param {string} params.resolvedName fully resolved name of module
* @param {string} params.shimName name of shim, used to associate multiple shim instances
* @param {string} params.pkgVersion version of pkg
* @returns {Shim} shim instance
*/
function createShimFromType(type, agent, moduleName, resolvedName, shimName) {
function createShimFromType({ type, agent, moduleName, resolvedName, shimName, pkgVersion }) {
let shim = null
if (properties.hasOwn(SHIM_TYPE_MAP, type)) {
const ShimClass = SHIM_TYPE_MAP[type]
shim = new ShimClass(agent, moduleName, resolvedName, shimName)
shim = new ShimClass(agent, moduleName, resolvedName, shimName, pkgVersion)
} else {
shim = new Shim(agent, moduleName, resolvedName, shimName)
shim = new Shim(agent, moduleName, resolvedName, shimName, pkgVersion)
}
return shim
}
Expand Down
5 changes: 3 additions & 2 deletions lib/shim/message-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,12 @@ const DESTINATION_TYPES = {
* @param {string} moduleName The name of the module being instrumented.
* @param {string} resolvedName The full path to the loaded module.
* @param {string} shimName Used to persist shim ids across different shim instances.
* @param {string} pkgVersion version of module
* @see Shim
* @see TransactionShim
*/
function MessageShim(agent, moduleName, resolvedName, shimName) {
TransactionShim.call(this, agent, moduleName, resolvedName, shimName)
function MessageShim(agent, moduleName, resolvedName, shimName, pkgVersion) {
TransactionShim.call(this, agent, moduleName, resolvedName, shimName, pkgVersion)
this._logger = logger.child({ module: moduleName })
this._metrics = null
this._transportType = TransactionShim.TRANSPORT_TYPES.UNKNOWN
Expand Down
5 changes: 3 additions & 2 deletions lib/shim/promise-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ class PromiseShim extends Shim {
* @param {string} moduleName The name of the module being instrumented.
* @param {string} resolvedName The full path to the loaded module.
* @param {string} shimName Used to persist shim ids across different shim instances.
* @param {string} pkgVersion version of module
* @param shimName
* @see Shim
*/
constructor(agent, moduleName, resolvedName, shimName) {
super(agent, moduleName, resolvedName, shimName)
constructor(agent, moduleName, resolvedName, shimName, pkgVersion) {
super(agent, moduleName, resolvedName, shimName, pkgVersion)
this._logger = logger.child({ module: moduleName })
this._class = null
}
Expand Down
4 changes: 3 additions & 1 deletion lib/shim/shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ const fnApply = Function.prototype.apply
* @param {string} moduleName - The name of the module being instrumented.
* @param {string} resolvedName - The full path to the loaded module.
* @param {string} shimName - Used to persist shim ids across different instances. This is
* @param {string} pkgVersion - version of module
* applicable to instrument that compliments each other across libraries(i.e - koa + koa-route/koa-router)
*/
function Shim(agent, moduleName, resolvedName, shimName) {
function Shim(agent, moduleName, resolvedName, shimName, pkgVersion) {
if (!agent || !moduleName) {
throw new Error('Shim must be initialized with an agent and module name.')
}
Expand All @@ -44,6 +45,7 @@ function Shim(agent, moduleName, resolvedName, shimName) {
this._debug = false
this.defineProperty(this, 'moduleName', moduleName)
this.assignId(shimName)
this.pkgVersion = pkgVersion

// Determine the root directory of the module.
let moduleRoot = null
Expand Down
6 changes: 4 additions & 2 deletions lib/shim/transaction-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ const TRANSACTION_TYPES_SET = Transaction.TYPES_SET
* @param {Agent} agent - The agent the shim will use.
* @param {string} moduleName - The name of the module being instrumented.
* @param {string} resolvedName - The full path to the loaded module.
* @param {string} shimName - Used to persist shim ids across different shim instances.
* @param {string} pkgVersion - version of module
* @see Shim
* @see WebFrameworkShim
*/
function TransactionShim(agent, moduleName, resolvedName, shimName) {
Shim.call(this, agent, moduleName, resolvedName, shimName)
function TransactionShim(agent, moduleName, resolvedName, shimName, pkgVersion) {
Shim.call(this, agent, moduleName, resolvedName, shimName, pkgVersion)
this._logger = logger.child({ module: moduleName })
}
module.exports = TransactionShim
Expand Down
8 changes: 6 additions & 2 deletions lib/shim/webframework-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,15 @@ const MIDDLEWARE_TYPE_NAMES = {
* The name of the module being instrumented.
* @param {string} resolvedName
* The full path to the loaded module.
* @param {string} shimName
* Used to persist shim ids across different shim instances.
* @param {string} pkgVersion
* version of module
* @see TransactionShim
* @see WebFrameworkShim.FRAMEWORK_NAMES
*/
function WebFrameworkShim(agent, moduleName, resolvedName, shimName) {
TransactionShim.call(this, agent, moduleName, resolvedName, shimName)
function WebFrameworkShim(agent, moduleName, resolvedName, shimName, pkgVersion) {
TransactionShim.call(this, agent, moduleName, resolvedName, shimName, pkgVersion)
this._logger = logger.child({ module: moduleName })
this._routeParser = _defaultRouteParser
this._errorPredicate = _defaultErrorPredicate
Expand Down
32 changes: 19 additions & 13 deletions lib/shimmer.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,12 @@ const shimmer = (module.exports = {
// Since this just registers subscriptions to diagnostics_channel events from undici
// We register this as core and it'll work for both fetch and undici
const undiciPath = path.join(__dirname, 'instrumentation', 'undici.js')
const undiciShim = shims.createShimFromType(MODULE_TYPE.TRANSACTION, agent, 'undici', 'undici')
const undiciShim = shims.createShimFromType({
type: MODULE_TYPE.TRANSACTION,
agent,
moduleName: 'undici',
resolvedName: 'undici'
})
_firstPartyInstrumentation(agent, undiciPath, undiciShim)

// Instrument each of the core modules.
Expand All @@ -345,7 +350,12 @@ const shimmer = (module.exports = {
logger.trace('Could not load core module %s got error %s', mojule, err)
}

const shim = shims.createShimFromType(core.type, agent, mojule, mojule)
const shim = shims.createShimFromType({
type: core.type,
agent,
moduleName: mojule,
resolvedName: mojule
})
applyDebugState(shim, core, false)
_firstPartyInstrumentation(agent, filePath, shim, uninstrumented, mojule)
}
Expand Down Expand Up @@ -531,13 +541,14 @@ function instrumentPostLoad(agent, nodule, moduleName, resolvedName, esmResolver
// callback hook
const resolvedNodule = instrumentation.isEsm || !esmResolver ? nodule : nodule.default

const shim = shims.createShimFromType(
instrumentation.type,
const shim = shims.createShimFromType({
type: instrumentation.type,
agent,
moduleName,
resolvedName,
instrumentation.shimName
)
shimName: instrumentation.shimName,
pkgVersion
})

applyDebugState(shim, resolvedNodule, esmResolver)
trackInstrumentationUsage(
Expand Down Expand Up @@ -710,14 +721,9 @@ function trackInstrumentationUsage(agent, shim, moduleName, metricPrefix) {

function tryGetVersion(shim) {
// Global module (i.e. domain) or finding root failed
if (shim._moduleRoot === '.') {
return
}

const packageInfo = shim.require('./package.json')
if (!packageInfo) {
if (shim._moduleRoot === '/') {
return
}

return packageInfo.version
return shim.pkgVersion
}
6 changes: 3 additions & 3 deletions test/unit/instrumentation/nest.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ test('Nest unit tests', (t) => {

t.test('Should record the error when in a transaction', (t) => {
// Minimum Nest.js version supported.
shim.require = sinon.stub().returns({ version: '8.0.0' })
shim.pkgVersion = '8.0.0'
initialize(agent, mockCore, '@nestjs/core', shim)

helper.runInTransaction(agent, (tx) => {
Expand Down Expand Up @@ -69,7 +69,7 @@ test('Nest unit tests', (t) => {

t.test('Should ignore the error when not in a transaction', (t) => {
// Minimum Nest.js version supported.
shim.require = sinon.stub().returns({ version: '8.0.0' })
shim.pkgVersion = '8.0.0'
initialize(agent, mockCore, '@nestjs/core', shim)

const err = new Error('something went wrong')
Expand All @@ -96,7 +96,7 @@ test('Nest unit tests', (t) => {

t.test('Should not instrument versions earlier than 8.0.0', (t) => {
// Unsupported version
shim.require = sinon.stub().returns({ version: '7.4.0' })
shim.pkgVersion = '7.4.0'
initialize(agent, mockCore, '@nestjs/core', shim)

const exceptionFilter = new mockCore.BaseExceptionFilter()
Expand Down
5 changes: 2 additions & 3 deletions test/unit/instrumentation/openai.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ test('openai unit tests', (t) => {
const agent = helper.loadMockedAgent()
agent.config.ai_monitoring = { enabled: true }
const shim = new GenericShim(agent, 'openai')
sandbox.stub(shim, 'require')
shim.require.returns({ version: '4.0.0' })
shim.pkgVersion = '4.0.0'
sandbox.stub(shim.logger, 'debug')

t.context.agent = agent
Expand Down Expand Up @@ -57,7 +56,7 @@ test('openai unit tests', (t) => {
t.test('should not register instrumentation if openai is < 4.0.0', (t) => {
const { shim, agent, initialize } = t.context
const MockOpenAi = getMockModule()
shim.require.returns({ version: '3.7.0' })
shim.pkgVersion = '3.7.0'
initialize(agent, MockOpenAi, 'openai', shim)
t.equal(shim.logger.debug.callCount, 1, 'should log 2 debug messages')
t.equal(
Expand Down
7 changes: 3 additions & 4 deletions test/unit/instrumentation/prisma-client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ test('PrismaClient unit tests', (t) => {
agent = helper.loadMockedAgent()
initialize = require('../../../lib/instrumentation/@prisma/client')
shim = new DatastoreShim(agent, 'prisma')
sandbox.stub(shim, 'require')
shim.require.returns({ version: '4.0.0' })
shim.pkgVersion = '4.0.0'
})

t.afterEach(function () {
Expand Down Expand Up @@ -220,7 +219,7 @@ test('PrismaClient unit tests', (t) => {
const MockPrismaClient = getMockModule()
const prisma = { PrismaClient: MockPrismaClient }

shim.require.returns({ version })
shim.pkgVersion = version
initialize(agent, prisma, '@prisma/client', shim)
const client = new prisma.PrismaClient()
client._engine.datamodel = `
Expand Down Expand Up @@ -256,7 +255,7 @@ test('PrismaClient unit tests', (t) => {
const MockPrismaClient = getMockModule()
const prisma = { PrismaClient: MockPrismaClient }

shim.require.returns({ version: '3.8.0' })
shim.pkgVersion = '3.8.0'
initialize(agent, prisma, '@prisma/client', shim)
const client = new prisma.PrismaClient()
t.notOk(shim.isWrapped(client._executeRequest), 'should not instrument @prisma/client')
Expand Down
11 changes: 11 additions & 0 deletions test/unit/shim/conglomerate-shim.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,15 @@ test('ConglomerateShim', (t) => {
t.ok(shim.makeSpecializedShim(shim.WEB_FRAMEWORK, 'foobar') instanceof WebFrameworkShim)
t.end()
})

t.test('should assign properties from parent', (t) => {
const mod = 'test-mod'
const name = mod
const version = '1.0.0'
const shim = new ConglomerateShim(agent, mod, mod, name, version)
t.equal(shim.moduleName, mod)
t.equal(agent, shim._agent)
t.equal(shim.pkgVersion, version)
t.end()
})
})