diff --git a/package-lock.json b/package-lock.json index 59af50e111..20eec0d1fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -162,6 +162,8 @@ "socket.io-client": "^4.7.5", "sqs-consumer": "^10.3.0", "sqs-consumer-v5": "npm:sqs-consumer@^5.7.0", + "square-calc": "3.2.1", + "square-calc-v2": "npm:square-calc@2.4.0", "stealthy-require": "1.1.1", "superagent": "^9.0.2", "tedious": "^17.0.0", @@ -47567,6 +47569,19 @@ "aws-sdk": "^2.1271.0" } }, + "node_modules/square-calc": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/square-calc/-/square-calc-3.2.1.tgz", + "integrity": "sha512-43TtHbgIdE5V9hPsIMRPXFcxQvP0QXZ2MjoI/xak3YiIJ4GMsdhFwXnShM3CCcIovaOd35KzF+xxQX0urACHqw==", + "dev": true + }, + "node_modules/square-calc-v2": { + "name": "square-calc", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/square-calc/-/square-calc-2.4.0.tgz", + "integrity": "sha512-9ud3FeFngJmIpEsiep4lvNhw1NvKLtirowvaMomMeHjyzJckDRbSbLkFv3Jjae6j+ZtxTd2eJvO8izkdpGCDDA==", + "dev": true + }, "node_modules/sshpk": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", @@ -51212,6 +51227,7 @@ "@opentelemetry/instrumentation-tedious": "0.11.0", "@opentelemetry/sdk-trace-base": "1.25.0", "cls-bluebird": "^2.1.0", + "import-in-the-middle": "1.8.1", "lru-cache": "^10.1.0", "methods": "^1.1.2", "opentracing": "^0.14.5", @@ -51316,6 +51332,17 @@ "@opentelemetry/api": "^1.3.0" } }, + "packages/core/node_modules/@opentelemetry/instrumentation-fs/node_modules/import-in-the-middle": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz", + "integrity": "sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-assertions": "^1.9.0", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, "packages/core/node_modules/@opentelemetry/instrumentation-restify": { "version": "0.38.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.38.0.tgz", @@ -51351,6 +51378,17 @@ "@opentelemetry/api": "^1.3.0" } }, + "packages/core/node_modules/@opentelemetry/instrumentation-restify/node_modules/import-in-the-middle": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz", + "integrity": "sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-assertions": "^1.9.0", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, "packages/core/node_modules/@opentelemetry/instrumentation-socket.io": { "version": "0.39.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.39.0.tgz", @@ -51385,6 +51423,17 @@ "@opentelemetry/api": "^1.3.0" } }, + "packages/core/node_modules/@opentelemetry/instrumentation-socket.io/node_modules/import-in-the-middle": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz", + "integrity": "sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-assertions": "^1.9.0", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, "packages/core/node_modules/@opentelemetry/instrumentation-tedious": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.11.0.tgz", @@ -51463,12 +51512,12 @@ } }, "packages/core/node_modules/import-in-the-middle": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz", - "integrity": "sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.8.1.tgz", + "integrity": "sha512-yhRwoHtiLGvmSozNOALgjRPFI6uYsds60EoMqqnXyyv+JOIW/BrrLejuTGBt+bq0T5tLzOHrN0T7xYTm4Qt/ng==", "dependencies": { "acorn": "^8.8.2", - "acorn-import-assertions": "^1.9.0", + "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^1.2.2", "module-details-from-path": "^1.0.3" } diff --git a/package.json b/package.json index 8fbb049684..b3251ee2eb 100644 --- a/package.json +++ b/package.json @@ -220,6 +220,8 @@ "socket.io-client": "^4.7.5", "sqs-consumer": "^10.3.0", "sqs-consumer-v5": "npm:sqs-consumer@^5.7.0", + "square-calc": "3.2.1", + "square-calc-v2": "npm:square-calc@2.4.0", "stealthy-require": "1.1.1", "superagent": "^9.0.2", "tedious": "^17.0.0", diff --git a/packages/aws-fargate/esm-loader.mjs b/packages/aws-fargate/esm-loader.mjs index 3b25bb492d..f0962b70a6 100644 --- a/packages/aws-fargate/esm-loader.mjs +++ b/packages/aws-fargate/esm-loader.mjs @@ -24,3 +24,6 @@ */ import './src/index.js'; +// Here we export all named exports from '@instana/core/iitm-loader.mjs', enabling +// integration of import-in-the-middle (IITM) for Native ESM module support. +export * from '@instana/core/iitm-loader.mjs'; diff --git a/packages/aws-fargate/esm-register.mjs b/packages/aws-fargate/esm-register.mjs index b011f8a5e8..fbed0ffa0a 100644 --- a/packages/aws-fargate/esm-register.mjs +++ b/packages/aws-fargate/esm-register.mjs @@ -9,7 +9,7 @@ * Previously, loading the Instana collector within the loader and after the update ESM support * no longer working with v18.19 and above. To address this, we've opted to load the Instana * collector in the main thread using --import. - * Additionally, we aim to incorporate native ESM support by utilizing the node register method, + * Additionally, we incorporated native ESM support by utilizing the node register method, * enabling customization of the ESM loader with 'import-in-the-middle'. * * Usage: @@ -19,7 +19,8 @@ // Import the initialization module for aws-fargate collector; it self-initializes upon import // and it should be executed in the main thread. import './src/index.js'; - -// We plan to utilize this for adding native ESM support in the near future -// import { register } from 'node:module'; -// register(./loader.mjs, import.meta.url); \ No newline at end of file +import { register } from 'node:module'; +// ESM module resolution and loading are facilitated by registering `@instana/core/iitm-loader.mjs`, which exports +// import-in-the-middle(IITM) hooks. This registration can be accomplished using the register method from node:module. +// see: https://nodejs.org/api/module.html#customization-hooks +register('@instana/core/iitm-loader.mjs', import.meta.url); \ No newline at end of file diff --git a/packages/azure-container-services/esm-loader.mjs b/packages/azure-container-services/esm-loader.mjs index c87787f4fd..06178f22b9 100644 --- a/packages/azure-container-services/esm-loader.mjs +++ b/packages/azure-container-services/esm-loader.mjs @@ -24,3 +24,6 @@ */ import './src/index.js'; +// Here we export all named exports from '@instana/core/iitm-loader.mjs', enabling +// integration of import-in-the-middle (IITM) for Native ESM module support. +export * from '@instana/core/iitm-loader.mjs'; diff --git a/packages/azure-container-services/esm-register.mjs b/packages/azure-container-services/esm-register.mjs index d33dae0917..a49e88948c 100644 --- a/packages/azure-container-services/esm-register.mjs +++ b/packages/azure-container-services/esm-register.mjs @@ -8,7 +8,7 @@ * see https://github.com/nodejs/node/pull/44710. * Previously, loading the Instana collector within the loader and after the update ESM support * no longer working with v18.19 and above. To address this, we've opted to load the Instana - * collector in the main thread using --import. Additionally, we aim to incorporate native ESM + * collector in the main thread using --import. Additionally, we incorporated native ESM * support by utilizing the node register method, enabling customization of the ESM loader * with 'import-in-the-middle'. * @@ -19,7 +19,8 @@ // Import the initialization module for azure-container-services collector; it self-initializes upon import // and it should be executed in the main thread. import './src/index.js'; - -// We plan to utilize this for adding native ESM support in the near future -// import { register } from 'node:module'; -// register(./loader.mjs, import.meta.url); \ No newline at end of file +import { register } from 'node:module'; +// ESM module resolution and loading are facilitated by registering `@instana/core/iitm-loader.mjs`, which exports +// import-in-the-middle(IITM) hooks. This registration can be accomplished using the register method from node:module. +// see: https://nodejs.org/api/module.html#customization-hooks +register('@instana/core/iitm-loader.mjs', import.meta.url); \ No newline at end of file diff --git a/packages/collector/esm-loader.mjs b/packages/collector/esm-loader.mjs index e778551940..cc6fb4b346 100644 --- a/packages/collector/esm-loader.mjs +++ b/packages/collector/esm-loader.mjs @@ -20,3 +20,6 @@ import instana from './src/index.js'; instana(); +// Here we export all named exports from '@instana/core/iitm-loader.mjs', enabling +// integration of import-in-the-middle (IITM) for Native ESM module support. +export * from '@instana/core/iitm-loader.mjs'; diff --git a/packages/collector/esm-register.mjs b/packages/collector/esm-register.mjs index 0b33f424ca..b8d1e33c8f 100644 --- a/packages/collector/esm-register.mjs +++ b/packages/collector/esm-register.mjs @@ -8,7 +8,7 @@ * see https://github.com/nodejs/node/pull/44710. * Previously, loading the Instana collector within the loader and after the update ESM support * no longer working with v18.19 and above. To address this, we've opted to load the Instana - * collector in the main thread using --import. Additionally, we aim to incorporate native ESM + * collector in the main thread using --import. Additionally, we incorporated native ESM * support by utilizing the node register method, enabling customization of the ESM loader * with 'import-in-the-middle'. * @@ -19,7 +19,8 @@ // Import the initialization module for Instana collector and it should be executed in the main thread. import instana from './src/index.js'; instana(); - -// We plan to utilize this for adding native ESM support in the near future -// import { register } from 'node:module'; -// register(./loader.mjs, import.meta.url); +// ESM module resolution and loading are facilitated by registering `@instana/core/iitm-loader.mjs`, which exports +// import-in-the-middle(IITM) hooks. This registration can be accomplished using the register method from node:module. +// see: https://nodejs.org/api/module.html#customization-hooks +import { register } from 'node:module'; +register('@instana/core/iitm-loader.mjs', import.meta.url); diff --git a/packages/collector/src/tracing/instrumentation/process/edgemicro.js b/packages/collector/src/tracing/instrumentation/process/edgemicro.js index d78b84eecc..8ea66904aa 100644 --- a/packages/collector/src/tracing/instrumentation/process/edgemicro.js +++ b/packages/collector/src/tracing/instrumentation/process/edgemicro.js @@ -7,7 +7,7 @@ const cluster = require('cluster'); -const requireHook = require('@instana/core').util.requireHook; +const hook = require('@instana/core').util.hook; const selfPath = require('./selfPath'); /** @type {import('@instana/core/src/logger').GenericLogger} */ @@ -17,7 +17,7 @@ logger = require('../../../logger').getLogger('tracing/edgemicro', newLogger => }); exports.init = function () { - requireHook.onFileLoad(/\/edgemicro\/cli\/lib\/reload-cluster.js/, instrumentReloadCluster); + hook.onFileLoad(/\/edgemicro\/cli\/lib\/reload-cluster.js/, instrumentReloadCluster); }; /** diff --git a/packages/collector/test/tracing/database/elasticsearch/mockVersion.js b/packages/collector/test/tracing/database/elasticsearch/mockVersion.js index fdfafb07f0..5912592db6 100644 --- a/packages/collector/test/tracing/database/elasticsearch/mockVersion.js +++ b/packages/collector/test/tracing/database/elasticsearch/mockVersion.js @@ -5,7 +5,7 @@ 'use strict'; const mock = require('mock-require'); -const requireHook = require('../../../../../core/src/util/requireHook'); +const hook = require('../../../../../core/src/util/hook'); const ELASTIC_VERSION = process.env.ELASTIC_VERSION; const ELASTIC_REQUIRE = process.env.ELASTIC_VERSION === 'latest' ? '@elastic/elasticsearch' : `@elastic/elasticsearch-v${ELASTIC_VERSION}`; @@ -14,8 +14,8 @@ if (ELASTIC_REQUIRE !== '@elastic/elasticsearch') { mock('@elastic/elasticsearch', ELASTIC_REQUIRE); } -const originalFn = requireHook.onModuleLoad; -requireHook.onModuleLoad = function onModuleLoad() { +const originalFn = hook.onModuleLoad; +hook.onModuleLoad = function onModuleLoad() { if (arguments[0] !== '@elastic/elasticsearch') { return originalFn.apply(this, arguments); } diff --git a/packages/collector/test/tracing/database/mongodb/mockVersion.js b/packages/collector/test/tracing/database/mongodb/mockVersion.js index 22d8e7257d..86278e79a0 100644 --- a/packages/collector/test/tracing/database/mongodb/mockVersion.js +++ b/packages/collector/test/tracing/database/mongodb/mockVersion.js @@ -5,7 +5,7 @@ 'use strict'; const mock = require('mock-require'); -const requireHook = require('../../../../../core/src/util/requireHook'); +const hook = require('../../../../../core/src/util/hook'); const MONGODB_VERSION = process.env.MONGODB_VERSION; const MONGODB_REQUIRE = process.env.MONGODB_VERSION === 'latest' ? 'mongodb' : `mongodb-${MONGODB_VERSION}`; @@ -14,8 +14,8 @@ if (MONGODB_REQUIRE !== 'mongodb') { mock('mongodb', MONGODB_REQUIRE); } -const originalOnFileLoad = requireHook.onFileLoad; -requireHook.onFileLoad = function onFileLoad() { +const originalOnFileLoad = hook.onFileLoad; +hook.onFileLoad = function onFileLoad() { if ( arguments[0].source === '\\/mongodb\\/lib\\/cmap\\/connection\\.js' || arguments[0].source === '\\/mongodb\\/lib\\/core\\/connection\\/pool\\.js' diff --git a/packages/collector/test/tracing/database/prisma/test.js b/packages/collector/test/tracing/database/prisma/test.js index cd952bb6db..ce95a00638 100644 --- a/packages/collector/test/tracing/database/prisma/test.js +++ b/packages/collector/test/tracing/database/prisma/test.js @@ -29,8 +29,21 @@ describe('tracing/prisma', function () { ['latest', 'v4', 'v450'].forEach(version => { providers.forEach(provider => { - let mochaSuiteFn = supportedVersion(process.versions.node) ? describe : describe.skip; - mochaSuiteFn = version === 'latest' && semver.lt(process.versions.node, '16.0.0') ? describe.skip : describe; + let mochaSuiteFn = describe; + + if (supportedVersion(process.versions.node)) { + // Skip ESM tests for Node.js version 18.19.0 and above due to an issue with import-in-the-middle (IITM) + // package. See https://github.com/DataDog/import-in-the-middle/issues/97 + if (semver.gte(process.versions.node, '18.19.0') && process.env.RUN_ESM) { + mochaSuiteFn = describe.skip; + } + + if (version === 'latest' && semver.lt(process.versions.node, '16.0.0')) { + mochaSuiteFn = describe.skip; + } + } else { + mochaSuiteFn = describe.skip; + } mochaSuiteFn(`[${version}] with provider ${provider}`, () => { if (provider === 'postgresql' && !process.env.PRISMA_POSTGRES_URL) { diff --git a/packages/collector/test/tracing/logger/pino/mockVersion.js b/packages/collector/test/tracing/logger/pino/mockVersion.js index 8dc3c364b4..8294971473 100644 --- a/packages/collector/test/tracing/logger/pino/mockVersion.js +++ b/packages/collector/test/tracing/logger/pino/mockVersion.js @@ -5,7 +5,7 @@ 'use strict'; const mock = require('mock-require'); -const requireHook = require('../../../../../core/src/util/requireHook'); +const hook = require('../../../../../core/src/util/hook'); const PINO_VERSION = process.env.PINO_VERSION || 'latest'; const PINO_REQUIRE = PINO_VERSION === 'latest' ? 'pino' : `pino-${PINO_VERSION}`; @@ -22,8 +22,8 @@ if (PINO_REQUIRE !== 'pino') { * If we test against `pino-v6`, we need to wait for the * on file load event for `node_modules/pino-v6/....js` */ -const originalOnFileLoad = requireHook.onFileLoad; -requireHook.onFileLoad = function onFileLoad() { +const originalOnFileLoad = hook.onFileLoad; +hook.onFileLoad = function onFileLoad() { if (arguments[0].toString() !== '/\\/pino\\/lib\\/tools\\.js/') { return originalOnFileLoad.apply(this, arguments); } diff --git a/packages/collector/test/tracing/messaging/amqp/mockVersion.js b/packages/collector/test/tracing/messaging/amqp/mockVersion.js index 4a17b20f1c..9016d70cf8 100644 --- a/packages/collector/test/tracing/messaging/amqp/mockVersion.js +++ b/packages/collector/test/tracing/messaging/amqp/mockVersion.js @@ -6,7 +6,7 @@ const Module = require('module'); const mock = require('mock-require'); -const requireHook = require('../../../../../core/src/util/requireHook'); +const hook = require('../../../../../core/src/util/hook'); const AMQPLIB_REQUIRE = process.env.AMQPLIB_VERSION === 'latest' ? 'amqplib' : `amqplib-${process.env.AMQPLIB_VERSION}`; @@ -23,8 +23,8 @@ if (AMQPLIB_REQUIRE !== 'amqplib') { * If we test against `amqplib-v0.8.0`, we need to wait for the * on file load event for `node_modules/amqplib-v0.8.0/lib/... */ -const originalOnFileLoad = requireHook.onFileLoad; -requireHook.onFileLoad = function onFileLoad() { +const originalOnFileLoad = hook.onFileLoad; +hook.onFileLoad = function onFileLoad() { if ( arguments[0].source === '\\/amqplib\\/lib\\/channel\\.js' || arguments[0].source === '\\/amqplib\\/lib\\/channel_model\\.js' || diff --git a/packages/collector/test/tracing/native_esm/app.js b/packages/collector/test/tracing/native_esm/app.js new file mode 100644 index 0000000000..d158ad7f94 --- /dev/null +++ b/packages/collector/test/tracing/native_esm/app.js @@ -0,0 +1,52 @@ +/* + * (c) Copyright IBM Corp. 2024 + */ + +'use strict'; + +// NOTE: c8 bug https://github.com/bcoe/c8/issues/166 +process.on('SIGTERM', () => { + process.disconnect(); + process.exit(0); +}); +const mock = require('mock-require'); +mock('square-calc', 'square-calc-v2'); +require('../../..')(); +const express = require('express'); +const morgan = require('morgan'); +const bodyParser = require('body-parser'); +const getAppPort = require('../../test_util/app-port'); +const calculateSquare = require('square-calc'); + +const port = getAppPort(); + +const app = express(); +const logPrefix = `Native ESM App (${process.pid}):\t`; + +if (process.env.WITH_STDOUT) { + app.use(morgan(`${logPrefix}:method :url :status`)); +} + +app.use(bodyParser.json()); + +app.get('/', async (req, res) => { + res.sendStatus(200); +}); + +app.get('/request', async (req, res) => { + const square = calculateSquare(5); + res.json({ square }); +}); + +app.listen(port, () => { + log(`Listening on port: ${port}`); +}); + +function log() { + const args = Array.prototype.slice.call(arguments); + args[0] = logPrefix + args[0]; + // eslint-disable-next-line no-console + console.log.apply(console, args); +} + +module.exports = app; diff --git a/packages/collector/test/tracing/native_esm/app.mjs b/packages/collector/test/tracing/native_esm/app.mjs new file mode 100644 index 0000000000..ddec1fa7b6 --- /dev/null +++ b/packages/collector/test/tracing/native_esm/app.mjs @@ -0,0 +1,47 @@ +/* + * (c) Copyright IBM Corp. 2024 + */ + +'use strict'; + +// NOTE: c8 bug https://github.com/bcoe/c8/issues/166 +process.on('SIGTERM', () => { + process.disconnect(); + process.exit(0); +}); +import express from 'express'; +import morgan from 'morgan'; +import bodyParser from 'body-parser'; +import getAppPort from '../../test_util/app-port.js'; +import calculateSquare from 'square-calc'; +const port = getAppPort(); + +const app = express(); +const logPrefix = `Native ESM App (${process.pid}):\t`; + +const agentPort = process.env.INSTANA_AGENT_PORT; + +if (process.env.WITH_STDOUT) { + app.use(morgan(`${logPrefix}:method :url :status`)); +} + +app.use(bodyParser.json()); + +app.get('/', async (req, res) => { + res.sendStatus(200); +}); + +app.get('/request', async (req, res) => { + const square = calculateSquare(5); + res.json({ square }); +}); + +app.listen(port, () => { + log(`Listening on port: ${port}`); +}); + +function log() { + const args = Array.prototype.slice.call(arguments); + args[0] = logPrefix + args[0]; + console.log.apply(console, args); +} diff --git a/packages/collector/test/tracing/native_esm/customInstrumentation/squareCalc.js b/packages/collector/test/tracing/native_esm/customInstrumentation/squareCalc.js new file mode 100644 index 0000000000..6d5a1fc6ae --- /dev/null +++ b/packages/collector/test/tracing/native_esm/customInstrumentation/squareCalc.js @@ -0,0 +1,60 @@ +/* + * (c) Copyright IBM Corp. 2024 + */ + +'use strict'; + +const { EXIT } = require('@instana/core').tracing.constants; +const tracingUtil = require('../../../../../core/src/tracing/tracingUtil'); +const cls = require('../../../../../core/src/tracing/cls'); +const hook = require('@instana/core').util.hook; +// eslint-disable-next-line no-unused-vars +let isActive = false; + +exports.activate = function activate() { + isActive = true; +}; + +exports.deactivate = function deactivate() { + isActive = false; +}; + +exports.init = function init() { + hook.onModuleLoad('square-calc', instrument, { nativeEsm: true }); +}; + +/** + * Instruments the 'calculateSquare' method of the 'square-calc' module. + * @param {{ calculateSquare: (...args: any[]) => undefined; }} orgModule - The original module. + */ +function instrument(orgModule) { + const originalCalculateSquare = orgModule; + + orgModule = function () { + const number = arguments[0]; + if (cls.skipExitTracing({ isActive })) { + return originalCalculateSquare.apply(this, arguments); + } + return cls.ns.runAndReturn(() => { + const span = cls.startSpan('square-calc', EXIT, null, null); + span.ts = Date.now(); + span.stack = tracingUtil.getStackTrace(instrument, 1); + + try { + const result = originalCalculateSquare.apply(this, arguments); + span.d = Date.now() - span.ts; + span.data.calculator = { number, method: 'calculateSquare' }; + span.transmit(); + return result; + } catch (err) { + span.ec = 1; + span.data.calculator.error = err.message; + span.d = Date.now() - span.ts; + span.transmit(); + throw err; + } + }, null); + }; + + return orgModule; +} diff --git a/packages/collector/test/tracing/native_esm/test.js b/packages/collector/test/tracing/native_esm/test.js new file mode 100644 index 0000000000..2802a7e6ab --- /dev/null +++ b/packages/collector/test/tracing/native_esm/test.js @@ -0,0 +1,78 @@ +/* + * (c) Copyright IBM Corp. 2024 + */ + +'use strict'; + +const expect = require('chai').expect; +const supportedVersion = require('@instana/core').tracing.supportedVersion; +const config = require('../../../../core/test/config'); +const { retry, verifyEntrySpan } = require('../../../../core/test/test_util'); +const ProcessControls = require('../../test_util/ProcessControls'); +const globalAgent = require('../../globalAgent'); +const constants = require('@instana/core').tracing.constants; +const path = require('path'); +const mochaSuiteFn = supportedVersion(process.versions.node) ? describe : describe.skip; + +mochaSuiteFn('tracing/native-esm modules', function () { + this.timeout(config.getTestTimeout()); + + globalAgent.setUpCleanUpHooks(); + const agentControls = globalAgent.instance; + + let controls; + + before(async () => { + controls = new ProcessControls({ + dirname: __dirname, + useGlobalAgent: true, + env: { + INSTANA_CUSTOM_INSTRUMENTATIONS: [path.resolve(__dirname, './customInstrumentation/squareCalc')] + } + }); + await controls.startAndWaitForAgentConnection(); + }); + + beforeEach(async () => { + await agentControls.clearReceivedTraceData(); + }); + + after(async () => { + await controls.stop(); + }); + + afterEach(async () => { + await controls.clearIpcMessages(); + }); + + it('should collect spans', async () => { + await controls.sendRequest({ + method: 'GET', + path: '/request' + }); + + await retry(async () => { + const spans = await agentControls.getSpans(); + expect(spans.length).to.equal(2); + verifyEntrySpan({ + spanName: 'node.http.server', + spans, + withError: false, + pid: String(controls.getPid()), + dataProperty: 'http', + extraTests: [ + span => { + expect(span.data.http.method).to.equal('GET'); + expect(span.data.http.url).to.equal('/request'); + expect(span.data.http.status).to.equal(200); + } + ] + }); + + const calculatorSpan = spans.find(span => span.n === 'square-calc'); + expect(calculatorSpan).to.exist; + expect(calculatorSpan.k).to.equal(constants.EXIT); + expect(calculatorSpan.data.calculator).to.be.an('object'); + }); + }); +}); diff --git a/packages/collector/test/tracing/protocols/graphql/mockVersion.js b/packages/collector/test/tracing/protocols/graphql/mockVersion.js index 2d82b1f6ba..1f857249d1 100644 --- a/packages/collector/test/tracing/protocols/graphql/mockVersion.js +++ b/packages/collector/test/tracing/protocols/graphql/mockVersion.js @@ -8,7 +8,7 @@ const semver = require('semver'); const path = require('path'); const mock = require('mock-require'); -const requireHook = require('../../../../../core/src/util/requireHook'); +const hook = require('../../../../../core/src/util/hook'); const graphqlMajorDefault = semver.major(require(`${path.dirname(require.resolve('graphql'))}/package.json`).version); const GRAPHQL_VERSION = process.env.GRAPHQL_VERSION || graphqlMajorDefault.toString(); @@ -45,8 +45,8 @@ if (GRAPHQL_REQUIRE !== 'graphql') { * If we test against `graphql-v16`, we need to wait for the * on file load event for `node_modules/graphql-v16/execution/execution.js` */ -const originalOnFileLoad = requireHook.onFileLoad; -requireHook.onFileLoad = function onFileLoad() { +const originalOnFileLoad = hook.onFileLoad; +hook.onFileLoad = function onFileLoad() { if (arguments[0].toString() !== '/\\/graphql\\/execution\\/execute.js/') { return originalOnFileLoad.apply(this, arguments); } diff --git a/packages/collector/test/tracing/protocols/grpc-js/mockVersion.js b/packages/collector/test/tracing/protocols/grpc-js/mockVersion.js index 96acb7762d..c44df83f9b 100644 --- a/packages/collector/test/tracing/protocols/grpc-js/mockVersion.js +++ b/packages/collector/test/tracing/protocols/grpc-js/mockVersion.js @@ -5,7 +5,7 @@ 'use strict'; const mock = require('mock-require'); -const requireHook = require('../../../../../core/src/util/requireHook'); +const hook = require('../../../../../core/src/util/hook'); const INSTANA_GRPC_VERSION = process.env.INSTANA_GRPC_VERSION; const GRPC_REQUIRE = @@ -15,8 +15,8 @@ if (GRPC_REQUIRE !== '@grpc/grpc-js') { mock('@grpc/grpc-js', GRPC_REQUIRE); } -const originalOnFileLoad = requireHook.onFileLoad; -requireHook.onFileLoad = function onFileLoad() { +const originalOnFileLoad = hook.onFileLoad; +hook.onFileLoad = function onFileLoad() { if ( arguments[0].source === '\\/@grpc\\/grpc-js\\/build\\/src\\/server\\.js' || arguments[0].source === '\\/@grpc\\/grpc-js\\/build\\/src\\/client\\.js' diff --git a/packages/core/iitm-loader.mjs b/packages/core/iitm-loader.mjs new file mode 100644 index 0000000000..252297264e --- /dev/null +++ b/packages/core/iitm-loader.mjs @@ -0,0 +1,5 @@ +/* + * (c) Copyright IBM Corp. 2024 + */ + +export * from 'import-in-the-middle/hook.mjs'; diff --git a/packages/core/package.json b/packages/core/package.json index a5f8dd35b7..f445262118 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -64,6 +64,7 @@ "@opentelemetry/instrumentation-tedious": "0.11.0", "@opentelemetry/sdk-trace-base": "1.25.0", "cls-bluebird": "^2.1.0", + "import-in-the-middle": "1.8.1", "lru-cache": "^10.1.0", "methods": "^1.1.2", "opentracing": "^0.14.5", diff --git a/packages/core/src/tracing/index.js b/packages/core/src/tracing/index.js index 7890eeaf13..22da03ac40 100644 --- a/packages/core/src/tracing/index.js +++ b/packages/core/src/tracing/index.js @@ -18,8 +18,10 @@ const { otelInstrumentations } = require('./opentelemetry-instrumentations'); const { esmSupportedVersion, isLatestEsmSupportedVersion, - hasExperimentalLoaderFlag -} = require('./esmSupportedVersion'); + hasExperimentalLoaderFlag, + isESMApp +} = require('../util/esm'); +const iitmHook = require('../util/iitmHook'); let tracingEnabled = false; let tracingActivated = false; @@ -42,7 +44,7 @@ let config = null; let processIdentityProvider = null; // Note: Also update initializedTooLateHeuristic.js and the accompanying test when adding instrumentations. -const instrumentations = [ +let instrumentations = [ './instrumentation/cloud/aws-sdk/v2/index', './instrumentation/cloud/aws-sdk/v3/index', './instrumentation/cloud/aws-sdk/v2/sdk', @@ -94,6 +96,15 @@ const instrumentations = [ './instrumentation/protocols/superagent' ]; +/** + * @type {string[]} + */ +const customInstrumentations = process.env.INSTANA_CUSTOM_INSTRUMENTATIONS + ? process.env.INSTANA_CUSTOM_INSTRUMENTATIONS.split(',') + : []; +if (customInstrumentations.length > 0) { + instrumentations = instrumentations.concat(customInstrumentations); +} /** * This is a temporary type definition for instrumented modules until we get to add types to these modules. * For now it is safe to say that these modules are objects with the following methods: @@ -133,8 +144,12 @@ exports.isLatestEsmSupportedVersion = isLatestEsmSupportedVersion; * @param {string} instrumentationKey */ const isInstrumentationDisabled = (cfg, instrumentationKey) => { - const extractedInstrumentationName = instrumentationKey.match(/.\/instrumentation\/[^/]*\/(.*)/)[1]; - + // Extracts the instrumentation name using the pattern '.\/instrumentation\/[^/]*\/(.*)', + // capturing the part after '/instrumentation/.../'. If this pattern doesn't match, + // it falls back to extracting the last part of the path after the final '/'. + // This is primarily implemented to handle customInstrumentation cases. + const matchResult = instrumentationKey.match(/.\/instrumentation\/[^/]*\/(.*)/); + const extractedInstrumentationName = matchResult ? matchResult[1] : instrumentationKey.match(/\/([^/]+)$/)[1]; return ( cfg.tracing.disabledTracers.includes(extractedInstrumentationName.toLowerCase()) || (instrumentationModules[instrumentationKey].instrumentationName && @@ -196,6 +211,9 @@ exports.init = function init(_config, downstreamConnection, _processIdentityProv if (_config.tracing.useOpentelemetry) { otelInstrumentations.init(config, cls); } + if (isESMApp()) { + iitmHook.init(); + } } } diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/index.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/index.js index 86a65deea8..14ff027291 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/index.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/index.js @@ -6,7 +6,7 @@ 'use strict'; const shimmer = require('../../../../shimmer'); -const requireHook = require('../../../../../util/requireHook'); +const hook = require('../../../../../util/hook'); /** @type {Array. */ const awsProducts = [ @@ -34,7 +34,7 @@ exports.isActive = function () { }; exports.init = function init() { - requireHook.onModuleLoad('aws-sdk', instrumentAWS); + hook.onModuleLoad('aws-sdk', instrumentAWS); }; exports.activate = function activate() { diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sdk.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sdk.js index 7c44b4004a..12fb71d7ed 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sdk.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sdk.js @@ -5,7 +5,7 @@ 'use strict'; -const requireHook = require('../../../../../util/requireHook'); +const hook = require('../../../../../util/hook'); const constants = require('../../../../constants'); /** @@ -14,7 +14,7 @@ const constants = require('../../../../constants'); * (see https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html). */ exports.init = function init() { - requireHook.onFileLoad(/\/aws-sdk\/lib\/signers\/v4.js/, addInstanaHeadersToUnsignableHeaders); + hook.onFileLoad(/\/aws-sdk\/lib\/signers\/v4.js/, addInstanaHeadersToUnsignableHeaders); }; function addInstanaHeadersToUnsignableHeaders(v4SignerModule) { diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js index a906e8b768..c15455bd65 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js @@ -15,7 +15,7 @@ const { readTracingAttributes } = require('../aws_utils'); const { ENTRY, EXIT, sqsAttributeNames } = require('../../../../constants'); -const requireHook = require('../../../../../util/requireHook'); +const hook = require('../../../../../util/hook'); const tracingUtil = require('../../../../tracingUtil'); // Available call types to be sent into span.data.sqs.type @@ -40,8 +40,8 @@ let logger = require('../../../../../logger').getLogger('tracing/sqs/v2', newLog let isActive = false; exports.init = function init() { - requireHook.onModuleLoad('aws-sdk', instrumentSQS); - requireHook.onModuleLoad('sqs-consumer', instrumentSQSConsumer); + hook.onModuleLoad('aws-sdk', instrumentSQS); + hook.onModuleLoad('sqs-consumer', instrumentSQSConsumer); }; function instrumentSQS(AWS) { diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/index.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/index.js index d8441f0a33..540e6b2bb0 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/index.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/index.js @@ -6,7 +6,7 @@ 'use strict'; const shimmer = require('../../../../shimmer'); -const requireHook = require('../../../../../util/requireHook'); +const hook = require('../../../../../util/hook'); const { getFunctionArguments } = require('../../../../../util/function_arguments'); /** @type {Array. */ @@ -37,23 +37,23 @@ exports.init = function init() { sqsConsumer.init(); // NOTE: each aws product can have it's own init fn to wrap or unwrap specific functions - awsProducts.forEach(awsProduct => awsProduct.init && awsProduct.init(requireHook, shimmer)); + awsProducts.forEach(awsProduct => awsProduct.init && awsProduct.init(hook, shimmer)); /** * @aws-sdk/smithly-client >= 3.36.0 changed how the dist structure gets delivered * https://github.com/aws/aws-sdk-js-v3/blob/main/packages/smithy-client/CHANGELOG.md#3360-2021-10-08 * @aws-sdk/smithly-client is a subdependency of any @aws-sdk/* package */ - requireHook.onFileLoad(/@aws-sdk\/smithy-client\/dist-cjs\/client\.js/, instrumentGlobalSmithy); + hook.onFileLoad(/@aws-sdk\/smithy-client\/dist-cjs\/client\.js/, instrumentGlobalSmithy); /** * @aws-sdk/smithly-client < 3.36.0 */ - requireHook.onFileLoad(/@aws-sdk\/smithy-client\/dist\/cjs\/client\.js/, instrumentGlobalSmithy); + hook.onFileLoad(/@aws-sdk\/smithy-client\/dist\/cjs\/client\.js/, instrumentGlobalSmithy); /** * @aws-sdk/smithly-client > 3.36.0 */ - requireHook.onModuleLoad('@smithy/smithy-client', instrumentGlobalSmithy); + hook.onModuleLoad('@smithy/smithy-client', instrumentGlobalSmithy); }; exports.isActive = function () { diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js index 26562c4d40..28c3ce826d 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js @@ -5,11 +5,11 @@ 'use strict'; const shimmer = require('../../../../shimmer'); -const requireHook = require('../../../../../util/requireHook'); +const hook = require('../../../../../util/hook'); const cls = require('../../../../cls'); function init() { - requireHook.onModuleLoad('sqs-consumer', instrument); + hook.onModuleLoad('sqs-consumer', instrument); } function instrument(SQSConsumer) { diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs.js index 3aefd4c67a..62c2679cc5 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs.js @@ -39,11 +39,11 @@ const operations = Object.keys(operationsInfo); const SPAN_NAME = 'sqs'; class InstanaAWSSQS extends InstanaAWSProduct { - init(requireHook, shimmer) { + init(hook, shimmer) { // < 3.481.0 // refs https://github.com/instana/nodejs/commit/6ae90e74fee5c47cc4ade67d21c4885d34c08847 // Background: sqs.receiveMessage returned a different promise than smithy.send (which is called internally) - requireHook.onFileLoad(/@aws-sdk\/client-sqs\/dist-cjs\/SQS\.js/, function (module) { + hook.onFileLoad(/@aws-sdk\/client-sqs\/dist-cjs\/SQS\.js/, function (module) { shimmer.wrap(module.SQS.prototype, 'receiveMessage', function (originalReceiveMsgFn) { return function instanaReceiveMessage() { return cls.ns.runAndReturn(() => { @@ -68,7 +68,7 @@ class InstanaAWSSQS extends InstanaAWSProduct { // >= 3.481 // https://github.com/aws/aws-sdk-js-v3/pull/5604 - requireHook.onFileLoad(/@aws-sdk\/client-sqs\/dist-cjs\/index\.js/, function (module) { + hook.onFileLoad(/@aws-sdk\/client-sqs\/dist-cjs\/index\.js/, function (module) { shimmer.wrap(module.SQS.prototype, 'receiveMessage', function (originalReceiveMsgFn) { return function instanaReceiveMessage() { return cls.ns.runAndReturn(() => { diff --git a/packages/core/src/tracing/instrumentation/cloud/azure/blob.js b/packages/core/src/tracing/instrumentation/cloud/azure/blob.js index d6c6f9e6eb..f7c4eb1e4d 100644 --- a/packages/core/src/tracing/instrumentation/cloud/azure/blob.js +++ b/packages/core/src/tracing/instrumentation/cloud/azure/blob.js @@ -6,7 +6,7 @@ const shimmer = require('../../../shimmer'); -const requireHook = require('../../../../util/requireHook'); +const hook = require('../../../../util/hook'); const tracingUtil = require('../../../tracingUtil'); const constants = require('../../../constants'); const cls = require('../../../cls'); @@ -16,7 +16,7 @@ let isActive = false; exports.spanName = 'azstorage'; exports.init = function init() { - requireHook.onModuleLoad('@azure/storage-blob', instrumentBlob); + hook.onModuleLoad('@azure/storage-blob', instrumentBlob); }; function instrumentBlob(blob) { diff --git a/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js b/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js index babe4933f7..dec7d195ce 100644 --- a/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js +++ b/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js @@ -15,7 +15,7 @@ const { ENTRY, EXIT } = require('../../../constants'); -const requireHook = require('../../../../util/requireHook'); +const hook = require('../../../../util/hook'); const tracingUtil = require('../../../tracingUtil'); let logger; @@ -28,8 +28,8 @@ const subscriptionRegex = /^projects\/([^/]+)\/subscriptions\/(.+)$/; let isActive = false; exports.init = function init() { - requireHook.onFileLoad(/\/@google-cloud\/pubsub\/build\/src\/publisher\/index.js/, instrumentPublisher); - requireHook.onFileLoad(/\/@google-cloud\/pubsub\/build\/src\/subscriber.js/, instrumentSubscriber); + hook.onFileLoad(/\/@google-cloud\/pubsub\/build\/src\/publisher\/index.js/, instrumentPublisher); + hook.onFileLoad(/\/@google-cloud\/pubsub\/build\/src\/subscriber.js/, instrumentSubscriber); }; function instrumentPublisher(publisher) { diff --git a/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js b/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js index 050a0641f8..178cd8f3bf 100644 --- a/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js +++ b/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js @@ -10,13 +10,13 @@ const shimmer = require('../../../shimmer'); const cls = require('../../../cls'); const constants = require('../../../constants'); -const requireHook = require('../../../../util/requireHook'); +const hook = require('../../../../util/hook'); const tracingUtil = require('../../../tracingUtil'); let isActive = false; exports.init = function init() { - requireHook.onModuleLoad('@google-cloud/storage', instrument); + hook.onModuleLoad('@google-cloud/storage', instrument); }; const storageInstrumentations = [ diff --git a/packages/core/src/tracing/instrumentation/control_flow/bluebird.js b/packages/core/src/tracing/instrumentation/control_flow/bluebird.js index 43a289af6c..2c01d3c474 100644 --- a/packages/core/src/tracing/instrumentation/control_flow/bluebird.js +++ b/packages/core/src/tracing/instrumentation/control_flow/bluebird.js @@ -6,7 +6,7 @@ 'use strict'; const instrument = require('cls-bluebird'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const cls = require('../../cls'); exports.activate = function activate() { @@ -18,7 +18,7 @@ exports.deactivate = function deactivate() { }; exports.init = function init() { - requireHook.onModuleLoad('bluebird', patchBluebird); + hook.onModuleLoad('bluebird', patchBluebird); }; function patchBluebird(bluebirdModule) { diff --git a/packages/core/src/tracing/instrumentation/control_flow/clsHooked.js b/packages/core/src/tracing/instrumentation/control_flow/clsHooked.js index e89cecdf0d..7e5da7eb5d 100644 --- a/packages/core/src/tracing/instrumentation/control_flow/clsHooked.js +++ b/packages/core/src/tracing/instrumentation/control_flow/clsHooked.js @@ -7,12 +7,12 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); let hasBeenPatched = false; exports.init = () => { - requireHook.onModuleLoad('cls-hooked', patchClsHooked); + hook.onModuleLoad('cls-hooked', patchClsHooked); }; // This module applies a patch to the cls-hooked module (https://github.com/Jeff-Lewis/cls-hooked/). This patch fixes a diff --git a/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js b/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js index dd2b7a079f..0182b1951e 100644 --- a/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js +++ b/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js @@ -7,7 +7,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const cls = require('../../cls'); let isActive = false; @@ -15,8 +15,8 @@ let isActive = false; const CLS_CONTEXT_SYMBOL = Symbol('_instana_cls_context'); exports.init = () => { - requireHook.onModuleLoad('graphql-subscriptions', instrumentModule); - requireHook.onFileLoad(/\/graphql-subscriptions\/dist\/pubsub-async-iterator\.js/, instrumentAsyncIterator); + hook.onModuleLoad('graphql-subscriptions', instrumentModule); + hook.onFileLoad(/\/graphql-subscriptions\/dist\/pubsub-async-iterator\.js/, instrumentAsyncIterator); }; function instrumentModule(graphQlSubscriptions) { diff --git a/packages/core/src/tracing/instrumentation/database/couchbase.js b/packages/core/src/tracing/instrumentation/database/couchbase.js index d725e01d93..8817a52962 100644 --- a/packages/core/src/tracing/instrumentation/database/couchbase.js +++ b/packages/core/src/tracing/instrumentation/database/couchbase.js @@ -5,7 +5,7 @@ 'use strict'; const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -24,7 +24,7 @@ exports.deactivate = function deactivate() { }; exports.init = function init() { - requireHook.onModuleLoad('couchbase', instrument); + hook.onModuleLoad('couchbase', instrument); // The couchbase client talks to some Couchbase services via http directly from the JS implementation. // e.g. search service diff --git a/packages/core/src/tracing/instrumentation/database/db2.js b/packages/core/src/tracing/instrumentation/database/db2.js index 7dd1507158..731b1e7d21 100644 --- a/packages/core/src/tracing/instrumentation/database/db2.js +++ b/packages/core/src/tracing/instrumentation/database/db2.js @@ -6,7 +6,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -18,7 +18,7 @@ const CLOSE_TIMEOUT_IN_MS = process.env.DB2_CLOSE_TIMEOUT_IN_MS || 1000 * 30; exports.spanName = 'ibmdb2'; exports.init = function init() { - requireHook.onModuleLoad('ibm_db', instrument); + hook.onModuleLoad('ibm_db', instrument); }; /** diff --git a/packages/core/src/tracing/instrumentation/database/elasticsearch.js b/packages/core/src/tracing/instrumentation/database/elasticsearch.js index 22d7df00b8..29e24f17ee 100644 --- a/packages/core/src/tracing/instrumentation/database/elasticsearch.js +++ b/packages/core/src/tracing/instrumentation/database/elasticsearch.js @@ -8,7 +8,7 @@ const url = require('url'); const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -27,7 +27,7 @@ exports.spanName = 'elasticsearch'; exports.batchable = true; exports.init = function init() { - requireHook.onModuleLoad('@elastic/elasticsearch', instrument); + hook.onModuleLoad('@elastic/elasticsearch', instrument); }; const connectionUrlCache = {}; diff --git a/packages/core/src/tracing/instrumentation/database/ioredis.js b/packages/core/src/tracing/instrumentation/database/ioredis.js index eb6fad0888..7262f38c55 100644 --- a/packages/core/src/tracing/instrumentation/database/ioredis.js +++ b/packages/core/src/tracing/instrumentation/database/ioredis.js @@ -7,7 +7,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -26,7 +26,7 @@ exports.deactivate = function deactivate() { }; exports.init = function init() { - requireHook.onModuleLoad('ioredis', instrument); + hook.onModuleLoad('ioredis', instrument); }; function instrument(ioredis) { diff --git a/packages/core/src/tracing/instrumentation/database/memcached.js b/packages/core/src/tracing/instrumentation/database/memcached.js index be3642d240..2a356716cb 100644 --- a/packages/core/src/tracing/instrumentation/database/memcached.js +++ b/packages/core/src/tracing/instrumentation/database/memcached.js @@ -10,7 +10,7 @@ const { EXIT } = require('../../constants'); const tracingUtil = require('../../tracingUtil'); const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const { getFunctionArguments } = require('../../../util/function_arguments'); const operationsInfo = { @@ -39,7 +39,7 @@ exports.isActive = function () { }; exports.init = function init() { - requireHook.onModuleLoad('memcached', instrumentMemcached); + hook.onModuleLoad('memcached', instrumentMemcached); }; exports.activate = function activate() { diff --git a/packages/core/src/tracing/instrumentation/database/mongodb.js b/packages/core/src/tracing/instrumentation/database/mongodb.js index 9a4bb22897..6ec503a56b 100644 --- a/packages/core/src/tracing/instrumentation/database/mongodb.js +++ b/packages/core/src/tracing/instrumentation/database/mongodb.js @@ -7,7 +7,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -34,11 +34,11 @@ exports.batchable = true; exports.init = function init() { // unified topology layer - requireHook.onFileLoad(/\/mongodb\/lib\/cmap\/connection\.js/, instrumentCmapConnection); + hook.onFileLoad(/\/mongodb\/lib\/cmap\/connection\.js/, instrumentCmapConnection); // mongodb >= 3.3.x, legacy topology layer - requireHook.onFileLoad(/\/mongodb\/lib\/core\/connection\/pool\.js/, instrumentLegacyTopologyPool); + hook.onFileLoad(/\/mongodb\/lib\/core\/connection\/pool\.js/, instrumentLegacyTopologyPool); // mongodb < 3.3.x, legacy topology layer - requireHook.onFileLoad(/\/mongodb-core\/lib\/connection\/pool\.js/, instrumentLegacyTopologyPool); + hook.onFileLoad(/\/mongodb-core\/lib\/connection\/pool\.js/, instrumentLegacyTopologyPool); }; function instrumentCmapConnection(connection) { diff --git a/packages/core/src/tracing/instrumentation/database/mongoose.js b/packages/core/src/tracing/instrumentation/database/mongoose.js index f68d129531..978abef482 100644 --- a/packages/core/src/tracing/instrumentation/database/mongoose.js +++ b/packages/core/src/tracing/instrumentation/database/mongoose.js @@ -12,11 +12,11 @@ logger = require('../../../logger').getLogger('tracing/mongoose', newLogger => { logger = newLogger; }); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const cls = require('../../cls'); exports.init = function () { - requireHook.onModuleLoad('mongoose', exports.instrument); + hook.onModuleLoad('mongoose', exports.instrument); }; // This instruments the Aggregate object exported by Mongoose. The Mongoose library uses the standard MongoDB driver diff --git a/packages/core/src/tracing/instrumentation/database/mssql.js b/packages/core/src/tracing/instrumentation/database/mssql.js index c668461ab0..a9ae2690f6 100644 --- a/packages/core/src/tracing/instrumentation/database/mssql.js +++ b/packages/core/src/tracing/instrumentation/database/mssql.js @@ -5,7 +5,7 @@ 'use strict'; -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -17,7 +17,7 @@ exports.spanName = 'mssql'; exports.batchable = true; exports.init = function init() { - requireHook.onModuleLoad('mssql', instrumentMssql); + hook.onModuleLoad('mssql', instrumentMssql); }; function instrumentMssql(mssql) { diff --git a/packages/core/src/tracing/instrumentation/database/mysql.js b/packages/core/src/tracing/instrumentation/database/mysql.js index 2507fc1dea..9bac2e00d4 100644 --- a/packages/core/src/tracing/instrumentation/database/mysql.js +++ b/packages/core/src/tracing/instrumentation/database/mysql.js @@ -7,7 +7,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -18,9 +18,9 @@ exports.spanName = 'mysql'; exports.batchable = true; exports.init = function init() { - requireHook.onModuleLoad('mysql', instrumentMysql); - requireHook.onModuleLoad('mysql2', instrumentMysql2); - requireHook.onModuleLoad('mysql2/promise', instrumentMysql2WithPromises); + hook.onModuleLoad('mysql', instrumentMysql); + hook.onModuleLoad('mysql2', instrumentMysql2); + hook.onModuleLoad('mysql2/promise', instrumentMysql2WithPromises); }; function instrumentMysql(mysql) { diff --git a/packages/core/src/tracing/instrumentation/database/pg.js b/packages/core/src/tracing/instrumentation/database/pg.js index 6792674a31..c22ee66966 100644 --- a/packages/core/src/tracing/instrumentation/database/pg.js +++ b/packages/core/src/tracing/instrumentation/database/pg.js @@ -7,7 +7,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -18,7 +18,7 @@ exports.spanName = 'postgres'; exports.batchable = true; exports.init = function init() { - requireHook.onModuleLoad('pg', instrumentPg); + hook.onModuleLoad('pg', instrumentPg); }; function instrumentPg(pg) { diff --git a/packages/core/src/tracing/instrumentation/database/pgNative.js b/packages/core/src/tracing/instrumentation/database/pgNative.js index d3ffc44468..31cfec1a70 100644 --- a/packages/core/src/tracing/instrumentation/database/pgNative.js +++ b/packages/core/src/tracing/instrumentation/database/pgNative.js @@ -8,7 +8,7 @@ const { LRUCache } = require('lru-cache'); const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -28,7 +28,7 @@ exports.spanName = 'postgres'; exports.batchable = true; exports.init = function init() { - requireHook.onModuleLoad('pg-native', instrumentPgNative); + hook.onModuleLoad('pg-native', instrumentPgNative); }; function instrumentPgNative(Client) { diff --git a/packages/core/src/tracing/instrumentation/database/prisma.js b/packages/core/src/tracing/instrumentation/database/prisma.js index d119dd00ba..c8a7fdc481 100644 --- a/packages/core/src/tracing/instrumentation/database/prisma.js +++ b/packages/core/src/tracing/instrumentation/database/prisma.js @@ -11,7 +11,7 @@ logger = require('../../../logger').getLogger('tracing/prisma', newLogger => { logger = newLogger; }); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const { getErrorDetails, getStackTrace } = require('../../tracingUtil'); const { EXIT } = require('../../constants'); const cls = require('../../cls'); @@ -21,7 +21,7 @@ let isActive = false; const providerAndDataSourceUriMap = new WeakMap(); exports.init = function init() { - requireHook.onModuleLoad('@prisma/client', instrumentPrismaClient); + hook.onModuleLoad('@prisma/client', instrumentPrismaClient); }; function instrumentPrismaClient(prismaClientModule) { diff --git a/packages/core/src/tracing/instrumentation/database/redis.js b/packages/core/src/tracing/instrumentation/database/redis.js index 37ad207c03..31c42bbdf7 100644 --- a/packages/core/src/tracing/instrumentation/database/redis.js +++ b/packages/core/src/tracing/instrumentation/database/redis.js @@ -7,7 +7,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -27,8 +27,8 @@ exports.deactivate = function deactivate() { exports.init = function init() { // v4 commands, "redis-commands" is outdated and no longer compatible with it - requireHook.onFileLoad(/\/@redis\/client\/dist\/lib\/cluster\/commands.js/, captureCommands); - requireHook.onModuleLoad('redis', instrument); + hook.onFileLoad(/\/@redis\/client\/dist\/lib\/cluster\/commands.js/, captureCommands); + hook.onModuleLoad('redis', instrument); }; let redisCommandList = []; diff --git a/packages/core/src/tracing/instrumentation/frameworks/express.js b/packages/core/src/tracing/instrumentation/frameworks/express.js index f737d7ca86..a69da20303 100644 --- a/packages/core/src/tracing/instrumentation/frameworks/express.js +++ b/packages/core/src/tracing/instrumentation/frameworks/express.js @@ -8,7 +8,7 @@ const shimmer = require('../../shimmer'); const methods = require('methods'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const httpServer = require('../protocols/httpServer'); const cls = require('../../cls'); @@ -24,7 +24,7 @@ exports.deactivate = function deactivate() { }; exports.init = function init() { - requireHook.onModuleLoad('express', instrument); + hook.onModuleLoad('express', instrument); }; function instrument(express) { diff --git a/packages/core/src/tracing/instrumentation/frameworks/fastify.js b/packages/core/src/tracing/instrumentation/frameworks/fastify.js index 2356b98e2f..dd2425cd88 100644 --- a/packages/core/src/tracing/instrumentation/frameworks/fastify.js +++ b/packages/core/src/tracing/instrumentation/frameworks/fastify.js @@ -5,7 +5,7 @@ 'use strict'; -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const httpServer = require('../protocols/httpServer'); const cls = require('../../cls'); let logger = require('../../../logger').getLogger('tracing/fastify', newLogger => { @@ -23,7 +23,7 @@ exports.deactivate = function deactivate() { }; exports.init = function init() { - requireHook.onModuleLoad('fastify', instrument); + hook.onModuleLoad('fastify', instrument); }; /** diff --git a/packages/core/src/tracing/instrumentation/frameworks/hapi.js b/packages/core/src/tracing/instrumentation/frameworks/hapi.js index 64eb8978fe..be4139daae 100644 --- a/packages/core/src/tracing/instrumentation/frameworks/hapi.js +++ b/packages/core/src/tracing/instrumentation/frameworks/hapi.js @@ -7,7 +7,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const httpServer = require('../protocols/httpServer'); const cls = require('../../cls'); @@ -19,7 +19,7 @@ logger = require('../../../logger').getLogger('tracing/hapi', newLogger => { let isActive = false; exports.init = function init() { - requireHook.onModuleLoad('@hapi/call', instrumentHapiCall); + hook.onModuleLoad('@hapi/call', instrumentHapiCall); }; function instrumentHapiCall(hapiCall) { diff --git a/packages/core/src/tracing/instrumentation/frameworks/koa.js b/packages/core/src/tracing/instrumentation/frameworks/koa.js index 639d517ca5..9da4d6f78d 100644 --- a/packages/core/src/tracing/instrumentation/frameworks/koa.js +++ b/packages/core/src/tracing/instrumentation/frameworks/koa.js @@ -7,7 +7,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const httpServer = require('../protocols/httpServer'); const cls = require('../../cls'); @@ -22,7 +22,7 @@ exports.deactivate = function deactivate() { }; exports.init = function init() { - requireHook.onModuleLoad('koa-router', instrumentRouter); + hook.onModuleLoad('koa-router', instrumentRouter); }; function instrumentRouter(Router) { diff --git a/packages/core/src/tracing/instrumentation/loggers/bunyan.js b/packages/core/src/tracing/instrumentation/loggers/bunyan.js index 555f3ff315..95ac7fba14 100644 --- a/packages/core/src/tracing/instrumentation/loggers/bunyan.js +++ b/packages/core/src/tracing/instrumentation/loggers/bunyan.js @@ -9,7 +9,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -17,7 +17,7 @@ const cls = require('../../cls'); let isActive = false; exports.init = function init() { - requireHook.onModuleLoad('bunyan', instrument); + hook.onModuleLoad('bunyan', instrument); }; function instrument(Logger) { diff --git a/packages/core/src/tracing/instrumentation/loggers/log4js.js b/packages/core/src/tracing/instrumentation/loggers/log4js.js index 8a9ed5bd4d..b2a61660f8 100644 --- a/packages/core/src/tracing/instrumentation/loggers/log4js.js +++ b/packages/core/src/tracing/instrumentation/loggers/log4js.js @@ -8,7 +8,7 @@ const util = require('util'); const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -18,8 +18,8 @@ let isActive = false; let levels; exports.init = function init() { - requireHook.onFileLoad(/\/log4js\/lib\/levels\.js/, saveLevelsRef); - requireHook.onFileLoad(/\/log4js\/lib\/logger\.js/, instrumentLog4jsLogger); + hook.onFileLoad(/\/log4js\/lib\/levels\.js/, saveLevelsRef); + hook.onFileLoad(/\/log4js\/lib\/logger\.js/, instrumentLog4jsLogger); }; function saveLevelsRef(levelsModule) { diff --git a/packages/core/src/tracing/instrumentation/loggers/pino.js b/packages/core/src/tracing/instrumentation/loggers/pino.js index d4a89971ab..a3dd810b31 100644 --- a/packages/core/src/tracing/instrumentation/loggers/pino.js +++ b/packages/core/src/tracing/instrumentation/loggers/pino.js @@ -10,7 +10,7 @@ const { inspect } = require('util'); const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -18,7 +18,7 @@ const cls = require('../../cls'); let isActive = false; exports.init = function init() { - requireHook.onFileLoad(/\/pino\/lib\/tools\.js/, instrumentPinoTools); + hook.onFileLoad(/\/pino\/lib\/tools\.js/, instrumentPinoTools); }; function instrumentPinoTools(toolsModule) { diff --git a/packages/core/src/tracing/instrumentation/loggers/winston.js b/packages/core/src/tracing/instrumentation/loggers/winston.js index 138fc46711..f8c131ec15 100644 --- a/packages/core/src/tracing/instrumentation/loggers/winston.js +++ b/packages/core/src/tracing/instrumentation/loggers/winston.js @@ -5,7 +5,7 @@ 'use strict'; -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -14,9 +14,9 @@ let isActive = false; exports.init = function init() { // Winston 2.x - requireHook.onFileLoad(/\/winston\/lib\/winston\/logger\.js/, instrumentWinston2); + hook.onFileLoad(/\/winston\/lib\/winston\/logger\.js/, instrumentWinston2); // Winston >= 3.x - requireHook.onFileLoad(/\/winston\/lib\/winston\/create-logger\.js/, instrumentWinston3); + hook.onFileLoad(/\/winston\/lib\/winston\/create-logger\.js/, instrumentWinston3); }; function instrumentWinston2(loggerModule) { diff --git a/packages/core/src/tracing/instrumentation/messaging/amqp.js b/packages/core/src/tracing/instrumentation/messaging/amqp.js index 13736c923b..40e1cbf6ed 100644 --- a/packages/core/src/tracing/instrumentation/messaging/amqp.js +++ b/packages/core/src/tracing/instrumentation/messaging/amqp.js @@ -7,7 +7,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -20,9 +20,9 @@ logger = require('../../../logger').getLogger('tracing/amqp', newLogger => { let isActive = false; exports.init = function init() { - requireHook.onFileLoad(/\/amqplib\/lib\/channel\.js/, instrumentChannel); - requireHook.onFileLoad(/\/amqplib\/lib\/channel_model\.js/, instrumentChannelModel); - requireHook.onFileLoad(/\/amqplib\/lib\/callback_model\.js/, instrumentCallbackModel); + hook.onFileLoad(/\/amqplib\/lib\/channel\.js/, instrumentChannel); + hook.onFileLoad(/\/amqplib\/lib\/channel_model\.js/, instrumentChannelModel); + hook.onFileLoad(/\/amqplib\/lib\/callback_model\.js/, instrumentCallbackModel); }; function instrumentChannel(channelModule) { diff --git a/packages/core/src/tracing/instrumentation/messaging/bull.js b/packages/core/src/tracing/instrumentation/messaging/bull.js index f70c6a1714..dd37c62d0f 100644 --- a/packages/core/src/tracing/instrumentation/messaging/bull.js +++ b/packages/core/src/tracing/instrumentation/messaging/bull.js @@ -8,7 +8,7 @@ const shimmer = require('../../shimmer'); const cls = require('../../cls'); const { ENTRY, EXIT } = require('../../constants'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const { getFunctionArguments } = require('../../../util/function_arguments'); @@ -21,7 +21,7 @@ let isActive = false; exports.spanName = 'bull'; exports.init = function init() { - requireHook.onModuleLoad('bull', instrumentBull); + hook.onModuleLoad('bull', instrumentBull); }; function instrumentBull(Bull) { diff --git a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js index a783dd6523..d511b4c9bb 100644 --- a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js +++ b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js @@ -5,7 +5,7 @@ 'use strict'; -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const { limitTraceId } = require('../../tracingHeaders'); const leftPad = require('../../leftPad'); @@ -23,8 +23,8 @@ let headerFormat = constants.kafkaHeaderFormatDefault; let isActive = false; exports.init = function init(config) { - requireHook.onFileLoad(/\/kafkajs\/src\/producer\/messageProducer\.js/, instrumentProducer); - requireHook.onFileLoad(/\/kafkajs\/src\/consumer\/runner\.js/, instrumentConsumer); + hook.onFileLoad(/\/kafkajs\/src\/producer\/messageProducer\.js/, instrumentProducer); + hook.onFileLoad(/\/kafkajs\/src\/consumer\/runner\.js/, instrumentConsumer); traceCorrelationEnabled = config.tracing.kafka.traceCorrelation; headerFormat = config.tracing.kafka.headerFormat; }; diff --git a/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js b/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js index 201dc3b967..9449008eab 100644 --- a/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js +++ b/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js @@ -7,7 +7,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -22,7 +22,7 @@ let isActive = false; // FYI: officially deprecated. No release since 4 years. But still very // high usage on npm trends. We will drop in 4.x v4. exports.init = function init() { - requireHook.onModuleLoad('kafka-node', instrument); + hook.onModuleLoad('kafka-node', instrument); }; function instrument(kafka) { diff --git a/packages/core/src/tracing/instrumentation/messaging/nats.js b/packages/core/src/tracing/instrumentation/messaging/nats.js index 37dc5bf46d..a1f8eb3876 100644 --- a/packages/core/src/tracing/instrumentation/messaging/nats.js +++ b/packages/core/src/tracing/instrumentation/messaging/nats.js @@ -7,7 +7,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -17,7 +17,7 @@ let clientHasBeenInstrumentedV1 = false; let clientHasBeenInstrumentedV2 = false; exports.init = function init() { - requireHook.onModuleLoad('nats', instrumentNats); + hook.onModuleLoad('nats', instrumentNats); }; let natsModule; diff --git a/packages/core/src/tracing/instrumentation/messaging/natsStreaming.js b/packages/core/src/tracing/instrumentation/messaging/natsStreaming.js index d60ef077bb..2d915eeb83 100644 --- a/packages/core/src/tracing/instrumentation/messaging/natsStreaming.js +++ b/packages/core/src/tracing/instrumentation/messaging/natsStreaming.js @@ -10,7 +10,7 @@ const shimmer = require('../../shimmer'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -19,7 +19,7 @@ let isActive = false; let clientHasBeenInstrumented = false; exports.init = function init() { - requireHook.onModuleLoad('node-nats-streaming', instrumentNatsStreaming); + hook.onModuleLoad('node-nats-streaming', instrumentNatsStreaming); }; function instrumentNatsStreaming(natsStreamingModule) { diff --git a/packages/core/src/tracing/instrumentation/messaging/rdkafka.js b/packages/core/src/tracing/instrumentation/messaging/rdkafka.js index 3e340e05d4..682cbd8b1a 100644 --- a/packages/core/src/tracing/instrumentation/messaging/rdkafka.js +++ b/packages/core/src/tracing/instrumentation/messaging/rdkafka.js @@ -4,7 +4,7 @@ 'use strict'; -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const { limitTraceId } = require('../../tracingHeaders'); const leftPad = require('../../leftPad'); @@ -23,9 +23,9 @@ logger = require('../../../logger').getLogger('tracing/rdkafka', newLogger => { let isActive = false; exports.init = function init(config) { - requireHook.onFileLoad(/\/node-rdkafka\/lib\/producer\.js/, instrumentProducer); - requireHook.onFileLoad(/\/node-rdkafka\/lib\/kafka-consumer-stream\.js/, instrumentConsumerAsStream); - requireHook.onModuleLoad('node-rdkafka', instrumentConsumer); + hook.onFileLoad(/\/node-rdkafka\/lib\/producer\.js/, instrumentProducer); + hook.onFileLoad(/\/node-rdkafka\/lib\/kafka-consumer-stream\.js/, instrumentConsumerAsStream); + hook.onModuleLoad('node-rdkafka', instrumentConsumer); traceCorrelationEnabled = config.tracing.kafka.traceCorrelation; configHeader = config.tracing.kafka.headerFormat; diff --git a/packages/core/src/tracing/instrumentation/process/memored.js b/packages/core/src/tracing/instrumentation/process/memored.js index 3494caf55c..9c473dd8b6 100644 --- a/packages/core/src/tracing/instrumentation/process/memored.js +++ b/packages/core/src/tracing/instrumentation/process/memored.js @@ -5,13 +5,13 @@ 'use strict'; -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const cls = require('../../cls'); let isActive = false; exports.init = function init() { - requireHook.onFileLoad(/\/memored\/index.js/, instrumentMemored); + hook.onFileLoad(/\/memored\/index.js/, instrumentMemored); }; // This instruments a dependency of edgemicro that usually lives in diff --git a/packages/core/src/tracing/instrumentation/protocols/graphql.js b/packages/core/src/tracing/instrumentation/protocols/graphql.js index 2ac3707c99..9bcf292490 100644 --- a/packages/core/src/tracing/instrumentation/protocols/graphql.js +++ b/packages/core/src/tracing/instrumentation/protocols/graphql.js @@ -12,7 +12,7 @@ logger = require('../../../logger').getLogger('tracing/graphql', newLogger => { logger = newLogger; }); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -27,9 +27,9 @@ const operationTypes = [queryOperationType, mutationOperationType, subscriptionO const subscriptionUpdate = 'subscription-update'; exports.init = function init() { - requireHook.onFileLoad(/\/graphql\/execution\/execute.js/, instrumentExecute); - requireHook.onFileLoad(/\/@apollo\/gateway\/dist\/executeQueryPlan.js/, instrumentApolloGatewayExecuteQueryPlan); - requireHook.onModuleLoad('@apollo/federation', logDeprecatedWarning); + hook.onFileLoad(/\/graphql\/execution\/execute.js/, instrumentExecute); + hook.onFileLoad(/\/@apollo\/gateway\/dist\/executeQueryPlan.js/, instrumentApolloGatewayExecuteQueryPlan); + hook.onModuleLoad('@apollo/federation', logDeprecatedWarning); }; function instrumentExecute(executeModule) { diff --git a/packages/core/src/tracing/instrumentation/protocols/grpcJs.js b/packages/core/src/tracing/instrumentation/protocols/grpcJs.js index 6cf06deeb9..42f001dcd9 100644 --- a/packages/core/src/tracing/instrumentation/protocols/grpcJs.js +++ b/packages/core/src/tracing/instrumentation/protocols/grpcJs.js @@ -11,7 +11,7 @@ logger = require('../../../logger').getLogger('tracing/grpcjs', newLogger => { logger = newLogger; }); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const tracingUtil = require('../../tracingUtil'); const constants = require('../../constants'); const cls = require('../../cls'); @@ -31,9 +31,9 @@ const TYPES_WITH_CALLBACK = [TYPES.UNARY, TYPES.CLIENT_STREAM]; const TYPES_WITH_CALL_END = [TYPES.SERVER_STREAM, TYPES.BIDI]; exports.init = function () { - requireHook.onModuleLoad('@grpc/grpc-js', instrumentModule); - requireHook.onFileLoad(/\/@grpc\/grpc-js\/build\/src\/server\.js/, instrumentServer); - requireHook.onFileLoad(/\/@grpc\/grpc-js\/build\/src\/client\.js/, instrumentClient); + hook.onModuleLoad('@grpc/grpc-js', instrumentModule); + hook.onFileLoad(/\/@grpc\/grpc-js\/build\/src\/server\.js/, instrumentServer); + hook.onFileLoad(/\/@grpc\/grpc-js\/build\/src\/client\.js/, instrumentClient); }; function instrumentModule(grpc) { diff --git a/packages/core/src/tracing/instrumentation/protocols/httpClient.js b/packages/core/src/tracing/instrumentation/protocols/httpClient.js index 2c498d6754..81c7a5b485 100644 --- a/packages/core/src/tracing/instrumentation/protocols/httpClient.js +++ b/packages/core/src/tracing/instrumentation/protocols/httpClient.js @@ -19,7 +19,7 @@ const { const constants = require('../../constants'); const cls = require('../../cls'); const url = require('url'); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); let logger; logger = require('../../../logger').getLogger('tracing/httpClient', newLogger => { logger = newLogger; @@ -32,7 +32,7 @@ exports.init = function init(config) { instrument(coreHttpModule, false); instrument(coreHttpsModule, true); extraHttpHeadersToCapture = config.tracing.http.extraHttpHeadersToCapture; - requireHook.onModuleLoad('request', logDeprecatedWarning); + hook.onModuleLoad('request', logDeprecatedWarning); }; function logDeprecatedWarning() { logger.warn( diff --git a/packages/core/src/tracing/instrumentation/protocols/superagent.js b/packages/core/src/tracing/instrumentation/protocols/superagent.js index 7bcdecc31d..5e1549e381 100644 --- a/packages/core/src/tracing/instrumentation/protocols/superagent.js +++ b/packages/core/src/tracing/instrumentation/protocols/superagent.js @@ -13,11 +13,11 @@ logger = require('../../../logger').getLogger('tracing/superagent', newLogger => logger = newLogger; }); -const requireHook = require('../../../util/requireHook'); +const hook = require('../../../util/hook'); const cls = require('../../cls'); exports.init = function () { - requireHook.onModuleLoad('superagent', exports.instrument); + hook.onModuleLoad('superagent', exports.instrument); }; // This instruments the Request object exported by superagent. The superagent library uses Node.js' http/https/http2 diff --git a/packages/core/src/util/applicationUnderMonitoring.js b/packages/core/src/util/applicationUnderMonitoring.js index 1f8cef8115..372e2fe331 100644 --- a/packages/core/src/util/applicationUnderMonitoring.js +++ b/packages/core/src/util/applicationUnderMonitoring.js @@ -7,6 +7,7 @@ const fs = require('../uninstrumentedFs'); const path = require('path'); +const isESMApp = require('./esm').isESMApp; /** @type {import('../logger').GenericLogger} */ let logger; @@ -166,18 +167,7 @@ function getMainPackageJsonPathStartingAtDirectory(startDirectory, cb) { if ( // @ts-ignore (process._preload_modules && process._preload_modules.length > 0) || - (process.env.NODE_OPTIONS && - (process.env.NODE_OPTIONS.indexOf('--experimental-loader') !== -1 || - process.env.NODE_OPTIONS.indexOf('--import') !== -1)) || - (process.execArgv && - process.execArgv.length > 0 && - ((process.execArgv[0].indexOf('--experimental-loader') !== -1 && - process.execArgv[0].indexOf('esm-loader.mjs')) !== -1 || - (process.execArgv[0].indexOf('--experimental-loader') !== -1 && - process.execArgv[1].indexOf('esm-loader.mjs') !== -1) || - (process.execArgv[0].indexOf('--import') !== -1 && process.execArgv[0].indexOf('esm-register.mjs')) !== - -1 || - (process.execArgv[0].indexOf('--import') !== -1 && process.execArgv[1].indexOf('esm-register.mjs') !== -1))) + isESMApp() ) { // @ts-ignore mainModule = { diff --git a/packages/core/src/tracing/esmSupportedVersion.js b/packages/core/src/util/esm.js similarity index 58% rename from packages/core/src/tracing/esmSupportedVersion.js rename to packages/core/src/util/esm.js index 8b076a1f78..5224dbe377 100644 --- a/packages/core/src/tracing/esmSupportedVersion.js +++ b/packages/core/src/util/esm.js @@ -39,3 +39,30 @@ exports.hasExperimentalLoaderFlag = function hasExperimentalLoaderFlag() { return experimentalLoaderFlagIsSet && exports.isLatestEsmSupportedVersion(process.versions.node); }; + +/** + * Checks if the application is an ECMAScript Modules (ESM) app by inspecting + * the presence of flags like --experimental-loader and --import in the environment. + * @returns {boolean} True if an ESM app is detected, false otherwise. +If the application is ESM, caching the result for efficiency. + * @type {boolean} + */ +let isESMCached = null; +exports.isESMApp = function isESMApp() { + if (isESMCached !== null) { + return isESMCached; + } + const isESM = + (process.env.NODE_OPTIONS && + (process.env.NODE_OPTIONS.includes('--experimental-loader') || process.env.NODE_OPTIONS.includes('--import'))) || + (process.execArgv && + process.execArgv.length > 0 && + ((process.execArgv[0].includes('--experimental-loader') && process.execArgv[0].includes('esm-loader.mjs')) || + (process.execArgv[0].includes('--experimental-loader') && process.execArgv[1]?.includes('esm-loader.mjs')) || + (process.execArgv[0].includes('--import') && process.execArgv[0].includes('esm-register.mjs')) || + (process.execArgv[0].includes('--import') && process.execArgv[1]?.includes('esm-register.mjs')))); + + isESMCached = isESM; + + return isESM; +}; diff --git a/packages/core/src/util/hook.js b/packages/core/src/util/hook.js new file mode 100644 index 0000000000..6a9662d25e --- /dev/null +++ b/packages/core/src/util/hook.js @@ -0,0 +1,52 @@ +/* + * (c) Copyright IBM Corp. 2024 + */ + +'use strict'; + +const iitmHook = require('./iitmHook'); +const requireHook = require('./requireHook'); +const isESMApp = require('./esm').isESMApp; + +/* + * RequireHook + * + * Provides support for all CommonJS (CJS) modules in CJS applications, + * Additionally, it extends support to all CommonJS (CJS) modules in ESM applications. + * + * Import-in-the-middle + * + * Offers support for all native ECMAScript Modules within ESM applications. + * However, it does not provide support for modules loaded from CJS applications. + * + * Note: In the next major release (4.x), we plan to transition all CJS modules in ESM applications to be + * supported with iitmHook. For now, this approach is chosen to minimize risk. + */ + +/** + * @param {string} moduleName + * @param {Function} fn + * @param {Object} [options] - Optional settings + * @param {boolean} [options.nativeEsm] - Indicates if the module is a native ECMAScript Module + */ +exports.onModuleLoad = (moduleName, fn, { nativeEsm = false } = {}) => { + // Use requireHook directly if the application is not an ESM app + if (!isESMApp()) { + requireHook.onModuleLoad(moduleName, fn); + return; + } + + // Use iitmHook if nativeEsm is true, otherwise use requireHook + if (nativeEsm) { + iitmHook.onModuleLoad(moduleName, fn); + } else { + requireHook.onModuleLoad(moduleName, fn); + } +}; +/** + * @param {RegExp} pattern + * @param {Function} fn + */ +exports.onFileLoad = (pattern, fn) => { + requireHook.onFileLoad(pattern, fn); +}; diff --git a/packages/core/src/util/iitmHook.js b/packages/core/src/util/iitmHook.js new file mode 100644 index 0000000000..dc308c909f --- /dev/null +++ b/packages/core/src/util/iitmHook.js @@ -0,0 +1,54 @@ +/* + * (c) Copyright IBM Corp. 2024 + */ + +'use strict'; + +const iitmHook = require('import-in-the-middle'); + +/** @type {import('../logger').GenericLogger} */ +let logger = require('../logger').getLogger('util/iitmHook', newLogger => { + logger = newLogger; +}); + +/** @type {Object.} */ +const byModuleNameTransformers = {}; + +/** + * Initializes the import-in-the-middle hooking. + */ +exports.init = function init() { + Object.entries(byModuleNameTransformers).forEach(([moduleName, applicableTransformers]) => { + if (applicableTransformers) { + applicableTransformers.forEach(transformerFn => { + if (typeof transformerFn === 'function') { + // @ts-ignore + iitmHook([moduleName], (exports, name) => { + logger.debug(`iitm-hooking enabled for module ${name}`); + if (exports && exports.default) { + exports.default = transformerFn(exports.default); + } else { + return transformerFn(exports); + } + return exports; + }); + } else { + logger.error( + `The transformer is not a function but of type "${typeof transformerFn}" (details: ${ + transformerFn == null ? 'null/undefined' : transformerFn + }).` + ); + } + }); + } + }); +}; + +/** + * @param {string} moduleName - The name of the module. + * @param {Function} transformFn - The transformer function. + */ +exports.onModuleLoad = function onModuleLoad(moduleName, transformFn) { + byModuleNameTransformers[moduleName] = byModuleNameTransformers[moduleName] || []; + byModuleNameTransformers[moduleName].push(transformFn); +}; diff --git a/packages/core/src/util/index.js b/packages/core/src/util/index.js index a008671a7e..baaee8b820 100644 --- a/packages/core/src/util/index.js +++ b/packages/core/src/util/index.js @@ -18,5 +18,7 @@ module.exports = { propertySizes: require('./propertySizes'), requireHook: require('./requireHook'), slidingWindow: require('./slidingWindow'), - stackTrace: require('./stackTrace') + stackTrace: require('./stackTrace'), + iitmHook: require('./iitmHook'), + hook: require('./hook') }; diff --git a/packages/core/src/util/requireHook.js b/packages/core/src/util/requireHook.js index d1b1a67447..61202c5281 100644 --- a/packages/core/src/util/requireHook.js +++ b/packages/core/src/util/requireHook.js @@ -57,8 +57,8 @@ function patchedModuleLoad(moduleName) { // CASE: when using ESM, the Node runtime passes a full path to Module._load // We aim to extract the module name to apply our instrumentation. // CASE: we ignore all file endings, which we are not interested in. Any module can load any file. - // CASE: Native ESM support is still pending, and the requireHook is not compatible with native ESM. - // However, starting from version 12, the 'got' module is transitioning to a pure ESM module but + // CASE: The requireHook is not compatible with native ESM so the native ESM is not handled here. + // Exception: Starting from version 12, the 'got' module is transitioning to a pure ESM module but // continues to function. This is because 'got' is instrumented coincidentally with the 'http' module. // The instrumentation of 'http' and 'https' works without the requireHook. // See: https://github.com/search?q=repo%3Asindresorhus%2Fgot%20from%20%27http2-wrapper%27&type=code. @@ -67,6 +67,7 @@ function patchedModuleLoad(moduleName) { // where we require 'http' and 'https' at the top. Native ESM libraries that import core Node modules // (e.g., import http from 'node:http') do not trigger Module._load, hence do not use the requireHook. // However, when an ESM library imports a CommonJS package, our requireHook is triggered. + // For native ESM libraries the iitmHook is triggered. if (path.isAbsolute(moduleName) && ['.node', '.json', '.ts'].indexOf(path.extname(moduleName)) === -1) { // EDGE CASE for ESM: mysql2/promise.js if (moduleName.indexOf('node_modules/mysql2/promise.js') !== -1) { diff --git a/packages/core/test/util/initializedTooLateHeuristic_test.js b/packages/core/test/util/initializedTooLateHeuristic_test.js index 716584c617..08c81b2ce3 100644 --- a/packages/core/test/util/initializedTooLateHeuristic_test.js +++ b/packages/core/test/util/initializedTooLateHeuristic_test.js @@ -8,7 +8,7 @@ const expect = require('chai').expect; const sinon = require('sinon'); const path = require('path'); const fs = require('fs'); -const requireHook = require('../../src/util/requireHook'); +const hook = require('../../src/util/hook'); const initializedTooLateHeurstic = require('../../src/util/initializedTooLateHeuristic'); const config = require('../config'); @@ -18,10 +18,10 @@ describe('[UNIT] util.initializedTooLateHeurstic', function () { const instrumentedModules = []; before(() => { - sinon.stub(requireHook, 'onFileLoad').callsFake(function fake(m) { + sinon.stub(hook, 'onFileLoad').callsFake(function fake(m) { instrumentedModules.push(m); }); - sinon.stub(requireHook, 'onModuleLoad').callsFake(function fake(m) { + sinon.stub(hook, 'onModuleLoad').callsFake(function fake(m) { instrumentedModules.push(m); }); diff --git a/packages/google-cloud-run/esm-loader.mjs b/packages/google-cloud-run/esm-loader.mjs index 87eb0881eb..f760b891c3 100644 --- a/packages/google-cloud-run/esm-loader.mjs +++ b/packages/google-cloud-run/esm-loader.mjs @@ -24,3 +24,6 @@ */ import './src/index.js'; +// Here we export all named exports from '@instana/core/iitm-loader.mjs', enabling +// integration of import-in-the-middle (IITM) for Native ESM module support. +export * from '@instana/core/iitm-loader.mjs'; diff --git a/packages/google-cloud-run/esm-register.mjs b/packages/google-cloud-run/esm-register.mjs index 97e5f564d2..168a8b439d 100644 --- a/packages/google-cloud-run/esm-register.mjs +++ b/packages/google-cloud-run/esm-register.mjs @@ -8,7 +8,7 @@ * see https://github.com/nodejs/node/pull/44710. * Previously, loading the Instana collector within the loader and after the update ESM support * no longer working with v18.19 and above. To address this, we've opted to load the Instana - * collector in the main thread using --import. Additionally, we aim to incorporate native ESM + * collector in the main thread using --import. Additionally, we incorporated native ESM * support by utilizing the node register method, enabling customization of the ESM loader * with 'import-in-the-middle'. * @@ -19,7 +19,8 @@ // Import the initialization module for google-cloud-run collector; it self-initializes upon import // and it should be executed in the main thread. import './src/index.js'; - -// We plan to utilize this for adding native ESM support in the near future -// import { register } from 'node:module'; -// register(./loader.mjs, import.meta.url); +import { register } from 'node:module'; +// ESM module resolution and loading are facilitated by registering `@instana/core/iitm-loader.mjs`, which exports +// import-in-the-middle(IITM) hooks. This registration can be accomplished using the register method from node:module. +// see: https://nodejs.org/api/module.html#customization-hooks +register('@instana/core/iitm-loader.mjs', import.meta.url); diff --git a/packages/shared-metrics/src/healthchecks.js b/packages/shared-metrics/src/healthchecks.js index ea751d5a52..247df9d3b0 100644 --- a/packages/shared-metrics/src/healthchecks.js +++ b/packages/shared-metrics/src/healthchecks.js @@ -5,7 +5,7 @@ 'use strict'; -const { requireHook } = require('@instana/core').util; +const { hook } = require('@instana/core').util; let logger = require('@instana/core').logger.getLogger('metrics'); @@ -30,7 +30,7 @@ exports.payloadPrefix = 'healthchecks'; // @ts-ignore exports.currentPayload = {}; -requireHook.onModuleLoad( +hook.onModuleLoad( 'admin-plugin-healthcheck', function onAdminPluginHealthcheckLoaded(/** @type {*} */ _adminPluginHealthcheck) { adminPluginHealthcheck = _adminPluginHealthcheck;