newrelic 9.0.3 → 9.2.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 +40 -6
- package/README.md +28 -2
- package/THIRD_PARTY_NOTICES.md +35 -5
- package/api.js +3 -1
- package/esm-loader.mjs +204 -0
- package/index.js +1 -1
- package/lib/collector/remote-method.js +3 -0
- package/lib/config/default.js +10 -2
- package/lib/config/env.js +4 -1
- package/lib/config/index.js +4 -0
- package/lib/esm-shim.mjs +36 -0
- package/lib/instrumentation/mongodb/v4-mongo.js +1 -1
- package/lib/metrics/names.js +5 -0
- package/lib/shimmer.js +14 -4
- package/newrelic.js +0 -8
- package/package.json +22 -9
package/NEWS.md
CHANGED
|
@@ -1,9 +1,43 @@
|
|
|
1
|
-
### v9.0
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
### v9.2.0 (2022-10-06)
|
|
2
|
+
|
|
3
|
+
* Added ability to instrument ES Modules with the New Relic ESM Loader.
|
|
4
|
+
* [Example ESM application](https://github.com/newrelic/newrelic-node-examples/tree/main/esm-app)
|
|
5
|
+
|
|
6
|
+
* Added support for custom ESM instrumentation.
|
|
7
|
+
* There is structure to registering custom ESM instrumentation. Set the relative path to the instrumentation entry point via `api.esm.custom_instrumentation_entrypoint`
|
|
8
|
+
* [Sample custom ESM instrumentation entrypoint](https://github.com/newrelic/newrelic-node-examples/blob/main/esm-app/custom-instrumentation/index.js)
|
|
9
|
+
* All the `newrelic.instrument*` methods will still work except `newrelic.instrumentLoadedModule`. This is because it is geared toward CommonJS modules.
|
|
10
|
+
|
|
11
|
+
* Added test for asserting ESM loader functionality on ESM-only package
|
|
12
|
+
|
|
13
|
+
* Added supportability metric of `Supportability/Nodejs/Collector/MaxPayloadSizeLimit/<endpoint>` when `max_payload_size_in_bytes` configuration value is exceeded.
|
|
14
|
+
|
|
15
|
+
* Removed `application_logging.forwarding.enabled` stanza from sample config as the feature is now enabled by default.
|
|
16
|
+
|
|
17
|
+
### v9.1.0 (2022-09-22)
|
|
18
|
+
|
|
19
|
+
* Added [experimental loader](https://nodejs.org/api/esm.html#loaders) to support instrumentation of CommonJS packages in ECMAScript Module(ESM) applications.
|
|
20
|
+
* It only supports versions of Node.js >= `16.12.0`.
|
|
21
|
+
* It is subject to change due to its experimental stability.
|
|
22
|
+
|
|
23
|
+
* Enhanced supportability metrics for ESM support.
|
|
24
|
+
* Added new metrics to track usage of ESM loader(`Supportability/Features/ESM/Loader` and `Supportability/Features/ESM/UnsupportedLoader`).
|
|
25
|
+
* Updated instrumentation map to include an optional "friendly name" for tracking metrics.
|
|
26
|
+
|
|
27
|
+
* Enabled re-throwing ESM import errors of `newrelic.js` so that the user is informed to rename it to `newrelic.cjs`
|
|
28
|
+
|
|
29
|
+
* Fixed an issue with mongodb instrumentation where IPv6 address([::1]) was not getting mapped to localhost when setting the host attribute on the segment.
|
|
30
|
+
|
|
31
|
+
* Added a test ESM loader to properly mock out agent in versioned tests.
|
|
32
|
+
|
|
33
|
+
* Added ESM versioned tests for: `express`, `pg`, `mongodb`, and `@grpc/grpc-js`.
|
|
34
|
+
|
|
35
|
+
### v9.0.3 (2022-09-06)
|
|
36
|
+
|
|
37
|
+
* Updated gRPC client instrumentation to respect `grpc.record_errors` when deciding to log errors on gRPC client requests.
|
|
38
|
+
|
|
39
|
+
* Fixed transaction name finalization to properly copy the appropriate transaction name to root segment.
|
|
40
|
+
|
|
7
41
|
### v9.0.2 (2022-08-23)
|
|
8
42
|
|
|
9
43
|
* Added unit test suite for `lib/logger.js`.
|
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
|
-
|
|
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
|
-
|
|
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).
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -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
|
|
1337
|
+
This product includes source derived from [@newrelic/eslint-config](https://github.com/newrelic/eslint-config-newrelic) ([v0.2.0](https://github.com/newrelic/eslint-config-newrelic/tree/v0.2.0)), distributed under the [Apache-2.0 License](https://github.com/newrelic/eslint-config-newrelic/blob/v0.2.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.
|
|
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
|
|
@@ -2223,7 +2224,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|
|
2223
2224
|
|
|
2224
2225
|
### eslint-plugin-jsdoc
|
|
2225
2226
|
|
|
2226
|
-
This product includes source derived from [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) ([
|
|
2227
|
+
This product includes source derived from [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) ([v39.3.6](https://github.com/gajus/eslint-plugin-jsdoc/tree/v39.3.6)), distributed under the [BSD-3-Clause License](https://github.com/gajus/eslint-plugin-jsdoc/blob/v39.3.6/LICENSE):
|
|
2227
2228
|
|
|
2228
2229
|
```
|
|
2229
2230
|
Copyright (c) 2018, Gajus Kuizinas (http://gajus.com/)
|
|
@@ -2317,10 +2318,10 @@ OTHER DEALINGS IN THE SOFTWARE.
|
|
|
2317
2318
|
|
|
2318
2319
|
### eslint
|
|
2319
2320
|
|
|
2320
|
-
This product includes source derived from [eslint](https://github.com/eslint/eslint) ([
|
|
2321
|
+
This product includes source derived from [eslint](https://github.com/eslint/eslint) ([v8.24.0](https://github.com/eslint/eslint/tree/v8.24.0)), distributed under the [MIT License](https://github.com/eslint/eslint/blob/v8.24.0/LICENSE):
|
|
2321
2322
|
|
|
2322
2323
|
```
|
|
2323
|
-
Copyright
|
|
2324
|
+
Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
|
|
2324
2325
|
|
|
2325
2326
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
2326
2327
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -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/api.js
CHANGED
|
@@ -1298,7 +1298,9 @@ API.prototype.instrumentMessages = function instrumentMessages(moduleName, onReq
|
|
|
1298
1298
|
}
|
|
1299
1299
|
|
|
1300
1300
|
/**
|
|
1301
|
-
* Applies an instrumentation to an already loaded module.
|
|
1301
|
+
* Applies an instrumentation to an already loaded CommonJs module.
|
|
1302
|
+
*
|
|
1303
|
+
* Note: This function will not work for ESM packages.
|
|
1302
1304
|
*
|
|
1303
1305
|
* // oh no, express was loaded before newrelic
|
|
1304
1306
|
* const express = require('express')
|
package/esm-loader.mjs
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
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
|
+
import path from 'node:path'
|
|
12
|
+
|
|
13
|
+
const isSupportedVersion = () => semver.gte(process.version, 'v16.12.0')
|
|
14
|
+
// This check will prevent resolve hooks executing from within this file
|
|
15
|
+
// If I do `import('foo')` in here it'll hit the resolve hook multiple times
|
|
16
|
+
const isFromEsmLoader = (context) =>
|
|
17
|
+
context && context.parentURL && context.parentURL.includes('newrelic/esm-loader.mjs')
|
|
18
|
+
|
|
19
|
+
const logger = loggingModule.child({ component: 'esm-loader' })
|
|
20
|
+
const esmShimPath = new URL('./lib/esm-shim.mjs', import.meta.url)
|
|
21
|
+
const customEntryPoint = newrelic?.agent?.config?.api.esm.custom_instrumentation_entrypoint
|
|
22
|
+
|
|
23
|
+
// Hook point within agent for customers to register their custom instrumentation.
|
|
24
|
+
if (customEntryPoint) {
|
|
25
|
+
const resolvedEntryPoint = path.resolve(customEntryPoint)
|
|
26
|
+
logger.debug('Registering custom ESM instrumentation at %s', resolvedEntryPoint)
|
|
27
|
+
await import(resolvedEntryPoint)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
addESMSupportabilityMetrics(newrelic.agent)
|
|
31
|
+
|
|
32
|
+
// exporting for testing purposes
|
|
33
|
+
export const registeredSpecifiers = new Map()
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Hook chain responsible for resolving a file URL for a given module specifier
|
|
37
|
+
*
|
|
38
|
+
* Our loader has to be the last user-supplied loader if chaining is happening,
|
|
39
|
+
* as we rely on `nextResolve` being the default Node.js resolve hook to get our URL
|
|
40
|
+
*
|
|
41
|
+
* Docs: https://nodejs.org/api/esm.html#resolvespecifier-context-nextresolve
|
|
42
|
+
*
|
|
43
|
+
* @param {string} specifier string identifier in an import statement or import() expression
|
|
44
|
+
* @param {object} context metadata about the specifier, including url of the parent module and any import assertions
|
|
45
|
+
* Optional argument that only needs to be passed when changed
|
|
46
|
+
* @param {Function} nextResolve The subsequent resolve hook in the chain, or the Node.js default resolve hook after the last user-supplied resolve hook
|
|
47
|
+
* @returns {Promise} Promise object representing the resolution of a given specifier
|
|
48
|
+
*/
|
|
49
|
+
export async function resolve(specifier, context, nextResolve) {
|
|
50
|
+
if (!newrelic.agent || !isSupportedVersion() || isFromEsmLoader(context)) {
|
|
51
|
+
return nextResolve(specifier, context, nextResolve)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* We manually call the default Node.js resolve hook so
|
|
56
|
+
* that we can get the fully qualified URL path and the
|
|
57
|
+
* package type (commonjs/module/builtin) without
|
|
58
|
+
* duplicating the logic of the Node.js hook
|
|
59
|
+
*/
|
|
60
|
+
const resolvedModule = await nextResolve(specifier, context, nextResolve)
|
|
61
|
+
const instrumentationName = shimmer.getInstrumentationNameFromModuleName(specifier)
|
|
62
|
+
const instrumentationDefinition = shimmer.registeredInstrumentations[instrumentationName]
|
|
63
|
+
|
|
64
|
+
if (instrumentationDefinition) {
|
|
65
|
+
const { url, format } = resolvedModule
|
|
66
|
+
logger.debug(`Instrumentation exists for ${specifier} ${format} package.`)
|
|
67
|
+
|
|
68
|
+
if (format === 'commonjs') {
|
|
69
|
+
// ES Modules translate import statements into fully qualified filepaths, so we create a copy of our instrumentation under this filepath
|
|
70
|
+
const instrumentationDefinitionCopy = Object.assign({}, instrumentationDefinition)
|
|
71
|
+
|
|
72
|
+
// Stripping the prefix is necessary because the code downstream gets this url without it
|
|
73
|
+
instrumentationDefinitionCopy.moduleName = url.replace('file://', '')
|
|
74
|
+
|
|
75
|
+
// Added to keep our Supportability metrics from exploding/including customer info via full filepath
|
|
76
|
+
instrumentationDefinitionCopy.specifier = specifier
|
|
77
|
+
|
|
78
|
+
shimmer.registerInstrumentation(instrumentationDefinitionCopy)
|
|
79
|
+
|
|
80
|
+
logger.debug(
|
|
81
|
+
`Registered CommonJS instrumentation for ${specifier} under ${instrumentationDefinitionCopy.moduleName}`
|
|
82
|
+
)
|
|
83
|
+
} else if (format === 'module') {
|
|
84
|
+
registeredSpecifiers.set(url, specifier)
|
|
85
|
+
const modifiedUrl = new URL(url)
|
|
86
|
+
// add a query param to the resolved url so the load hook below knows
|
|
87
|
+
// to rewrite and wrap the source code
|
|
88
|
+
modifiedUrl.searchParams.set('hasNrInstrumentation', 'true')
|
|
89
|
+
resolvedModule.url = modifiedUrl.href
|
|
90
|
+
} else {
|
|
91
|
+
logger.debug(`${specifier} is not a CommonJS nor ESM package, skipping for now.`)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return resolvedModule
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Hook chain responsible for determining how a URL should be interpreted, retrieved, and parsed.
|
|
100
|
+
*
|
|
101
|
+
* Our loader has to be the last user-supplied loader if chaining is happening,
|
|
102
|
+
* as we rely on `nextLoad` being the default Node.js resolve hook to load the ESM.
|
|
103
|
+
*
|
|
104
|
+
* Docs: https://nodejs.org/dist/latest-v18.x/docs/api/esm.html#loadurl-context-nextload
|
|
105
|
+
*
|
|
106
|
+
* @param {string} url the URL returned by the resolve chain
|
|
107
|
+
* @param {object} context metadata about the url, including conditions, format and import assertions
|
|
108
|
+
* @param {Function} nextLoad the subsequent load hook in the chain, or the Node.js default load hook after the last user-supplied load hook
|
|
109
|
+
* @returns {Promise} Promise object representing the load of a given url
|
|
110
|
+
*/
|
|
111
|
+
export async function load(url, context, nextLoad) {
|
|
112
|
+
if (!newrelic.agent || !isSupportedVersion()) {
|
|
113
|
+
return nextLoad(url, context, nextLoad)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let parsedUrl
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
parsedUrl = new URL(url)
|
|
120
|
+
} catch (err) {
|
|
121
|
+
logger.error('Unable to parse url: %s, msg: %s', url, err.message)
|
|
122
|
+
return nextLoad(url, context, nextLoad)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const hasNrInstrumentation = parsedUrl.searchParams.get('hasNrInstrumentation')
|
|
126
|
+
|
|
127
|
+
if (!hasNrInstrumentation) {
|
|
128
|
+
return nextLoad(url, context, nextLoad)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* undo the work we did in the resolve hook above
|
|
133
|
+
* so we can properly rewrite source and not get in an infinite loop
|
|
134
|
+
*/
|
|
135
|
+
parsedUrl.searchParams.delete('hasNrInstrumentation')
|
|
136
|
+
|
|
137
|
+
const originalUrl = parsedUrl.href
|
|
138
|
+
const specifier = registeredSpecifiers.get(originalUrl)
|
|
139
|
+
const rewrittenSource = await wrapEsmSource(originalUrl, specifier)
|
|
140
|
+
logger.debug(`Registered module instrumentation for ${specifier}.`)
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
format: 'module',
|
|
144
|
+
source: rewrittenSource,
|
|
145
|
+
shortCircuit: true
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Helper function for determining which of our Supportability metrics to use for the current loader invocation
|
|
151
|
+
*
|
|
152
|
+
* @param {object} agent
|
|
153
|
+
* instantiation of the New Relic agent
|
|
154
|
+
* @returns {void}
|
|
155
|
+
*/
|
|
156
|
+
function addESMSupportabilityMetrics(agent) {
|
|
157
|
+
if (!agent) {
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (isSupportedVersion()) {
|
|
162
|
+
agent.metrics.getOrCreateMetric(NAMES.FEATURES.ESM.LOADER).incrementCallCount()
|
|
163
|
+
} else {
|
|
164
|
+
logger.warn(
|
|
165
|
+
'New Relic for Node.js ESM loader requires a version of Node >= v16.12.0; your version is %s. Instrumentation will not be registered.',
|
|
166
|
+
process.version
|
|
167
|
+
)
|
|
168
|
+
agent.metrics.getOrCreateMetric(NAMES.FEATURES.ESM.UNSUPPORTED_LOADER).incrementCallCount()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (customEntryPoint) {
|
|
172
|
+
agent.metrics.getOrCreateMetric(NAMES.FEATURES.ESM.CUSTOM_INSTRUMENTATION).incrementCallCount()
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Rewrites the source code of a ES module we want to instrument.
|
|
178
|
+
* This is done by injecting the ESM shim which proxies every property on the exported
|
|
179
|
+
* module and registers the module with shimmer so instrumentation can be registered properly.
|
|
180
|
+
*
|
|
181
|
+
* @param {string} url the URL returned by the resolve chain
|
|
182
|
+
* @param {string} specifier string identifier in an import statement or import() expression
|
|
183
|
+
* @returns {string} source code rewritten to wrap with our esm-shim
|
|
184
|
+
*/
|
|
185
|
+
async function wrapEsmSource(url, specifier) {
|
|
186
|
+
const pkg = await import(url)
|
|
187
|
+
const props = Object.keys(pkg)
|
|
188
|
+
const trimmedUrl = url.replace('file://', '')
|
|
189
|
+
|
|
190
|
+
return `
|
|
191
|
+
import wrapModule from '${esmShimPath.href}'
|
|
192
|
+
import * as _originalModule from '${url}'
|
|
193
|
+
const _wrappedModule = wrapModule(_originalModule, '${specifier}', '${trimmedUrl}')
|
|
194
|
+
${props
|
|
195
|
+
.map((propName) => {
|
|
196
|
+
const propertyExportSource = `
|
|
197
|
+
let _${propName} = _wrappedModule.${propName}
|
|
198
|
+
export { _${propName} as ${propName} }`
|
|
199
|
+
|
|
200
|
+
return propertyExportSource
|
|
201
|
+
})
|
|
202
|
+
.join('\n')}
|
|
203
|
+
`
|
|
204
|
+
}
|
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)
|
|
@@ -235,6 +235,9 @@ RemoteMethod.prototype._safeRequest = function _safeRequest(options) {
|
|
|
235
235
|
let level = 'trace'
|
|
236
236
|
|
|
237
237
|
if (!isValidLength(options.body, maxPayloadSize)) {
|
|
238
|
+
this._agent.metrics
|
|
239
|
+
.getOrCreateMetric(`${DATA_USAGE.PREFIX}/MaxPayloadSizeLimit/${this.name}`)
|
|
240
|
+
.incrementCallCount()
|
|
238
241
|
logger.warn(
|
|
239
242
|
'The payload size %d being sent to method %s exceeded the maximum size of %d',
|
|
240
243
|
Buffer.byteLength(options.body, 'utf8'),
|
package/lib/config/default.js
CHANGED
|
@@ -26,7 +26,7 @@ exports.config = () => ({
|
|
|
26
26
|
license_key: '',
|
|
27
27
|
/**
|
|
28
28
|
*
|
|
29
|
-
* Enables/Disables security
|
|
29
|
+
* Enables/Disables security policies. Paste your security policies
|
|
30
30
|
* token from the New Relic Admin below.
|
|
31
31
|
*
|
|
32
32
|
* @env NEW_RELIC_SECURITY_POLICIES_TOKEN
|
|
@@ -623,7 +623,15 @@ exports.config = () => ({
|
|
|
623
623
|
*
|
|
624
624
|
* @env NEW_RELIC_API_NOTICE_ERROR
|
|
625
625
|
*/
|
|
626
|
-
notice_error_enabled: true
|
|
626
|
+
notice_error_enabled: true,
|
|
627
|
+
/**
|
|
628
|
+
* Sets the path to custom ESM instrumentation
|
|
629
|
+
*
|
|
630
|
+
* @env NEW_RELIC_API_ESM_CUSTOM_INSTRUMENTATION_ENTRYPOINT
|
|
631
|
+
*/
|
|
632
|
+
esm: {
|
|
633
|
+
custom_instrumentation_entrypoint: null
|
|
634
|
+
}
|
|
627
635
|
},
|
|
628
636
|
/**
|
|
629
637
|
* Transaction Events
|
package/lib/config/env.js
CHANGED
|
@@ -114,7 +114,10 @@ const ENV_MAPPING = {
|
|
|
114
114
|
api: {
|
|
115
115
|
custom_attributes_enabled: 'NEW_RELIC_API_CUSTOM_ATTRIBUTES',
|
|
116
116
|
custom_events_enabled: 'NEW_RELIC_API_CUSTOM_EVENTS',
|
|
117
|
-
notice_error_enabled: 'NEW_RELIC_API_NOTICE_ERROR'
|
|
117
|
+
notice_error_enabled: 'NEW_RELIC_API_NOTICE_ERROR',
|
|
118
|
+
esm: {
|
|
119
|
+
custom_instrumentation_entrypoint: 'NEW_RELIC_API_ESM_CUSTOM_INSTRUMENTATION_ENTRYPOINT'
|
|
120
|
+
}
|
|
118
121
|
},
|
|
119
122
|
datastore_tracer: {
|
|
120
123
|
instance_reporting: {
|
package/lib/config/index.js
CHANGED
|
@@ -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(
|
package/lib/esm-shim.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2022 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import shimmer from './shimmer.js'
|
|
7
|
+
import newrelic from '../index.js'
|
|
8
|
+
const { agent } = newrelic
|
|
9
|
+
|
|
10
|
+
export default function wrapModule(module, moduleName, resolvedPath) {
|
|
11
|
+
const proxiedProps = Object.assign(Object.create(null), module)
|
|
12
|
+
const proxiedModule = new Proxy(module, {
|
|
13
|
+
get: (target, key) => {
|
|
14
|
+
return proxiedProps[key] || target[key]
|
|
15
|
+
},
|
|
16
|
+
set: (target, key, val) => {
|
|
17
|
+
return (proxiedProps[key] = val)
|
|
18
|
+
},
|
|
19
|
+
/**
|
|
20
|
+
* This is normally not allowed.
|
|
21
|
+
* We have some things that define property, unsure if at the module level.
|
|
22
|
+
* Ideally, we can get away from doing this sort of thing at all in the future
|
|
23
|
+
* in a post AsyncLocalStorage world, etc.
|
|
24
|
+
* If we never do at the module level, perhaps we can not define this or defer upstream
|
|
25
|
+
*
|
|
26
|
+
* @param target
|
|
27
|
+
* @param key
|
|
28
|
+
* @param descriptor
|
|
29
|
+
*/
|
|
30
|
+
defineProperty: (target, key, descriptor) => {
|
|
31
|
+
return Object.defineProperty(proxiedProps, key, descriptor)
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
return shimmer.instrumentPostLoad(agent, proxiedModule, moduleName, resolvedPath, true)
|
|
36
|
+
}
|
|
@@ -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
|
|
package/lib/metrics/names.js
CHANGED
|
@@ -257,6 +257,11 @@ 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
|
+
CUSTOM_INSTRUMENTATION: `${SUPPORTABILITY.FEATURES}/ESM/CustomInstrumentation`
|
|
264
|
+
},
|
|
260
265
|
CERTIFICATES: SUPPORTABILITY.FEATURES + '/Certificates',
|
|
261
266
|
INSTRUMENTATION: {
|
|
262
267
|
ON_RESOLVED: SUPPORTABILITY.FEATURES + '/Instrumentation/OnResolved',
|
package/lib/shimmer.js
CHANGED
|
@@ -467,12 +467,12 @@ const shimmer = (module.exports = {
|
|
|
467
467
|
return instrumentation
|
|
468
468
|
},
|
|
469
469
|
|
|
470
|
-
instrumentPostLoad(agent, module, moduleName, resolvedName) {
|
|
470
|
+
instrumentPostLoad(agent, module, moduleName, resolvedName, returnModule = false) {
|
|
471
471
|
const result = _postLoad(agent, module, moduleName, resolvedName)
|
|
472
472
|
// This is to not break the public API
|
|
473
473
|
// previously it would just call instrumentation
|
|
474
474
|
// and not check the result
|
|
475
|
-
return !!result.__NR_instrumented
|
|
475
|
+
return returnModule ? result : !!result.__NR_instrumented
|
|
476
476
|
}
|
|
477
477
|
})
|
|
478
478
|
|
|
@@ -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(
|
|
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(
|
|
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/newrelic.js
CHANGED
|
@@ -28,14 +28,6 @@ exports.config = {
|
|
|
28
28
|
* attributes include/exclude lists.
|
|
29
29
|
*/
|
|
30
30
|
allow_all_headers: true,
|
|
31
|
-
application_logging: {
|
|
32
|
-
forwarding: {
|
|
33
|
-
/**
|
|
34
|
-
* Toggles whether the agent gathers log records for sending to New Relic.
|
|
35
|
-
*/
|
|
36
|
-
enabled: true
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
31
|
attributes: {
|
|
40
32
|
/**
|
|
41
33
|
* Prefix of attributes to exclude from all destinations. Allows * as wildcard
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "9.0
|
|
3
|
+
"version": "9.2.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,21 +191,21 @@
|
|
|
180
191
|
"@newrelic/native-metrics": "^9.0.0"
|
|
181
192
|
},
|
|
182
193
|
"devDependencies": {
|
|
183
|
-
"@newrelic/eslint-config": "^0.0
|
|
194
|
+
"@newrelic/eslint-config": "^0.2.0",
|
|
184
195
|
"@newrelic/newrelic-oss-cli": "^0.1.2",
|
|
185
196
|
"@newrelic/proxy": "^2.0.0",
|
|
186
|
-
"@newrelic/test-utilities": "^7.
|
|
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",
|
|
190
201
|
"async": "^3.2.4",
|
|
191
202
|
"chai": "^4.1.2",
|
|
192
203
|
"commander": "^7.0.0",
|
|
193
|
-
"eslint": "^
|
|
204
|
+
"eslint": "^8.24.0",
|
|
194
205
|
"eslint-config-prettier": "^8.3.0",
|
|
195
206
|
"eslint-plugin-disable": "^2.0.1",
|
|
196
207
|
"eslint-plugin-header": "^3.1.1",
|
|
197
|
-
"eslint-plugin-jsdoc": "^
|
|
208
|
+
"eslint-plugin-jsdoc": "^39.3.6",
|
|
198
209
|
"eslint-plugin-node": "^11.1.0",
|
|
199
210
|
"eslint-plugin-prettier": "^3.4.0",
|
|
200
211
|
"express": "*",
|
|
@@ -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
|
}
|