newrelic 10.6.2 → 11.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 +66 -0
- package/README.md +11 -23
- package/THIRD_PARTY_NOTICES.md +140 -65
- package/esm-loader.mjs +1 -225
- package/index.js +22 -4
- package/lib/config/default.js +0 -8
- package/lib/context-manager/create-context-manager.js +3 -17
- package/lib/feature_flags.js +4 -3
- package/lib/instrumentation/@prisma/client.js +23 -46
- package/lib/instrumentation/{amqplib.js → amqplib/amqplib.js} +0 -28
- package/lib/instrumentation/amqplib/nr-hooks.js +20 -0
- package/lib/instrumentation/core/async-hooks.js +1 -1
- package/lib/instrumentation/core/timers.js +2 -2
- package/lib/instrumentation/grpc-js/grpc.js +11 -15
- package/lib/instrumentation/grpc-js/nr-hooks.js +17 -5
- package/lib/instrumentation/pino/nr-hooks.js +3 -4
- package/lib/instrumentation/pino/pino.js +7 -20
- package/lib/instrumentation/undici.js +71 -82
- package/lib/instrumentations.js +1 -2
- package/lib/metrics/names.js +1 -2
- package/lib/shim/message-shim.js +1 -1
- package/lib/shim/transaction-shim.js +4 -4
- package/lib/shimmer.js +62 -163
- package/lib/symbols.js +1 -0
- package/lib/transaction/trace/index.js +2 -2
- package/package.json +15 -17
- package/lib/context-manager/diagnostics/legacy-diagnostic-context-manager.js +0 -36
- package/lib/esm-shim.mjs +0 -36
package/esm-loader.mjs
CHANGED
|
@@ -3,228 +3,4 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
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
|
-
import { fileURLToPath } from 'node:url'
|
|
13
|
-
|
|
14
|
-
const isSupportedVersion = () => semver.gte(process.version, 'v16.12.0')
|
|
15
|
-
// This check will prevent resolve hooks executing from within this file
|
|
16
|
-
// If I do `import('foo')` in here it'll hit the resolve hook multiple times
|
|
17
|
-
const isFromEsmLoader = (context) =>
|
|
18
|
-
context && context.parentURL && context.parentURL.includes('newrelic/esm-loader.mjs')
|
|
19
|
-
|
|
20
|
-
const logger = loggingModule.child({ component: 'esm-loader' })
|
|
21
|
-
const esmShimPath = new URL('./lib/esm-shim.mjs', import.meta.url)
|
|
22
|
-
const customEntryPoint = newrelic?.agent?.config?.api.esm.custom_instrumentation_entrypoint
|
|
23
|
-
|
|
24
|
-
// Hook point within agent for customers to register their custom instrumentation.
|
|
25
|
-
if (customEntryPoint) {
|
|
26
|
-
const resolvedEntryPoint = path.resolve(customEntryPoint)
|
|
27
|
-
logger.debug('Registering custom ESM instrumentation at %s', resolvedEntryPoint)
|
|
28
|
-
await import(resolvedEntryPoint)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
addESMSupportabilityMetrics(newrelic.agent)
|
|
32
|
-
|
|
33
|
-
// exporting for testing purposes
|
|
34
|
-
export const registeredSpecifiers = new Map()
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Hook chain responsible for resolving a file URL for a given module specifier
|
|
38
|
-
*
|
|
39
|
-
* Our loader has to be the last user-supplied loader if chaining is happening,
|
|
40
|
-
* as we rely on `nextResolve` being the default Node.js resolve hook to get our URL
|
|
41
|
-
*
|
|
42
|
-
* Docs: https://nodejs.org/api/esm.html#resolvespecifier-context-nextresolve
|
|
43
|
-
*
|
|
44
|
-
* @param {string} specifier string identifier in an import statement or import() expression
|
|
45
|
-
* @param {object} context metadata about the specifier, including url of the parent module and any import assertions
|
|
46
|
-
* Optional argument that only needs to be passed when changed
|
|
47
|
-
* @param {Function} nextResolve The subsequent resolve hook in the chain, or the Node.js default resolve hook after the last user-supplied resolve hook
|
|
48
|
-
* @returns {Promise} Promise object representing the resolution of a given specifier
|
|
49
|
-
*/
|
|
50
|
-
export async function resolve(specifier, context, nextResolve) {
|
|
51
|
-
if (!newrelic.agent || !isSupportedVersion() || isFromEsmLoader(context)) {
|
|
52
|
-
return nextResolve(specifier, context, nextResolve)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* We manually call the default Node.js resolve hook so
|
|
57
|
-
* that we can get the fully qualified URL path and the
|
|
58
|
-
* package type (commonjs/module/builtin) without
|
|
59
|
-
* duplicating the logic of the Node.js hook
|
|
60
|
-
*/
|
|
61
|
-
const resolvedModule = await nextResolve(specifier, context, nextResolve)
|
|
62
|
-
const instrumentationName = shimmer.getInstrumentationNameFromModuleName(specifier)
|
|
63
|
-
const instrumentationDefinition = shimmer.registeredInstrumentations[instrumentationName]
|
|
64
|
-
|
|
65
|
-
if (instrumentationDefinition) {
|
|
66
|
-
const { url, format } = resolvedModule
|
|
67
|
-
logger.debug(`Instrumentation exists for ${specifier} ${format} package.`)
|
|
68
|
-
|
|
69
|
-
if (registeredSpecifiers.get(url)) {
|
|
70
|
-
logger.debug(
|
|
71
|
-
`Instrumentation already registered for ${specifier} under ${fileURLToPath(
|
|
72
|
-
url
|
|
73
|
-
)}, skipping resolve hook...`
|
|
74
|
-
)
|
|
75
|
-
} else if (format === 'commonjs') {
|
|
76
|
-
// ES Modules translate import statements into fully qualified filepaths, so we create a copy of our instrumentation under this filepath
|
|
77
|
-
const instrumentationDefinitionCopy = [...instrumentationDefinition]
|
|
78
|
-
|
|
79
|
-
instrumentationDefinitionCopy.forEach((copy) => {
|
|
80
|
-
// Stripping the prefix is necessary because the code downstream gets this url without it
|
|
81
|
-
copy.moduleName = fileURLToPath(url)
|
|
82
|
-
|
|
83
|
-
// Added to keep our Supportability metrics from exploding/including customer info via full filepath
|
|
84
|
-
copy.specifier = specifier
|
|
85
|
-
shimmer.registerInstrumentation(copy)
|
|
86
|
-
logger.debug(
|
|
87
|
-
`Registered CommonJS instrumentation for ${specifier} under ${copy.moduleName}`
|
|
88
|
-
)
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
// Keep track of what we've registered so we don't double register (see: https://github.com/newrelic/node-newrelic/issues/1646)
|
|
92
|
-
registeredSpecifiers.set(url, { specifier })
|
|
93
|
-
} else if (format === 'module') {
|
|
94
|
-
addNrInstrumentation(resolvedModule)
|
|
95
|
-
registeredSpecifiers.set(url, { specifier, hasNrInstrumentation: true })
|
|
96
|
-
} else {
|
|
97
|
-
logger.debug(`${specifier} is not a CommonJS nor ESM package, skipping for now.`)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return resolvedModule
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* This is purely done so that we can import the incoming specifier
|
|
106
|
-
* in the load hook. It used to be used to determine if instrumentation existed
|
|
107
|
-
* for specifier but we found a RCE with that solution.
|
|
108
|
-
*
|
|
109
|
-
* @param {object} resolvedModule the result of call resolve on a specifier
|
|
110
|
-
*/
|
|
111
|
-
function addNrInstrumentation(resolvedModule) {
|
|
112
|
-
const modifiedUrl = new URL(resolvedModule.url)
|
|
113
|
-
modifiedUrl.searchParams.set('hasNrInstrumentation', 'true')
|
|
114
|
-
resolvedModule.url = modifiedUrl.href
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Extracts the href without query params
|
|
119
|
-
*
|
|
120
|
-
* @param {string} url url of specifier
|
|
121
|
-
* @returns {string} new url without hasNrInstrumentation query param
|
|
122
|
-
*/
|
|
123
|
-
function removeNrInstrumentation(url) {
|
|
124
|
-
let parsedUrl
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
parsedUrl = new URL(url)
|
|
128
|
-
url = parsedUrl.href.split('?')[0]
|
|
129
|
-
} catch (err) {
|
|
130
|
-
logger.error('Unable to parse url: %s, msg: %s', url, err.message)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return url
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Hook chain responsible for determining how a URL should be interpreted, retrieved, and parsed.
|
|
138
|
-
*
|
|
139
|
-
* Our loader has to be the last user-supplied loader if chaining is happening,
|
|
140
|
-
* as we rely on `nextLoad` being the default Node.js resolve hook to load the ESM.
|
|
141
|
-
*
|
|
142
|
-
* Docs: https://nodejs.org/dist/latest-v18.x/docs/api/esm.html#loadurl-context-nextload
|
|
143
|
-
*
|
|
144
|
-
* @param {string} url the URL returned by the resolve chain
|
|
145
|
-
* @param {object} context metadata about the url, including conditions, format and import assertions
|
|
146
|
-
* @param {Function} nextLoad the subsequent load hook in the chain, or the Node.js default load hook after the last user-supplied load hook
|
|
147
|
-
* @returns {Promise} Promise object representing the load of a given url
|
|
148
|
-
*/
|
|
149
|
-
export async function load(url, context, nextLoad) {
|
|
150
|
-
if (!newrelic.agent || !isSupportedVersion()) {
|
|
151
|
-
return nextLoad(url, context, nextLoad)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
url = removeNrInstrumentation(url)
|
|
155
|
-
|
|
156
|
-
const pkg = registeredSpecifiers.get(url)
|
|
157
|
-
|
|
158
|
-
if (!pkg?.hasNrInstrumentation) {
|
|
159
|
-
return nextLoad(url, context, nextLoad)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const { specifier } = pkg
|
|
163
|
-
|
|
164
|
-
const rewrittenSource = await wrapEsmSource(url, specifier)
|
|
165
|
-
logger.debug(`Registered module instrumentation for ${specifier}.`)
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
format: 'module',
|
|
169
|
-
source: rewrittenSource,
|
|
170
|
-
shortCircuit: true
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Helper function for determining which of our Supportability metrics to use for the current loader invocation
|
|
176
|
-
*
|
|
177
|
-
* @param {object} agent
|
|
178
|
-
* instantiation of the New Relic agent
|
|
179
|
-
* @returns {void}
|
|
180
|
-
*/
|
|
181
|
-
function addESMSupportabilityMetrics(agent) {
|
|
182
|
-
if (!agent) {
|
|
183
|
-
return
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (isSupportedVersion()) {
|
|
187
|
-
agent.metrics.getOrCreateMetric(NAMES.FEATURES.ESM.LOADER).incrementCallCount()
|
|
188
|
-
} else {
|
|
189
|
-
logger.warn(
|
|
190
|
-
'New Relic for Node.js ESM loader requires a version of Node >= v16.12.0; your version is %s. Instrumentation will not be registered.',
|
|
191
|
-
process.version
|
|
192
|
-
)
|
|
193
|
-
agent.metrics.getOrCreateMetric(NAMES.FEATURES.ESM.UNSUPPORTED_LOADER).incrementCallCount()
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (customEntryPoint) {
|
|
197
|
-
agent.metrics.getOrCreateMetric(NAMES.FEATURES.ESM.CUSTOM_INSTRUMENTATION).incrementCallCount()
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Rewrites the source code of a ES module we want to instrument.
|
|
203
|
-
* This is done by injecting the ESM shim which proxies every property on the exported
|
|
204
|
-
* module and registers the module with shimmer so instrumentation can be registered properly.
|
|
205
|
-
*
|
|
206
|
-
* Note: this autogenerated code _requires_ that the import have the file:// prefix!
|
|
207
|
-
* Without it, Node.js throws an ERR_INVALID_URL error: you've been warned.
|
|
208
|
-
*
|
|
209
|
-
* @param {string} url the URL returned by the resolve chain
|
|
210
|
-
* @param {string} specifier string identifier in an import statement or import() expression
|
|
211
|
-
* @returns {string} source code rewritten to wrap with our esm-shim
|
|
212
|
-
*/
|
|
213
|
-
async function wrapEsmSource(url, specifier) {
|
|
214
|
-
const pkg = await import(url)
|
|
215
|
-
const props = Object.keys(pkg)
|
|
216
|
-
const trimmedUrl = fileURLToPath(url)
|
|
217
|
-
|
|
218
|
-
return `
|
|
219
|
-
import wrapModule from '${esmShimPath.href}'
|
|
220
|
-
import * as _originalModule from '${url}'
|
|
221
|
-
const _wrappedModule = wrapModule(_originalModule, '${specifier}', '${trimmedUrl}')
|
|
222
|
-
${props
|
|
223
|
-
.map((propName) => {
|
|
224
|
-
return `
|
|
225
|
-
let _${propName} = _wrappedModule.${propName}
|
|
226
|
-
export { _${propName} as ${propName} }`
|
|
227
|
-
})
|
|
228
|
-
.join('\n')}
|
|
229
|
-
`
|
|
230
|
-
}
|
|
6
|
+
export * from 'import-in-the-middle/hook.mjs'
|
package/index.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// Record opening times before loading any other files.
|
|
9
9
|
const preAgentTime = process.uptime()
|
|
10
10
|
const agentStart = Date.now()
|
|
11
|
+
const { isMainThread } = require('worker_threads')
|
|
11
12
|
|
|
12
13
|
// Load unwrapped core now to ensure it gets the freshest properties.
|
|
13
14
|
require('./lib/util/unwrapped-core')
|
|
@@ -16,8 +17,14 @@ const featureFlags = require('./lib/feature_flags').prerelease
|
|
|
16
17
|
const psemver = require('./lib/util/process-version')
|
|
17
18
|
let logger = require('./lib/logger') // Gets re-loaded after initialization.
|
|
18
19
|
const NAMES = require('./lib/metrics/names')
|
|
20
|
+
const isESMSupported = psemver.satisfies('>=16.2.0')
|
|
19
21
|
|
|
20
22
|
const pkgJSON = require('./package.json')
|
|
23
|
+
if (!isMainThread) {
|
|
24
|
+
logger.warn('Using New Relic for Node.js in worker_threads is not supported. Not starting!')
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
21
28
|
logger.info(
|
|
22
29
|
'Using New Relic for Node.js. Agent version: %s; Node version: %s.',
|
|
23
30
|
pkgJSON.version,
|
|
@@ -150,12 +157,12 @@ function createAgent(config) {
|
|
|
150
157
|
}
|
|
151
158
|
|
|
152
159
|
const shimmer = require('./lib/shimmer')
|
|
153
|
-
shimmer.patchModule(agent)
|
|
154
160
|
shimmer.bootstrapInstrumentation(agent)
|
|
155
161
|
|
|
156
162
|
// Check for already loaded modules and warn about them.
|
|
157
163
|
const uninstrumented = require('./lib/uninstrumented')
|
|
158
164
|
uninstrumented.check(shimmer.registeredInstrumentations)
|
|
165
|
+
shimmer.registerHooks(agent)
|
|
159
166
|
|
|
160
167
|
agent.start(function afterStart(error) {
|
|
161
168
|
if (error) {
|
|
@@ -221,22 +228,33 @@ function recordFeatureFlagMetrics(agent) {
|
|
|
221
228
|
* 3. require('newrelic')
|
|
222
229
|
*
|
|
223
230
|
* Then a supportability metric is loaded to decide.
|
|
224
|
-
* Note: We already take care of scenario #2 in newrelic/esm-loader.mjs
|
|
225
231
|
*
|
|
226
232
|
* @param {Agent} agent active NR agent
|
|
227
233
|
*/
|
|
228
234
|
function recordLoaderMetric(agent) {
|
|
229
|
-
const isESM = agent.metrics.getMetric(NAMES.FEATURES.ESM.LOADER)
|
|
230
235
|
let isDashR = false
|
|
231
236
|
|
|
232
237
|
process.execArgv.forEach((arg, index) => {
|
|
233
238
|
if (arg === '-r' && process.execArgv[index + 1] === 'newrelic') {
|
|
234
239
|
agent.metrics.getOrCreateMetric(NAMES.FEATURES.CJS.PRELOAD).incrementCallCount()
|
|
235
240
|
isDashR = true
|
|
241
|
+
} else if (
|
|
242
|
+
(arg === '--loader' || arg === '--experimental-loader') &&
|
|
243
|
+
process.execArgv[index + 1] === 'newrelic/esm-loader.mjs'
|
|
244
|
+
) {
|
|
245
|
+
if (isESMSupported) {
|
|
246
|
+
agent.metrics.getOrCreateMetric(NAMES.FEATURES.ESM.LOADER).incrementCallCount()
|
|
247
|
+
} else {
|
|
248
|
+
agent.metrics.getOrCreateMetric(NAMES.FEATURES.ESM.UNSUPPORTED_LOADER)
|
|
249
|
+
logger.warn(
|
|
250
|
+
'New Relic for Node.js ESM loader requires a version of Node >= v16.12.0; your version is %s. Instrumentation will not be registered.',
|
|
251
|
+
process.version
|
|
252
|
+
)
|
|
253
|
+
}
|
|
236
254
|
}
|
|
237
255
|
})
|
|
238
256
|
|
|
239
|
-
if (!
|
|
257
|
+
if (!isDashR) {
|
|
240
258
|
agent.metrics.getOrCreateMetric(NAMES.FEATURES.CJS.REQUIRE).incrementCallCount()
|
|
241
259
|
}
|
|
242
260
|
}
|
package/lib/config/default.js
CHANGED
|
@@ -738,14 +738,6 @@ defaultConfig.definition = () => ({
|
|
|
738
738
|
formatter: boolean,
|
|
739
739
|
default: true,
|
|
740
740
|
env: 'NEW_RELIC_API_NOTICE_ERROR'
|
|
741
|
-
},
|
|
742
|
-
/**
|
|
743
|
-
* Sets the path to custom ESM instrumentation
|
|
744
|
-
*/
|
|
745
|
-
esm: {
|
|
746
|
-
custom_instrumentation_entrypoint: {
|
|
747
|
-
default: null
|
|
748
|
-
}
|
|
749
741
|
}
|
|
750
742
|
},
|
|
751
743
|
/**
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
|
-
const semver = require('semver')
|
|
9
|
-
|
|
10
8
|
const logger = require('../logger')
|
|
11
9
|
|
|
12
10
|
/**
|
|
@@ -18,16 +16,11 @@ const logger = require('../logger')
|
|
|
18
16
|
* the current configuration.
|
|
19
17
|
*/
|
|
20
18
|
function createContextManager(config) {
|
|
21
|
-
if (config.feature_flag.
|
|
22
|
-
|
|
23
|
-
if (semver.satisfies(process.version, '>=16.4.0')) {
|
|
24
|
-
return createAsyncLocalContextManager(config)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
logger.warn('The AsyncLocalContextManager is only supported on Node version 16.4.0 and later.')
|
|
19
|
+
if (config.feature_flag.legacy_context_manager) {
|
|
20
|
+
return createLegacyContextManager(config)
|
|
28
21
|
}
|
|
29
22
|
|
|
30
|
-
return
|
|
23
|
+
return createAsyncLocalContextManager(config)
|
|
31
24
|
}
|
|
32
25
|
|
|
33
26
|
function createAsyncLocalContextManager(config) {
|
|
@@ -38,13 +31,6 @@ function createAsyncLocalContextManager(config) {
|
|
|
38
31
|
}
|
|
39
32
|
|
|
40
33
|
function createLegacyContextManager(config) {
|
|
41
|
-
if (config.logging.diagnostics) {
|
|
42
|
-
logger.info('Using LegacyDiagnosticContextManager')
|
|
43
|
-
|
|
44
|
-
const LegacyDiagnosticContextManager = require('./diagnostics/legacy-diagnostic-context-manager')
|
|
45
|
-
return new LegacyDiagnosticContextManager(config)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
34
|
logger.info('Using LegacyContextManager')
|
|
49
35
|
|
|
50
36
|
const LegacyContextManager = require('./legacy-context-manager')
|
package/lib/feature_flags.js
CHANGED
|
@@ -10,10 +10,9 @@ exports.prerelease = {
|
|
|
10
10
|
express5: false,
|
|
11
11
|
promise_segments: false,
|
|
12
12
|
reverse_naming_rules: false,
|
|
13
|
-
undici_instrumentation: false,
|
|
14
13
|
undici_async_tracking: true,
|
|
15
14
|
unresolved_promise_cleanup: true,
|
|
16
|
-
|
|
15
|
+
legacy_context_manager: false
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
// flags that are no longer used for released features
|
|
@@ -32,7 +31,9 @@ exports.released = [
|
|
|
32
31
|
'dt_format_w3c',
|
|
33
32
|
'fastify_instrumentation',
|
|
34
33
|
'await_support',
|
|
35
|
-
'certificate_bundle'
|
|
34
|
+
'certificate_bundle',
|
|
35
|
+
'async_local_context',
|
|
36
|
+
'undici_instrumentation'
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
// flags that are no longer used for unreleased features
|
|
@@ -13,41 +13,6 @@ const parseSql = require('../../db/query-parsers/sql')
|
|
|
13
13
|
const RAW_COMMANDS = ['executeRaw', 'queryRaw']
|
|
14
14
|
|
|
15
15
|
const semver = require('semver')
|
|
16
|
-
const { getSchema } = require('@mrleebo/prisma-ast')
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* The library we use to parse the prisma schema retains double quotes around
|
|
20
|
-
* strings, and they need to be stripped
|
|
21
|
-
*
|
|
22
|
-
* @param {string} [str=''] string to strip double-quotes from
|
|
23
|
-
* @returns {string} stripped string
|
|
24
|
-
*/
|
|
25
|
-
function trimQuotes(str = '') {
|
|
26
|
-
return str.match(/"(.*)"/)[1]
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* You can set the connection string in schema as raw string,
|
|
31
|
-
* env var mapping, or an override at client instantiation time.
|
|
32
|
-
*
|
|
33
|
-
* @param {*} url string/object value of url in datsource stanza of schema
|
|
34
|
-
* @param {string} overrideUrl value of url in overrides at client instantiation
|
|
35
|
-
* @returns {string} properly parsed connection url
|
|
36
|
-
*/
|
|
37
|
-
function parseDataModelUrl(url, overrideUrl) {
|
|
38
|
-
let parsedUrl = ''
|
|
39
|
-
|
|
40
|
-
if (overrideUrl) {
|
|
41
|
-
parsedUrl = overrideUrl
|
|
42
|
-
} else if (typeof url === 'string') {
|
|
43
|
-
parsedUrl = trimQuotes(url)
|
|
44
|
-
} else if (url.name && url.name === 'env') {
|
|
45
|
-
const envVar = trimQuotes(url.params[0])
|
|
46
|
-
parsedUrl = process.env[envVar]
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return parsedUrl
|
|
50
|
-
}
|
|
51
16
|
|
|
52
17
|
/**
|
|
53
18
|
* Parses a connection string. Most database engines in prisma are SQL and all
|
|
@@ -148,22 +113,34 @@ function queryParser(query) {
|
|
|
148
113
|
}
|
|
149
114
|
|
|
150
115
|
/**
|
|
151
|
-
* Extracts
|
|
152
|
-
*
|
|
153
|
-
*
|
|
116
|
+
* Extracts connection string from parse datasource config
|
|
117
|
+
*
|
|
118
|
+
* @param {object} datasource active datasource
|
|
119
|
+
* @returns {string} connection string
|
|
120
|
+
*/
|
|
121
|
+
function extractConnectionString(datasource = {}) {
|
|
122
|
+
return process.env[datasource?.url?.fromEnvVar] || datasource?.url?.value
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Extracts the prisma connection information from the engine. This calls an internal
|
|
127
|
+
* package used to parse the prisma schema config.
|
|
154
128
|
*
|
|
155
129
|
* @param {object} client prisma client instance
|
|
156
|
-
* @returns {
|
|
130
|
+
* @returns {object} returns prisma datasource connection configuration { provider, url }
|
|
157
131
|
*/
|
|
158
132
|
function extractPrismaDatasource(client) {
|
|
159
|
-
const {
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
133
|
+
const { get_config: getConfig } = require('@prisma/prisma-fmt-wasm')
|
|
134
|
+
const options = JSON.stringify({
|
|
135
|
+
prismaSchema: client?._engine?.datamodel,
|
|
136
|
+
ignoreEnvVarErrors: true
|
|
137
|
+
})
|
|
138
|
+
const config = JSON.parse(getConfig(options))
|
|
139
|
+
const activeDatasource = config?.datasources?.[0]
|
|
140
|
+
|
|
164
141
|
return {
|
|
165
|
-
provider:
|
|
166
|
-
url
|
|
142
|
+
provider: activeDatasource.provider,
|
|
143
|
+
url: extractConnectionString(activeDatasource)
|
|
167
144
|
}
|
|
168
145
|
}
|
|
169
146
|
|
|
@@ -7,34 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
const url = require('url')
|
|
9
9
|
|
|
10
|
-
module.exports.selfRegister = function selfRegister(shimmer) {
|
|
11
|
-
shimmer.registerInstrumentation({
|
|
12
|
-
moduleName: 'amqplib',
|
|
13
|
-
type: 'message',
|
|
14
|
-
onRequire: instrumentChannelAPI
|
|
15
|
-
})
|
|
16
|
-
shimmer.registerInstrumentation({
|
|
17
|
-
moduleName: 'amqplib/channel_api',
|
|
18
|
-
type: 'message',
|
|
19
|
-
onRequire: instrumentChannelAPI
|
|
20
|
-
})
|
|
21
|
-
shimmer.registerInstrumentation({
|
|
22
|
-
moduleName: 'amqplib/channel_api.js',
|
|
23
|
-
type: 'message',
|
|
24
|
-
onRequire: instrumentChannelAPI
|
|
25
|
-
})
|
|
26
|
-
shimmer.registerInstrumentation({
|
|
27
|
-
moduleName: 'amqplib/callback_api',
|
|
28
|
-
type: 'message',
|
|
29
|
-
onRequire: instrumentCallbackAPI
|
|
30
|
-
})
|
|
31
|
-
shimmer.registerInstrumentation({
|
|
32
|
-
moduleName: 'amqplib/callback_api.js',
|
|
33
|
-
type: 'message',
|
|
34
|
-
onRequire: instrumentCallbackAPI
|
|
35
|
-
})
|
|
36
|
-
}
|
|
37
|
-
|
|
38
10
|
module.exports.instrumentPromiseAPI = instrumentChannelAPI
|
|
39
11
|
module.exports.instrumentCallbackAPI = instrumentCallbackAPI
|
|
40
12
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
const amqplib = require('./amqplib')
|
|
8
|
+
|
|
9
|
+
module.exports = [
|
|
10
|
+
{
|
|
11
|
+
moduleName: 'amqplib/callback_api',
|
|
12
|
+
type: 'message',
|
|
13
|
+
onRequire: amqplib.instrumentCallbackAPI
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
moduleName: 'amqplib/channel_api',
|
|
17
|
+
type: 'message',
|
|
18
|
+
onRequire: amqplib.instrumentPromiseAPI
|
|
19
|
+
}
|
|
20
|
+
]
|
|
@@ -11,7 +11,7 @@ const asyncHooks = require('async_hooks')
|
|
|
11
11
|
module.exports = initialize
|
|
12
12
|
|
|
13
13
|
function initialize(agent, shim) {
|
|
14
|
-
if (agent.config.feature_flag.
|
|
14
|
+
if (!agent.config.feature_flag.legacy_context_manager) {
|
|
15
15
|
logger.debug(
|
|
16
16
|
'New AsyncLocalStorage context enabled. Not enabling manual async_hooks or promise instrumentation'
|
|
17
17
|
)
|
|
@@ -11,9 +11,9 @@ const Timers = require('timers')
|
|
|
11
11
|
module.exports = initialize
|
|
12
12
|
|
|
13
13
|
function initialize(agent, timers, _moduleName, shim) {
|
|
14
|
-
const
|
|
14
|
+
const isLegacyContext = agent.config.feature_flag.legacy_context_manager
|
|
15
15
|
|
|
16
|
-
if (
|
|
16
|
+
if (isLegacyContext) {
|
|
17
17
|
instrumentProcessMethods(shim, process)
|
|
18
18
|
instrumentSetImmediate(shim, [timers, global])
|
|
19
19
|
}
|
|
@@ -11,27 +11,23 @@ const { DESTINATIONS } = require('../../config/attribute-filter')
|
|
|
11
11
|
const DESTINATION = DESTINATIONS.TRANS_EVENT | DESTINATIONS.ERROR_EVENT
|
|
12
12
|
const semver = require('semver')
|
|
13
13
|
|
|
14
|
-
module.exports = function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (semver.gte(grpcVersion, '1.8.0')) {
|
|
19
|
-
const resolvingCall = genericShim.require('./build/src/resolving-call')
|
|
20
|
-
genericShim.wrap(resolvingCall.ResolvingCall.prototype, 'start', wrapStart)
|
|
21
|
-
} else {
|
|
22
|
-
const callStream = genericShim.require('./build/src/call-stream')
|
|
23
|
-
genericShim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
|
|
24
|
-
}
|
|
14
|
+
module.exports.wrapStartResolve = function wrappedClient(shim, resolvingCall) {
|
|
15
|
+
shim.wrap(resolvingCall.ResolvingCall.prototype, 'start', wrapStart)
|
|
16
|
+
}
|
|
25
17
|
|
|
26
|
-
|
|
18
|
+
module.exports.wrapStartCall = function wrappedClient(shim, callStream) {
|
|
19
|
+
shim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports.wrapServer = function wrapServer(shim, server) {
|
|
23
|
+
const grpcVersion = shim.require('./package.json').version
|
|
27
24
|
if (semver.lt(grpcVersion, '1.4.0')) {
|
|
28
25
|
shim.logger.debug('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
|
|
29
26
|
return
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
webFrameworkShim.wrap(server.Server.prototype, 'register', wrapRegister)
|
|
29
|
+
shim.setFramework('gRPC')
|
|
30
|
+
shim.wrap(server.Server.prototype, 'register', wrapRegister)
|
|
35
31
|
}
|
|
36
32
|
|
|
37
33
|
/**
|
|
@@ -7,13 +7,25 @@
|
|
|
7
7
|
const grpc = require('./grpc')
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Need to use nr-hooks style for grpc because we're
|
|
11
|
-
* to register instrumentation.
|
|
10
|
+
* Need to use nr-hooks style for grpc because we're instrumentation a submodule.
|
|
12
11
|
*/
|
|
13
12
|
module.exports = [
|
|
14
13
|
{
|
|
15
|
-
type: '
|
|
16
|
-
moduleName: '@grpc/grpc-js',
|
|
17
|
-
|
|
14
|
+
type: 'generic',
|
|
15
|
+
moduleName: '@grpc/grpc-js/build/src/resolving-call',
|
|
16
|
+
isEsm: true,
|
|
17
|
+
onRequire: grpc.wrapStartResolve
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: 'generic',
|
|
21
|
+
moduleName: '@grpc/grpc-js/build/src/call-stream',
|
|
22
|
+
isEsm: true,
|
|
23
|
+
onRequire: grpc.wrapStartCall
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: 'web-framework',
|
|
27
|
+
moduleName: '@grpc/grpc-js/build/src/server',
|
|
28
|
+
isEsm: true,
|
|
29
|
+
onRequire: grpc.wrapServer
|
|
18
30
|
}
|
|
19
31
|
]
|
|
@@ -7,13 +7,12 @@
|
|
|
7
7
|
const pino = require('./pino')
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Need to use nr-hooks style
|
|
11
|
-
* to register instrumentation.
|
|
10
|
+
* Need to use nr-hooks style because we are instrumenting a submodule.
|
|
12
11
|
*/
|
|
13
12
|
module.exports = [
|
|
14
13
|
{
|
|
15
14
|
type: 'generic',
|
|
16
|
-
moduleName: 'pino',
|
|
17
|
-
|
|
15
|
+
moduleName: 'pino/lib/tools',
|
|
16
|
+
onRequire: pino
|
|
18
17
|
}
|
|
19
18
|
]
|