diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 721bed5b15..b56f4cb6f8 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -26,6 +26,7 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic * [https-proxy-agent](#https-proxy-agent) * [json-bigint](#json-bigint) * [json-stringify-safe](#json-stringify-safe) +* [module-details-from-path](#module-details-from-path) * [readable-stream](#readable-stream) * [semver](#semver) * [winston-transport](#winston-transport) @@ -1338,6 +1339,35 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ``` +### module-details-from-path + +This product includes source derived from [module-details-from-path](https://github.com/watson/module-details-from-path) ([v1.0.3](https://github.com/watson/module-details-from-path/tree/v1.0.3)), distributed under the [MIT License](https://github.com/watson/module-details-from-path/blob/v1.0.3/LICENSE): + +``` +The MIT License (MIT) + +Copyright (c) 2016 Thomas Watson Steen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + ### readable-stream This product includes source derived from [readable-stream](https://github.com/nodejs/readable-stream) ([v3.6.2](https://github.com/nodejs/readable-stream/tree/v3.6.2)), distributed under the [MIT License](https://github.com/nodejs/readable-stream/blob/v3.6.2/LICENSE): diff --git a/esm-loader.mjs b/esm-loader.mjs index c79c342021..9a361ef24e 100644 --- a/esm-loader.mjs +++ b/esm-loader.mjs @@ -4,7 +4,6 @@ */ import newrelic from './index.js' -import shimmer from './lib/shimmer.js' import loggingModule from './lib/logger.js' import NAMES from './lib/metrics/names.js' import semver from 'semver' @@ -59,47 +58,22 @@ export async function resolve(specifier, context, nextResolve) { * duplicating the logic of the Node.js hook */ const resolvedModule = await nextResolve(specifier, context, nextResolve) - const instrumentationName = shimmer.getInstrumentationNameFromModuleName(specifier) - const instrumentationDefinition = shimmer.registeredInstrumentations[instrumentationName] - - if (instrumentationDefinition) { - const { url, format } = resolvedModule - logger.debug(`Instrumentation exists for ${specifier} ${format} package.`) - - if (registeredSpecifiers.get(url)) { - logger.debug( - `Instrumentation already registered for ${specifier} under ${fileURLToPath( - url - )}, skipping resolve hook...` - ) - } else if (format === 'commonjs') { - // ES Modules translate import statements into fully qualified filepaths, so we create a copy of our instrumentation under this filepath - const instrumentationDefinitionCopy = [...instrumentationDefinition] - - instrumentationDefinitionCopy.forEach((copy) => { - // Stripping the prefix is necessary because the code downstream gets this url without it - copy.moduleName = fileURLToPath(url) - - // Added to keep our Supportability metrics from exploding/including customer info via full filepath - copy.specifier = specifier - shimmer.registerInstrumentation(copy) - logger.debug( - `Registered CommonJS instrumentation for ${specifier} under ${copy.moduleName}` - ) - }) - // Keep track of what we've registered so we don't double register (see: https://github.com/newrelic/node-newrelic/issues/1646) - registeredSpecifiers.set(url, specifier) - } else if (format === 'module') { - registeredSpecifiers.set(url, specifier) - const modifiedUrl = new URL(url) - // add a query param to the resolved url so the load hook below knows - // to rewrite and wrap the source code - modifiedUrl.searchParams.set('hasNrInstrumentation', 'true') - resolvedModule.url = modifiedUrl.href - } else { - logger.debug(`${specifier} is not a CommonJS nor ESM package, skipping for now.`) - } + if (registeredSpecifiers.get(resolvedModule.url)) { + logger.debug( + `Instrumentation already registered for ${specifier} under ${fileURLToPath( + resolvedModule.url + )}, skipping resolve hook...` + ) + } + + if (resolvedModule.format === 'module') { + registeredSpecifiers.set(resolvedModule.url, specifier) + const modifiedUrl = new URL(resolvedModule.url) + // add a query param to the resolved url so the load hook below knows + // to rewrite and wrap the source code + modifiedUrl.searchParams.set('hasNrInstrumentation', 'true') + resolvedModule.url = modifiedUrl.href } return resolvedModule diff --git a/lib/shimmer.js b/lib/shimmer.js index af876594bb..ef4fa2a70f 100644 --- a/lib/shimmer.js +++ b/lib/shimmer.js @@ -12,6 +12,8 @@ const logger = require('./logger').child({ component: 'shimmer' }) const INSTRUMENTATIONS = require('./instrumentations')() const properties = require('./util/properties') const shims = require('./shim') +const parse = require('module-details-from-path') +const Module = require('module') const NAMES = require('./metrics/names') const symbols = require('./symbols') @@ -287,7 +289,6 @@ const shimmer = (module.exports = { */ patchModule: function patchModule(agent) { logger.trace('Wrapping module loader.') - const Module = require('module') const filepathMap = {} shimmer.wrapMethod(Module, 'Module', '_resolveFilename', function wrapRes(resolve) { @@ -346,7 +347,6 @@ const shimmer = (module.exports = { unpatchModule: function unpatchModule() { logger.trace('Unwrapping to previous module loader.') - const Module = require('module') shimmer.unwrapMethod(Module, 'Module', '_resolveFilename') shimmer.unwrapMethod(Module, 'Module', '_load') @@ -478,6 +478,17 @@ const shimmer = (module.exports = { * @param moduleName */ getInstrumentationNameFromModuleName(moduleName) { + // import statements translate the module name into it's absolute filepath + // so figure out if we're in that scenario and the thing being imported is the main entrypoint + if (path.isAbsolute(moduleName)) { + const packageInfo = parse(moduleName) + + if (packageInfo && packageInfo.path !== 'package.json') { + console.log(packageInfo) + return packageInfo.name + } + } + // XXX When updating these special cases, also update `uninstrumented`. // To allow for instrumenting both 'pg' and 'pg.js'. if (moduleName === 'pg.js') { diff --git a/package-lock.json b/package-lock.json index cfa2ab049a..1002f28f89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "https-proxy-agent": "^5.0.0", "json-bigint": "^1.0.0", "json-stringify-safe": "^5.0.0", + "module-details-from-path": "^1.0.3", "readable-stream": "^3.6.1", "semver": "^7.5.2", "winston-transport": "^4.5.0" @@ -10953,6 +10954,11 @@ "node": ">=0.10.0" } }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, "node_modules/module-not-found-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", @@ -25034,6 +25040,11 @@ "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", "dev": true }, + "module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, "module-not-found-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", diff --git a/package.json b/package.json index 63ebe8cfd3..f71e762b9c 100644 --- a/package.json +++ b/package.json @@ -192,6 +192,7 @@ "https-proxy-agent": "^5.0.0", "json-bigint": "^1.0.0", "json-stringify-safe": "^5.0.0", + "module-details-from-path": "^1.0.3", "readable-stream": "^3.6.1", "semver": "^7.5.2", "winston-transport": "^4.5.0" diff --git a/third_party_manifest.json b/third_party_manifest.json index c4ce1b2b6c..cb62e4fb3a 100644 --- a/third_party_manifest.json +++ b/third_party_manifest.json @@ -1,5 +1,5 @@ { - "lastUpdated": "Mon Jul 24 2023 12:44:47 GMT-0400 (Eastern Daylight Time)", + "lastUpdated": "Fri Jul 28 2023 17:17:46 GMT-0400 (Eastern Daylight Time)", "projectName": "New Relic Node Agent", "projectUrl": "https://github.com/newrelic/node-newrelic", "includeOptDeps": true, @@ -186,6 +186,20 @@ "email": "i@izs.me", "url": "http://blog.izs.me" }, + "module-details-from-path@1.0.3": { + "name": "module-details-from-path", + "version": "1.0.3", + "range": "^1.0.3", + "licenses": "MIT", + "repoUrl": "https://github.com/watson/module-details-from-path", + "versionedRepoUrl": "https://github.com/watson/module-details-from-path/tree/v1.0.3", + "licenseFile": "node_modules/module-details-from-path/LICENSE", + "licenseUrl": "https://github.com/watson/module-details-from-path/blob/v1.0.3/LICENSE", + "licenseTextSource": "file", + "publisher": "Thomas Watson Steen", + "email": "w@tson.dk", + "url": "https://twitter.com/wa7son" + }, "readable-stream@3.6.2": { "name": "readable-stream", "version": "3.6.2",