newrelic 9.0.3 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/NEWS.md CHANGED
@@ -1,6 +1,24 @@
1
+ ### v9.1.0 (2022-09-22)
2
+
3
+ * Added [experimental loader](https://nodejs.org/api/esm.html#loaders) to support instrumentation of CommonJS packages in ECMAScript Module(ESM) applications.
4
+ * It only supports versions of Node.js >= `16.12.0`.
5
+ * It is subject to change due to its experimental stability.
6
+
7
+ * Enhanced supportability metrics for ESM support.
8
+ * Added new metrics to track usage of ESM loader(`Supportability/Features/ESM/Loader` and `Supportability/Features/ESM/UnsupportedLoader`).
9
+ * Updated instrumentation map to include an optional "friendly name" for tracking metrics.
10
+
11
+ * Enabled re-throwing ESM import errors of `newrelic.js` so that the user is informed to rename it to `newrelic.cjs`
12
+
13
+ * Fixed an issue with mongodb instrumentation where IPv6 address([::1]) was not getting mapped to localhost when setting the host attribute on the segment.
14
+
15
+ * Added a test ESM loader to properly mock out agent in versioned tests.
16
+
17
+ * Added ESM versioned tests for: `express`, `pg`, `mongodb`, and `@grpc/grpc-js`.
18
+
1
19
  ### v9.0.3 (2022-09-06)
2
20
 
3
- * Updated gRPC client instrumenation to respect `grpc.record_errors` when deciding to log errors on gRPC client requests.
21
+ * Updated gRPC client instrumentation to respect `grpc.record_errors` when deciding to log errors on gRPC client requests.
4
22
 
5
23
  * Fixed transaction name finalization to properly copy the appropriate transaction name to root segment.
6
24
 
package/README.md CHANGED
@@ -20,11 +20,15 @@ To use New Relic's Node.js agent entails these three steps, which are described
20
20
 
21
21
  1. To install the agent for performance monitoring, use your favorite npm-based package manager and install the `newrelic` package into your application:
22
22
 
23
- `$ npm install newrelic`
23
+ ```sh
24
+ $ npm install newrelic
25
+ ```
24
26
 
25
27
  2. Then, copy the stock configuration file to your program's base folder:
26
28
 
27
- `$ cp ./node_modules/newrelic/newrelic.js ./<your destination>`
29
+ ```sh
30
+ $ cp ./node_modules/newrelic/newrelic.js ./<your destination>
31
+ ```
28
32
 
29
33
  3. Now, add your New Relic license key and application/service name to that file:
30
34
 
@@ -58,6 +62,28 @@ If you cannot control how your program is run, you can load the `newrelic` modul
58
62
  /* ... the rest of your program ... */
59
63
  ```
60
64
 
65
+ ## ECMAScript Modules
66
+
67
+ If your application is written with `import` and `export` statements in javascript, you are using [ES Modules](https://nodejs.org/api/esm.html#modules-ecmascript-modules) and must bootstrap the agent in a different way.
68
+
69
+ The New Relic Node.js agent includes ***_experimental_*** support for ES Modules. The agent is reliant on an experimental feature in Node.js in order to appropriately register instrumentation. Until the Node.js API for [ES Module Loaders](https://nodejs.org/api/esm.html#loaders) is stable, breaking changes may occur when updating Node.js. Lastly, the ESM loader does not follow the same [supported Node.js versions](https://docs.newrelic.com/docs/apm/agents/nodejs-agent/getting-started/compatibility-requirements-nodejs-agent#system) as the agent. The minimum supported version of Node.js is `v16.12.0`.
70
+
71
+ ### Setup
72
+
73
+ 1. If you rely on a configuration file to run the agent, you must rename the file from `newrelic.js` to `newrelic.cjs` so it can be properly loaded. All the contents of the configuration file will behave the same once you rename. See [CommonJS modules in ESM](https://nodejs.org/api/modules.html#enabling) for more details.
74
+
75
+ ```sh
76
+ $ mv newrelic.js newrelic.cjs
77
+ ```
78
+
79
+ 2. To use the newrelic ESM loader, start your program with node and use the `--experimental-loader` flag and a path to the loader file, like this:
80
+
81
+ ```sh
82
+ $ node --experimental-loader newrelic/esm-loader.mjs your-program.js
83
+ ```
84
+
85
+ **Note**: Unlike the CommonJS methods listed above, there are no alternatives to running the agent without the `--experimental-loader` flag.
86
+
61
87
  ## Getting Started
62
88
 
63
89
  For more information on getting started, [check the Node.js docs](https://docs.newrelic.com/docs/agents/nodejs-agent/getting-started/introduction-new-relic-nodejs).
@@ -64,6 +64,7 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic
64
64
  * [sinon](#sinon)
65
65
  * [tap](#tap)
66
66
  * [temp](#temp)
67
+ * [testdouble](#testdouble)
67
68
  * [when](#when)
68
69
 
69
70
  **[optionalDependencies](#optionalDependencies)**
@@ -1333,7 +1334,7 @@ SOFTWARE.
1333
1334
 
1334
1335
  ### @newrelic/eslint-config
1335
1336
 
1336
- This product includes source derived from [@newrelic/eslint-config](https://github.com/newrelic/eslint-config-newrelic) ([v0.0.3](https://github.com/newrelic/eslint-config-newrelic/tree/v0.0.3)), distributed under the [Apache-2.0 License](https://github.com/newrelic/eslint-config-newrelic/blob/v0.0.3/LICENSE):
1337
+ This product includes source derived from [@newrelic/eslint-config](https://github.com/newrelic/eslint-config-newrelic) ([v0.1.0](https://github.com/newrelic/eslint-config-newrelic/tree/v0.1.0)), distributed under the [Apache-2.0 License](https://github.com/newrelic/eslint-config-newrelic/blob/v0.1.0/LICENSE):
1337
1338
 
1338
1339
  ```
1339
1340
  Apache License
@@ -1766,7 +1767,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
1766
1767
 
1767
1768
  ### @newrelic/test-utilities
1768
1769
 
1769
- This product includes source derived from [@newrelic/test-utilities](https://github.com/newrelic/node-test-utilities) ([v7.0.0](https://github.com/newrelic/node-test-utilities/tree/v7.0.0)), distributed under the [Apache-2.0 License](https://github.com/newrelic/node-test-utilities/blob/v7.0.0/LICENSE):
1770
+ This product includes source derived from [@newrelic/test-utilities](https://github.com/newrelic/node-test-utilities) ([v7.1.1](https://github.com/newrelic/node-test-utilities/tree/v7.1.1)), distributed under the [Apache-2.0 License](https://github.com/newrelic/node-test-utilities/blob/v7.1.1/LICENSE):
1770
1771
 
1771
1772
  ```
1772
1773
  Apache License
@@ -8977,6 +8978,35 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8977
8978
 
8978
8979
  ```
8979
8980
 
8981
+ ### testdouble
8982
+
8983
+ This product includes source derived from [testdouble](https://github.com/testdouble/testdouble.js) ([v3.16.6](https://github.com/testdouble/testdouble.js/tree/v3.16.6)), distributed under the [MIT License](https://github.com/testdouble/testdouble.js/blob/v3.16.6/LICENSE.txt):
8984
+
8985
+ ```
8986
+ The MIT License (MIT)
8987
+
8988
+ Copyright (c) 2015 Test Double, LLC.
8989
+
8990
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8991
+ of this software and associated documentation files (the "Software"), to deal
8992
+ in the Software without restriction, including without limitation the rights
8993
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8994
+ copies of the Software, and to permit persons to whom the Software is
8995
+ furnished to do so, subject to the following conditions:
8996
+
8997
+ The above copyright notice and this permission notice shall be included in all
8998
+ copies or substantial portions of the Software.
8999
+
9000
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9001
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9002
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
9003
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
9004
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9005
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9006
+ SOFTWARE.
9007
+
9008
+ ```
9009
+
8980
9010
  ### when
8981
9011
 
8982
9012
  This product includes source derived from [when](https://github.com/cujojs/when) ([v3.7.8](https://github.com/cujojs/when/tree/v3.7.8)), distributed under the [MIT License](https://github.com/cujojs/when/blob/v3.7.8/LICENSE.txt):
package/esm-loader.mjs ADDED
@@ -0,0 +1,94 @@
1
+ /*
2
+ * Copyright 2022 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import newrelic from './index.js'
7
+ import shimmer from './lib/shimmer.js'
8
+ import loggingModule from './lib/logger.js'
9
+ import NAMES from './lib/metrics/names.js'
10
+ import semver from 'semver'
11
+
12
+ const isSupportedVersion = () => semver.gte(process.version, 'v16.12.0')
13
+ const logger = loggingModule.child({ component: 'esm-loader' })
14
+
15
+ if (newrelic.agent) {
16
+ addESMSupportabilityMetrics(newrelic.agent)
17
+ }
18
+
19
+ /**
20
+ * Hook chain responsible for resolving a file URL for a given module specifier
21
+ *
22
+ * Our loader has to be the last user-supplied loader if chaining is happening,
23
+ * as we rely on `nextResolve` being the default Node.js resolve hook to get our URL
24
+ *
25
+ * Docs: https://nodejs.org/api/esm.html#resolvespecifier-context-nextresolve
26
+ *
27
+ * @param {string} specifier
28
+ * String identifier in an import statement or import() expression
29
+ * @param {object} context
30
+ * Metadata about the specifier, including url of the parent module and any import assertions
31
+ * Optional argument that only needs to be passed when changed
32
+ * @param {Function} nextResolve
33
+ * The Node.js default resolve hook
34
+ * @returns {Promise} Promise object representing the resolution of a given specifier
35
+ */
36
+ export async function resolve(specifier, context, nextResolve) {
37
+ if (!newrelic.agent || !isSupportedVersion()) {
38
+ return nextResolve(specifier, context, nextResolve)
39
+ }
40
+
41
+ /**
42
+ * We manually call the default Node.js resolve hook so
43
+ * that we can get the fully qualified URL path and the
44
+ * package type (commonjs/module/builtin) without
45
+ * duplicating the logic of the Node.js hook
46
+ */
47
+ const resolvedModule = await nextResolve(specifier, context, nextResolve)
48
+ const instrumentationName = shimmer.getInstrumentationNameFromModuleName(specifier)
49
+ const instrumentationDefinition = shimmer.registeredInstrumentations[instrumentationName]
50
+
51
+ if (instrumentationDefinition) {
52
+ logger.debug(`Instrumentation exists for ${specifier}`)
53
+
54
+ if (resolvedModule.format === 'commonjs') {
55
+ // ES Modules translate import statements into fully qualified filepaths, so we create a copy of our instrumentation under this filepath
56
+ const instrumentationDefinitionCopy = Object.assign({}, instrumentationDefinition)
57
+
58
+ // Stripping the prefix is necessary because the code downstream gets this url without it
59
+ instrumentationDefinitionCopy.moduleName = resolvedModule.url.replace('file://', '')
60
+
61
+ // Added to keep our Supportability metrics from exploding/including customer info via full filepath
62
+ instrumentationDefinitionCopy.specifier = specifier
63
+
64
+ shimmer.registerInstrumentation(instrumentationDefinitionCopy)
65
+
66
+ logger.debug(
67
+ `Registered CommonJS instrumentation for ${specifier} under ${instrumentationDefinitionCopy.moduleName}`
68
+ )
69
+ } else {
70
+ logger.debug(`${specifier} is not a CommonJS module, skipping for now`)
71
+ }
72
+ }
73
+
74
+ return resolvedModule
75
+ }
76
+
77
+ /**
78
+ * Helper function for determining which of our Supportability metrics to use for the current loader invocation
79
+ *
80
+ * @param {object} agent
81
+ * instantiation of the New Relic agent
82
+ * @returns {void}
83
+ */
84
+ export function addESMSupportabilityMetrics(agent) {
85
+ if (isSupportedVersion()) {
86
+ agent.metrics.getOrCreateMetric(NAMES.FEATURES.ESM.LOADER).incrementCallCount()
87
+ } else {
88
+ logger.warn(
89
+ 'New Relic for Node.js ESM loader requires a version of Node >= v16.12.0; your version is %s. Instrumentation will not be registered.',
90
+ process.version
91
+ )
92
+ agent.metrics.getOrCreateMetric(NAMES.FEATURES.ESM.UNSUPPORTED_LOADER).incrementCallCount()
93
+ }
94
+ }
package/index.js CHANGED
@@ -134,7 +134,7 @@ function createAgent(config) {
134
134
  if (appNames.length < 1) {
135
135
  const message =
136
136
  'New Relic requires that you name this application!\n' +
137
- 'Set app_name in your newrelic.js file or set environment variable\n' +
137
+ 'Set app_name in your newrelic.js or newrelic.cjs file or set environment variable\n' +
138
138
  'NEW_RELIC_APP_NAME. Not starting!'
139
139
  logger.error(message)
140
140
  throw new Error(message)
@@ -1647,6 +1647,10 @@ function initialize(config) {
1647
1647
  try {
1648
1648
  userConf = require(filepath).config
1649
1649
  } catch (error) {
1650
+ if (error.code === 'ERR_REQUIRE_ESM') {
1651
+ // Attempted to import newrelic.js or similar from an ESM module, error out early.
1652
+ throw error
1653
+ }
1650
1654
  logger.error(error)
1651
1655
 
1652
1656
  logger.warn(
@@ -45,7 +45,7 @@ function cmdStartedHandler(shim, evnt) {
45
45
  if (hosts && hosts.length && hosts[0].socketPath) {
46
46
  port = hosts[0].socketPath
47
47
  }
48
- } else if (['127.0.0.1', '::1'].includes(host)) {
48
+ } else if (['127.0.0.1', '::1', '[::1]'].includes(host)) {
49
49
  host = 'localhost'
50
50
  }
51
51
 
@@ -257,6 +257,10 @@ const INFINITE_TRACING = {
257
257
  }
258
258
 
259
259
  const FEATURES = {
260
+ ESM: {
261
+ LOADER: `${SUPPORTABILITY.FEATURES}/ESM/Loader`,
262
+ UNSUPPORTED_LOADER: `${SUPPORTABILITY.FEATURES}/ESM/UnsupportedLoader`
263
+ },
260
264
  CERTIFICATES: SUPPORTABILITY.FEATURES + '/Certificates',
261
265
  INSTRUMENTATION: {
262
266
  ON_RESOLVED: SUPPORTABILITY.FEATURES + '/Instrumentation/OnResolved',
package/lib/shimmer.js CHANGED
@@ -517,7 +517,12 @@ function instrumentPostLoad(agent, nodule, moduleName, resolvedName) {
517
517
  const shim = shims.createShimFromType(instrumentation.type, agent, moduleName, resolvedName)
518
518
 
519
519
  applyDebugState(shim, nodule)
520
- trackInstrumentationUsage(agent, shim, moduleName, NAMES.FEATURES.INSTRUMENTATION.ON_REQUIRE)
520
+ trackInstrumentationUsage(
521
+ agent,
522
+ shim,
523
+ instrumentation.specifier || moduleName,
524
+ NAMES.FEATURES.INSTRUMENTATION.ON_REQUIRE
525
+ )
521
526
 
522
527
  try {
523
528
  if (instrumentation.onRequire(shim, nodule, moduleName) !== false) {
@@ -613,7 +618,12 @@ function _instrumentOnResolved(agent, moduleName, resolvedFilepath) {
613
618
 
614
619
  const shim = shims.createShimFromType(instrumentation.type, agent, moduleName, resolvedFilepath)
615
620
 
616
- trackInstrumentationUsage(agent, shim, moduleName, NAMES.FEATURES.INSTRUMENTATION.ON_RESOLVED)
621
+ trackInstrumentationUsage(
622
+ agent,
623
+ shim,
624
+ instrumentation.specifier || moduleName,
625
+ NAMES.FEATURES.INSTRUMENTATION.ON_RESOLVED
626
+ )
617
627
 
618
628
  try {
619
629
  instrumentation.onResolved(shim, moduleName, resolvedFilepath)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newrelic",
3
- "version": "9.0.3",
3
+ "version": "9.1.0",
4
4
  "author": "New Relic Node.js agent team <nodejs@newrelic.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [
@@ -113,6 +113,16 @@
113
113
  "name": "Jordi Gutiérrez Hermoso",
114
114
  "email": "jgutierrezhermoso@newrelic.com",
115
115
  "web": "https://newrelic.com"
116
+ },
117
+ {
118
+ "name": "Maurice Rickard",
119
+ "email": "mrickard@newrelic.com",
120
+ "web": "https://newrelic.com"
121
+ },
122
+ {
123
+ "name": "Jessica Lopatta",
124
+ "email": "jlopatta@newrelic.com",
125
+ "web": "https://newrelic.com"
116
126
  }
117
127
  ],
118
128
  "description": "New Relic agent",
@@ -138,17 +148,18 @@
138
148
  "docs": "npm ci && jsdoc -c ./jsdoc-conf.json --private -r .",
139
149
  "integration": "npm run prepare-test && npm run sub-install && time tap test/integration/**/**/*.tap.js --timeout=180 --no-coverage --reporter classic",
140
150
  "prepare-test": "npm run ssl && npm run docker-env",
141
- "lint": "eslint ./*.js lib test bin examples",
142
- "lint:fix": "eslint --fix, ./*.js lib test bin examples",
151
+ "lint": "eslint ./*.{js,mjs} lib test bin examples",
152
+ "lint:fix": "eslint --fix, ./*.{js,mjs} lib test bin examples",
143
153
  "public-docs": "jsdoc -c ./jsdoc-conf.json --tutorials examples/shim api.js lib/shim/ lib/transaction/handle.js && cp examples/shim/*.png out/",
144
154
  "publish-docs": "./bin/publish-docs.sh",
145
155
  "services": "./bin/docker-services.sh",
146
156
  "smoke": "npm run ssl && time tap test/smoke/**/**/*.tap.js --timeout=180 --no-coverage",
147
157
  "ssl": "./bin/ssl.sh",
148
158
  "sub-install": "node test/bin/install_sub_deps",
149
- "test": "npm run integration && npm run unit",
159
+ "test": "npm run integration && npm run unit && npm run unit:esm",
150
160
  "third-party-updates": "oss third-party manifest --includeOptDeps && oss third-party notices --includeOptDeps && git add THIRD_PARTY_NOTICES.md third_party_manifest.json",
151
161
  "unit": "rm -f newrelic_agent.log && time tap --test-regex='(\\/|^test\\/unit\\/.*\\.test\\.js)$' --timeout=180 --no-coverage --reporter classic",
162
+ "unit:esm": "rm -f newrelic_agent.log && time tap --test-regex='(\\/|^test\\/unit\\/.*\\.test\\.mjs)$' --timeout=180 --no-coverage --reporter classic --node-arg=--experimental-loader=testdouble",
152
163
  "update-cross-agent-tests": "./bin/update-cats.sh",
153
164
  "versioned-tests": "./bin/run-versioned-tests.sh",
154
165
  "update-changelog-version": "node ./bin/update-changelog-version",
@@ -180,10 +191,10 @@
180
191
  "@newrelic/native-metrics": "^9.0.0"
181
192
  },
182
193
  "devDependencies": {
183
- "@newrelic/eslint-config": "^0.0.3",
194
+ "@newrelic/eslint-config": "^0.1.0",
184
195
  "@newrelic/newrelic-oss-cli": "^0.1.2",
185
196
  "@newrelic/proxy": "^2.0.0",
186
- "@newrelic/test-utilities": "^7.0.0",
197
+ "@newrelic/test-utilities": "^7.1.1",
187
198
  "@octokit/rest": "^18.0.15",
188
199
  "@slack/bolt": "^3.7.0",
189
200
  "ajv": "^6.12.6",
@@ -215,6 +226,7 @@
215
226
  "sinon": "^4.5.0",
216
227
  "tap": "^16.0.1",
217
228
  "temp": "^0.8.1",
229
+ "testdouble": "^3.16.6",
218
230
  "when": "*"
219
231
  },
220
232
  "repository": {
@@ -232,6 +244,7 @@
232
244
  "THIRD_PARTY_NOTICES.md",
233
245
  "lib/",
234
246
  "bin/tracetractor",
235
- "bin/test-naming-rules.js"
247
+ "bin/test-naming-rules.js",
248
+ "esm-loader.mjs"
236
249
  ]
237
250
  }