newrelic 13.0.0 → 13.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 +92 -0
- package/README.md +2 -4
- package/THIRD_PARTY_NOTICES.md +429 -9
- package/esm-loader.mjs +9 -1
- package/index.js +38 -3
- package/lib/agent.js +8 -2
- package/lib/config/build-instrumentation-config.js +3 -0
- package/lib/config/default.js +1397 -1383
- package/lib/config/index.js +15 -19
- package/lib/context-manager/context.js +13 -4
- package/lib/harvester.js +11 -13
- package/lib/instrumentation/@google/genai.js +8 -3
- package/lib/instrumentation/@hapi/hapi.js +4 -6
- package/lib/instrumentation/@nestjs/core.js +12 -14
- package/lib/instrumentation/aws-sdk/v3/bedrock.js +2 -2
- package/lib/instrumentation/core/http-outbound.js +11 -26
- package/lib/instrumentation/kafkajs/producer.js +13 -19
- package/lib/instrumentation/openai.js +2 -2
- package/lib/instrumentation/undici.js +48 -40
- package/lib/instrumentations.js +14 -5
- package/lib/llm-events/aws-bedrock/utils.js +1 -1
- package/lib/metrics/names.js +8 -0
- package/lib/metrics/normalizer.js +1 -3
- package/lib/otel/context.js +18 -14
- package/lib/otel/logs/bootstrap-logs.js +84 -0
- package/lib/otel/logs/no-op-exporter.js +25 -0
- package/lib/otel/logs/normalize-timestamp.js +59 -0
- package/lib/otel/logs/proxying-provider.js +46 -0
- package/lib/otel/logs/severity-to-string.js +56 -0
- package/lib/otel/metrics/bootstrap-metrics.js +1 -1
- package/lib/otel/setup.js +21 -8
- package/lib/patch-module.js +70 -0
- package/lib/serverless/aws-lambda.js +1 -3
- package/lib/shim/webframework-shim/middleware-mounter.js +1 -3
- package/lib/shimmer.js +56 -8
- package/lib/subscriber-configs.js +17 -0
- package/lib/subscribers/application-logs.js +55 -0
- package/lib/subscribers/base.js +177 -0
- package/lib/subscribers/create-config.js +27 -0
- package/lib/subscribers/db-operation.js +21 -0
- package/lib/subscribers/db-query.js +54 -0
- package/lib/subscribers/db.js +57 -0
- package/lib/subscribers/elasticsearch/config.js +49 -0
- package/lib/subscribers/elasticsearch/elasticsearch.js +42 -0
- package/lib/subscribers/elasticsearch/opensearch.js +15 -0
- package/lib/subscribers/elasticsearch/transport.js +14 -0
- package/lib/subscribers/ioredis/config.js +37 -0
- package/lib/subscribers/ioredis/index.js +48 -0
- package/lib/subscribers/mcp-sdk/client-prompt.js +23 -0
- package/lib/subscribers/mcp-sdk/client-resource.js +24 -0
- package/lib/subscribers/mcp-sdk/client-tool.js +23 -0
- package/lib/subscribers/mcp-sdk/client.js +29 -0
- package/lib/subscribers/mcp-sdk/config.js +104 -0
- package/lib/subscribers/pino/config.js +20 -0
- package/lib/subscribers/pino/index.js +102 -0
- package/lib/transaction/index.js +4 -1
- package/lib/transaction/trace/aggregator.js +7 -9
- package/lib/util/camel-case.js +1 -3
- package/lib/util/get-package-version.js +34 -0
- package/lib/util/urltils.js +19 -13
- package/lib/w3c/tracestate.js +3 -3
- package/newrelic.js +10 -0
- package/package.json +9 -5
- package/lib/instrumentation/@elastic/elasticsearch.js +0 -62
- package/lib/instrumentation/@opensearch-project/opensearch.js +0 -66
- package/lib/instrumentation/ioredis.js +0 -50
- package/lib/instrumentation/pino/nr-hooks.js +0 -19
- package/lib/instrumentation/pino/pino.js +0 -168
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
|
|
8
|
+
const Subscriber = require('../base')
|
|
9
|
+
|
|
10
|
+
class McpClientSubscriber extends Subscriber {
|
|
11
|
+
constructor({ agent, logger, channelName }) {
|
|
12
|
+
super({ agent, logger, packageName: '@modelcontextprotocol/sdk', channelName })
|
|
13
|
+
this.events = ['asyncEnd']
|
|
14
|
+
this.segmentName = 'Unknown'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get enabled() {
|
|
18
|
+
return super.enabled && this.config.ai_monitoring.enabled === true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
handler(ctx) {
|
|
22
|
+
return this.createSegment({
|
|
23
|
+
name: this.segmentName,
|
|
24
|
+
ctx
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = McpClientSubscriber
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const mcpClientToolConfig = {
|
|
7
|
+
path: './mcp-sdk/client-tool',
|
|
8
|
+
instrumentations: [
|
|
9
|
+
{
|
|
10
|
+
channelName: 'nr_callTool',
|
|
11
|
+
module: {
|
|
12
|
+
name: '@modelcontextprotocol/sdk',
|
|
13
|
+
versionRange: '>=1.13.0',
|
|
14
|
+
filePath: 'dist/cjs/client/index.js'
|
|
15
|
+
},
|
|
16
|
+
functionQuery: {
|
|
17
|
+
className: 'Client',
|
|
18
|
+
methodName: 'callTool', // must be methodName, not functionName
|
|
19
|
+
kind: 'Async'
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
channelName: 'nr_callTool',
|
|
24
|
+
module: {
|
|
25
|
+
name: '@modelcontextprotocol/sdk',
|
|
26
|
+
versionRange: '>=1.13.0',
|
|
27
|
+
filePath: 'dist/esm/client/index.js'
|
|
28
|
+
},
|
|
29
|
+
functionQuery: {
|
|
30
|
+
className: 'Client',
|
|
31
|
+
methodName: 'callTool',
|
|
32
|
+
kind: 'Async'
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const mcpClientResourceConfig = {
|
|
39
|
+
path: './mcp-sdk/client-resource',
|
|
40
|
+
instrumentations: [
|
|
41
|
+
{
|
|
42
|
+
channelName: 'nr_readResource',
|
|
43
|
+
module: {
|
|
44
|
+
name: '@modelcontextprotocol/sdk',
|
|
45
|
+
versionRange: '>=1.13.0',
|
|
46
|
+
filePath: 'dist/cjs/client/index.js'
|
|
47
|
+
},
|
|
48
|
+
functionQuery: {
|
|
49
|
+
className: 'Client',
|
|
50
|
+
methodName: 'readResource',
|
|
51
|
+
kind: 'Async'
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
channelName: 'nr_readResource',
|
|
56
|
+
module: {
|
|
57
|
+
name: '@modelcontextprotocol/sdk',
|
|
58
|
+
versionRange: '>=1.13.0',
|
|
59
|
+
filePath: 'dist/esm/client/index.js'
|
|
60
|
+
},
|
|
61
|
+
functionQuery: {
|
|
62
|
+
className: 'Client',
|
|
63
|
+
methodName: 'readResource',
|
|
64
|
+
kind: 'Async'
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const mcpClientPromptConfig = {
|
|
71
|
+
path: './mcp-sdk/client-prompt',
|
|
72
|
+
instrumentations: [
|
|
73
|
+
{
|
|
74
|
+
channelName: 'nr_getPrompt',
|
|
75
|
+
module: {
|
|
76
|
+
name: '@modelcontextprotocol/sdk',
|
|
77
|
+
versionRange: '>=1.13.0',
|
|
78
|
+
filePath: 'dist/cjs/client/index.js'
|
|
79
|
+
},
|
|
80
|
+
functionQuery: {
|
|
81
|
+
className: 'Client',
|
|
82
|
+
methodName: 'getPrompt',
|
|
83
|
+
kind: 'Async'
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
channelName: 'nr_getPrompt',
|
|
88
|
+
module: {
|
|
89
|
+
name: '@modelcontextprotocol/sdk',
|
|
90
|
+
versionRange: '>=1.13.0',
|
|
91
|
+
filePath: 'dist/esm/client/index.js'
|
|
92
|
+
},
|
|
93
|
+
functionQuery: {
|
|
94
|
+
className: 'Client',
|
|
95
|
+
methodName: 'getPrompt',
|
|
96
|
+
kind: 'Async'
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = {
|
|
103
|
+
'@modelcontextprotocol/sdk': [mcpClientToolConfig, mcpClientResourceConfig, mcpClientPromptConfig],
|
|
104
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
pino: [{
|
|
8
|
+
path: './pino',
|
|
9
|
+
instrumentations: [
|
|
10
|
+
{
|
|
11
|
+
channelName: 'nr_asJson',
|
|
12
|
+
module: { name: 'pino', versionRange: '>=8.0.0', filePath: 'lib/tools.js' },
|
|
13
|
+
functionQuery: {
|
|
14
|
+
functionName: 'asJson',
|
|
15
|
+
kind: 'Sync'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}]
|
|
20
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
const ApplicationLogsSubscriber = require('../application-logs')
|
|
8
|
+
const { truncate } = require('../../util/application-logging')
|
|
9
|
+
|
|
10
|
+
class PinoSubscriber extends ApplicationLogsSubscriber {
|
|
11
|
+
constructor({ agent, logger }) {
|
|
12
|
+
super({ agent, logger, packageName: 'pino', channelName: 'nr_asJson' })
|
|
13
|
+
this.events = ['end']
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
handler(data, ctx) {
|
|
17
|
+
this.createModuleUsageMetric('pino')
|
|
18
|
+
const { self, arguments: args } = data
|
|
19
|
+
const level = self?.levels?.labels?.[args[2]]
|
|
20
|
+
this.incrementLinesMetric(level)
|
|
21
|
+
const useMergeObj = this.decorateLogLine(data)
|
|
22
|
+
ctx.extras = { useMergeObj }
|
|
23
|
+
return ctx
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
decorateLogLine(data) {
|
|
27
|
+
// Pino log methods accept a singular object (a merging object) that can
|
|
28
|
+
// have a `msg` property for the log message. In such cases, we need to
|
|
29
|
+
// update that log property instead of the second parameter.
|
|
30
|
+
const useMergeObj = data.arguments[1] === undefined && Object.hasOwn(data.arguments[0], 'msg')
|
|
31
|
+
const meta = super.decorateLogLine()
|
|
32
|
+
if (meta) {
|
|
33
|
+
if (useMergeObj === true) {
|
|
34
|
+
data.arguments[0].msg += this.agent.getNRLinkingMetadata()
|
|
35
|
+
} else {
|
|
36
|
+
data.arguments[1] += this.agent.getNRLinkingMetadata()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return useMergeObj
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
end(data) {
|
|
43
|
+
this.forwardLogLine(data)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* reformats error and assigns NR context data
|
|
48
|
+
* to log line
|
|
49
|
+
*
|
|
50
|
+
* @param {object} data event passed to the end handler
|
|
51
|
+
* @param {object} ctx agent context object
|
|
52
|
+
* @returns {function} wrapped log formatter function
|
|
53
|
+
*/
|
|
54
|
+
reformatLogLine(data, ctx) {
|
|
55
|
+
const subscriber = this
|
|
56
|
+
const { self, result: logLine, arguments: args } = data
|
|
57
|
+
const msg = ctx?.extras?.useMergeObj === true ? args[0].msg : args[1]
|
|
58
|
+
const level = self?.levels?.labels?.[args[2]]
|
|
59
|
+
const metadata = this.agent.getLinkingMetadata(true)
|
|
60
|
+
|
|
61
|
+
const agentMeta = Object.assign({}, { timestamp: Date.now() }, metadata)
|
|
62
|
+
// eslint-disable-next-line eqeqeq
|
|
63
|
+
if (msg != undefined) {
|
|
64
|
+
// The spec lists `message` as "MUST" under the required column, but then
|
|
65
|
+
// details that it "MUST be omitted" if the value is "empty". Additionally,
|
|
66
|
+
// if someone has logged only a merging object, and that object contains a
|
|
67
|
+
// message key, we do not want to overwrite their value. See issue 2595.
|
|
68
|
+
agentMeta.message = msg
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A function that gets executed in `_toPayloadSync` of log aggregator.
|
|
73
|
+
* This will parse the serialized log line and then add the relevant NR
|
|
74
|
+
* context metadata and rename the time/msg keys to timestamp/message
|
|
75
|
+
* @returns {object} formatted log line
|
|
76
|
+
*/
|
|
77
|
+
return function formatLogLine() {
|
|
78
|
+
let formattedLog
|
|
79
|
+
try {
|
|
80
|
+
formattedLog = JSON.parse(logLine)
|
|
81
|
+
} catch (err) {
|
|
82
|
+
subscriber.logger.error('Failed to parse log line as json: %s', err.message)
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (formattedLog.err) {
|
|
87
|
+
formattedLog['error.message'] = truncate(formattedLog.err.message)
|
|
88
|
+
formattedLog['error.stack'] = truncate(formattedLog.err.stack)
|
|
89
|
+
formattedLog['error.class'] = formattedLog.err.type
|
|
90
|
+
delete formattedLog.err
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
Object.assign(formattedLog, agentMeta)
|
|
94
|
+
formattedLog.level = level
|
|
95
|
+
delete formattedLog.time
|
|
96
|
+
delete formattedLog.msg
|
|
97
|
+
return formattedLog
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = PinoSubscriber
|
package/lib/transaction/index.js
CHANGED
|
@@ -409,7 +409,10 @@ function finalizeNameFromUri(requestURL, statusCode) {
|
|
|
409
409
|
)
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
-
this.
|
|
412
|
+
if (!this.agent.config.url_obfuscation.enabled) {
|
|
413
|
+
this.url = urltils.scrub(requestURL)
|
|
414
|
+
}
|
|
415
|
+
|
|
413
416
|
this.statusCode = statusCode
|
|
414
417
|
this.name = this.getFullName()
|
|
415
418
|
|
|
@@ -190,16 +190,14 @@ class TransactionTraceAggregator extends TraceAggregator {
|
|
|
190
190
|
return callback(null, traces)
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
const tracePromises = traces.map((trace) => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
resolve(data)
|
|
200
|
-
})
|
|
193
|
+
const tracePromises = traces.map((trace) => new Promise((resolve, reject) => {
|
|
194
|
+
trace.generateJSON((err, data) => {
|
|
195
|
+
if (err) {
|
|
196
|
+
reject(err)
|
|
197
|
+
}
|
|
198
|
+
resolve(data)
|
|
201
199
|
})
|
|
202
|
-
})
|
|
200
|
+
}))
|
|
203
201
|
|
|
204
202
|
try {
|
|
205
203
|
const encodedTraces = await Promise.all(tracePromises)
|
package/lib/util/camel-case.js
CHANGED
|
@@ -19,8 +19,6 @@ const words = (string) => string.match(wordPattern) || []
|
|
|
19
19
|
*/
|
|
20
20
|
module.exports = function toCamelCase(string) {
|
|
21
21
|
return words(string)
|
|
22
|
-
.map((word, index) =>
|
|
23
|
-
index === 0 ? word.toLowerCase() : word.slice(0, 1).toUpperCase() + word.slice(1)
|
|
24
|
-
)
|
|
22
|
+
.map((word, index) => (index === 0 ? word.toLowerCase() : word.slice(0, 1).toUpperCase() + word.slice(1)))
|
|
25
23
|
.join('')
|
|
26
24
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
const { readFileSync } = require('node:fs')
|
|
8
|
+
const { join } = require('node:path')
|
|
9
|
+
|
|
10
|
+
const packageVersions = new Map()
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Retrieves the version of a package from its package.json file.
|
|
14
|
+
* If the package.json file cannot be read, it defaults to the Node.js version.
|
|
15
|
+
* @param {string} baseDir - The base directory where the package.json file is located.
|
|
16
|
+
* @returns {string} The version of the package or the Node.js version if the package.json cannot be read.
|
|
17
|
+
*/
|
|
18
|
+
function getPackageVersion(baseDir) {
|
|
19
|
+
if (packageVersions.has(baseDir)) {
|
|
20
|
+
return packageVersions.get(baseDir)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const packageJsonPath = join(baseDir, 'package.json')
|
|
25
|
+
const jsonFile = readFileSync(packageJsonPath)
|
|
26
|
+
const { version } = JSON.parse(jsonFile)
|
|
27
|
+
packageVersions.set(baseDir, version)
|
|
28
|
+
return version
|
|
29
|
+
} catch {
|
|
30
|
+
return process.version.slice(1)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = getPackageVersion
|
package/lib/util/urltils.js
CHANGED
|
@@ -83,7 +83,7 @@ module.exports = {
|
|
|
83
83
|
* 2. Strip off session trackers after ';' (a New Relic convention)
|
|
84
84
|
* 3. Remove trailing slash.
|
|
85
85
|
*
|
|
86
|
-
* @param {string} requestURL The URL fragment to be scrubbed.
|
|
86
|
+
* @param {string|url.URL} requestURL The URL fragment to be scrubbed.
|
|
87
87
|
* @returns {string} The cleaned URL.
|
|
88
88
|
*/
|
|
89
89
|
scrub: function scrub(requestURL) {
|
|
@@ -116,28 +116,34 @@ module.exports = {
|
|
|
116
116
|
* variable. Some web frameworks behave this way as well, so don't lose
|
|
117
117
|
* information.
|
|
118
118
|
*
|
|
119
|
-
* @param {string} requestURL The URL to be parsed.
|
|
119
|
+
* @param {string|url.URL} requestURL The URL to be parsed.
|
|
120
120
|
* @returns {object} The parameters parsed from the request
|
|
121
121
|
*/
|
|
122
122
|
parseParameters: function parseParameters(requestURL) {
|
|
123
123
|
let parsed = requestURL
|
|
124
|
+
const parameters = Object.create(null)
|
|
125
|
+
|
|
126
|
+
function addParam(key, value, raw) {
|
|
127
|
+
if (value === '' && raw.indexOf(key + '=') === -1) {
|
|
128
|
+
parameters[key] = true
|
|
129
|
+
} else {
|
|
130
|
+
parameters[key] = value
|
|
131
|
+
}
|
|
132
|
+
}
|
|
124
133
|
|
|
125
134
|
if (typeof requestURL === 'string') {
|
|
126
135
|
parsed = url.parse(requestURL, true)
|
|
127
136
|
}
|
|
128
137
|
|
|
129
|
-
const parameters = Object.create(null)
|
|
130
|
-
|
|
131
138
|
if (parsed.query) {
|
|
132
|
-
const
|
|
139
|
+
for (const key of Object.keys(parsed.query)) {
|
|
140
|
+
addParam(key, parsed.query[key], parsed.path || '')
|
|
141
|
+
}
|
|
142
|
+
}
|
|
133
143
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
parameters[key] = true
|
|
138
|
-
} else {
|
|
139
|
-
parameters[key] = parsed.query[key]
|
|
140
|
-
}
|
|
144
|
+
if (parsed.searchParams) {
|
|
145
|
+
for (const [key, value] of parsed.searchParams) {
|
|
146
|
+
addParam(key, value, parsed.pathname || '')
|
|
141
147
|
}
|
|
142
148
|
}
|
|
143
149
|
|
|
@@ -148,7 +154,7 @@ module.exports = {
|
|
|
148
154
|
* Performs the logic of `urltils.scrub` and `urltils.parseParameters` with
|
|
149
155
|
* only a single parse of the given URL.
|
|
150
156
|
*
|
|
151
|
-
* @param {string} requestURL - The URL to scrub and extra parameters from.
|
|
157
|
+
* @param {string|url.URL} requestURL - The URL to scrub and extra parameters from.
|
|
152
158
|
* @returns {object} An object containing the scrubbed url at `.path` and the
|
|
153
159
|
* parsed parameters at `.parameters`.
|
|
154
160
|
*/
|
package/lib/w3c/tracestate.js
CHANGED
|
@@ -69,7 +69,7 @@ class Tracestate {
|
|
|
69
69
|
const vendors = new Set()
|
|
70
70
|
const listMembers = new Map()
|
|
71
71
|
for (const listMember of kvPairs) {
|
|
72
|
-
const parts = listMember.split('=', 2).filter(v => Boolean(v))
|
|
72
|
+
const parts = listMember.split('=', 2).filter((v) => Boolean(v))
|
|
73
73
|
if (parts.length !== 2) {
|
|
74
74
|
this.#logger.debug('Unable to parse tracestate list members.')
|
|
75
75
|
this.#agent.recordSupportability('TraceContext/TraceState/Parse/Exception/ListMember')
|
|
@@ -94,7 +94,7 @@ class Tracestate {
|
|
|
94
94
|
let intrinsics
|
|
95
95
|
if (nrTraceStateValue) {
|
|
96
96
|
intrinsics = new TracestateIntrinsics()
|
|
97
|
-
const values = nrTraceStateValue.split('-').map(v => v === '' ? null : v)
|
|
97
|
+
const values = nrTraceStateValue.split('-').map((v) => (v === '' ? null : v))
|
|
98
98
|
intrinsics.version = values[0]
|
|
99
99
|
intrinsics.parentType = values[1]
|
|
100
100
|
intrinsics.accountId = values[2]
|
|
@@ -150,7 +150,7 @@ class Tracestate {
|
|
|
150
150
|
if (typeof header !== 'string') {
|
|
151
151
|
throw Error('header value must be a string')
|
|
152
152
|
}
|
|
153
|
-
const kvPairs = header.split(',').map(item => item.trim()).filter(Boolean)
|
|
153
|
+
const kvPairs = header.split(',').map((item) => item.trim()).filter(Boolean)
|
|
154
154
|
return new Tracestate({ agent, logger, kvPairs })
|
|
155
155
|
}
|
|
156
156
|
|
package/newrelic.js
CHANGED
|
@@ -22,6 +22,16 @@ exports.config = {
|
|
|
22
22
|
*/
|
|
23
23
|
level: 'info'
|
|
24
24
|
},
|
|
25
|
+
/**
|
|
26
|
+
* This provides instrumentation for `setTimeout` and `setInterval` calls.
|
|
27
|
+
* We recommend you disable this instrumentation as it does not not provide
|
|
28
|
+
* much value and creates a lot of unncessary TraceSegments/Span events.
|
|
29
|
+
*/
|
|
30
|
+
instrumentation: {
|
|
31
|
+
timers: {
|
|
32
|
+
enabled: false
|
|
33
|
+
}
|
|
34
|
+
},
|
|
25
35
|
/**
|
|
26
36
|
* When true, all request headers except for those listed in attributes.exclude
|
|
27
37
|
* will be captured for all traces, unless otherwise specified in a destination's
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.2.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"contributors": [
|
|
@@ -172,11 +172,11 @@
|
|
|
172
172
|
"services": "DOCKER_PLATFORM=linux/$(uname -m) docker compose up -d --wait",
|
|
173
173
|
"services:start": "npm run services",
|
|
174
174
|
"services:stop": "docker compose down",
|
|
175
|
-
"smoke": "time borp --timeout 180000 --reporter ./test/lib/test-reporter.mjs 'test/smoke/**/*.
|
|
175
|
+
"smoke": "time borp --timeout 180000 --reporter ./test/lib/test-reporter.mjs 'test/smoke/**/*.test.js'",
|
|
176
176
|
"sub-install": "node test/bin/install_sub_deps",
|
|
177
177
|
"test": "npm run integration && npm run unit",
|
|
178
178
|
"third-party-updates": "oss third-party manifest --includeOptDeps && oss third-party notices --includeOptDeps && git add THIRD_PARTY_NOTICES.md third_party_manifest.json",
|
|
179
|
-
"unit": "rm -f newrelic_agent.log && time c8 -o ./coverage/unit borp --timeout 180000 --reporter ./test/lib/test-reporter.mjs 'test/unit/**/*.test.js'",
|
|
179
|
+
"unit": "rm -f newrelic_agent.log && time c8 -o ./coverage/unit borp --timeout 180000 --reporter ./test/lib/test-reporter.mjs 'test/unit/**/*.test.{js,mjs}'",
|
|
180
180
|
"unit:scripts": "time c8 -o ./coverage/scripts-unit borp --reporter ./test/lib/test-reporter.mjs 'bin/test/*.test.js'",
|
|
181
181
|
"update-cross-agent-tests": "./bin/update-cats.sh",
|
|
182
182
|
"versioned-tests": "./bin/run-versioned-tests.sh",
|
|
@@ -201,13 +201,16 @@
|
|
|
201
201
|
"#test/assert": "./test/lib/custom-assertions/index.js"
|
|
202
202
|
},
|
|
203
203
|
"dependencies": {
|
|
204
|
+
"@apm-js-collab/code-transformer": "^0.6.0",
|
|
204
205
|
"@grpc/grpc-js": "^1.13.2",
|
|
205
206
|
"@grpc/proto-loader": "^0.7.5",
|
|
206
207
|
"@newrelic/security-agent": "^2.4.2",
|
|
207
208
|
"@opentelemetry/api": "^1.9.0",
|
|
209
|
+
"@opentelemetry/api-logs": "^0.203.0",
|
|
208
210
|
"@opentelemetry/core": "^2.0.0",
|
|
209
211
|
"@opentelemetry/exporter-metrics-otlp-proto": "^0.201.1",
|
|
210
212
|
"@opentelemetry/resources": "^2.0.1",
|
|
213
|
+
"@opentelemetry/sdk-logs": "^0.203.0",
|
|
211
214
|
"@opentelemetry/sdk-metrics": "^2.0.1",
|
|
212
215
|
"@opentelemetry/sdk-trace-base": "^2.0.0",
|
|
213
216
|
"@tyriar/fibonacci-heap": "^2.0.7",
|
|
@@ -232,7 +235,7 @@
|
|
|
232
235
|
"@aws-sdk/s3-request-presigner": "^3.556.0",
|
|
233
236
|
"@koa/router": "^12.0.1",
|
|
234
237
|
"@matteo.collina/tspl": "^0.1.1",
|
|
235
|
-
"@newrelic/eslint-config": "^0.
|
|
238
|
+
"@newrelic/eslint-config": "^0.6.0",
|
|
236
239
|
"@newrelic/newrelic-oss-cli": "^0.1.2",
|
|
237
240
|
"@newrelic/test-utilities": "^10.0.0",
|
|
238
241
|
"@octokit/rest": "^18.0.15",
|
|
@@ -269,7 +272,8 @@
|
|
|
269
272
|
"self-cert": "^2.0.0",
|
|
270
273
|
"should": "*",
|
|
271
274
|
"sinon": "^5.1.1",
|
|
272
|
-
"superagent": "^9.0.1"
|
|
275
|
+
"superagent": "^9.0.1",
|
|
276
|
+
"testdouble": "^3.20.2"
|
|
273
277
|
},
|
|
274
278
|
"repository": {
|
|
275
279
|
"type": "git",
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use strict'
|
|
7
|
-
|
|
8
|
-
const { QuerySpec } = require('../../shim/specs')
|
|
9
|
-
const semver = require('semver')
|
|
10
|
-
const { queryParser } = require('../../db/query-parsers/elasticsearch')
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Instruments the `@elastic/elasticsearch` module. This function is
|
|
14
|
-
* passed to `onRequire` when instantiating instrumentation.
|
|
15
|
-
*
|
|
16
|
-
* @param {object} _agent New Relic agent
|
|
17
|
-
* @param {object} elastic resolved module
|
|
18
|
-
* @param {string} _moduleName string representation of require/import path
|
|
19
|
-
* @param {object} shim New Relic shim
|
|
20
|
-
* @returns {void}
|
|
21
|
-
*/
|
|
22
|
-
module.exports = function initialize(_agent, elastic, _moduleName, shim) {
|
|
23
|
-
const pkgVersion = shim.pkgVersion
|
|
24
|
-
if (semver.lt(pkgVersion, '7.16.0')) {
|
|
25
|
-
shim &&
|
|
26
|
-
shim.logger.debug(
|
|
27
|
-
`ElasticSearch support is for versions 7.16.0 and above. Not instrumenting ${pkgVersion}.`
|
|
28
|
-
)
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
shim.setDatastore(shim.ELASTICSEARCH)
|
|
33
|
-
shim.setParser(queryParser)
|
|
34
|
-
|
|
35
|
-
shim.recordQuery(elastic.Transport.prototype, 'request', function wrapQuery(shim, _, __, args) {
|
|
36
|
-
const ctx = this
|
|
37
|
-
return new QuerySpec({
|
|
38
|
-
query: JSON.stringify(args?.[0]),
|
|
39
|
-
promise: true,
|
|
40
|
-
opaque: true,
|
|
41
|
-
inContext: function inContext() {
|
|
42
|
-
getConnection.call(ctx, shim)
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Convenience function for deriving connection information from
|
|
50
|
-
* elasticsearch
|
|
51
|
-
*
|
|
52
|
-
* @param {object} shim The New Relic datastore-shim
|
|
53
|
-
* @returns {Function} captureInstanceAttributes method of shim
|
|
54
|
-
*/
|
|
55
|
-
function getConnection(shim) {
|
|
56
|
-
const connectionPool = this.connectionPool.connections[0]
|
|
57
|
-
const host = connectionPool.url.host.split(':')
|
|
58
|
-
const port = connectionPool.url.port || host?.[1]
|
|
59
|
-
return shim.captureInstanceAttributes(host[0], port)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
module.exports.getConnection = getConnection
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2024 New Relic Corporation. All rights reserved.
|
|
3
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use strict'
|
|
7
|
-
|
|
8
|
-
const { QuerySpec } = require('../../shim/specs')
|
|
9
|
-
const semver = require('semver')
|
|
10
|
-
const { queryParser } = require('../../db/query-parsers/elasticsearch')
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Instruments the `@opensearch-project/opensearch` module. This function is
|
|
14
|
-
* passed to `onRequire` when instantiating instrumentation.
|
|
15
|
-
*
|
|
16
|
-
* @param {object} _agent New Relic agent
|
|
17
|
-
* @param {object} opensearch resolved module
|
|
18
|
-
* @param {string} _moduleName string representation of require/import path
|
|
19
|
-
* @param {object} shim New Relic shim
|
|
20
|
-
* @returns {void}
|
|
21
|
-
*/
|
|
22
|
-
module.exports = function initialize(_agent, opensearch, _moduleName, shim) {
|
|
23
|
-
const pkgVersion = shim.pkgVersion
|
|
24
|
-
if (semver.lt(pkgVersion, '2.1.0')) {
|
|
25
|
-
shim &&
|
|
26
|
-
shim.logger.debug(
|
|
27
|
-
`Opensearch support is for versions 2.1.0 and above. Not instrumenting ${pkgVersion}.`
|
|
28
|
-
)
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
shim.setDatastore(shim.OPENSEARCH)
|
|
33
|
-
shim.setParser(queryParser)
|
|
34
|
-
|
|
35
|
-
shim.recordQuery(
|
|
36
|
-
opensearch.Transport.prototype,
|
|
37
|
-
'request',
|
|
38
|
-
function wrapQuery(shim, _, __, args) {
|
|
39
|
-
const ctx = this
|
|
40
|
-
return new QuerySpec({
|
|
41
|
-
query: JSON.stringify(args?.[0]),
|
|
42
|
-
promise: true,
|
|
43
|
-
opaque: true,
|
|
44
|
-
inContext: function inContext() {
|
|
45
|
-
getConnection.call(ctx, shim)
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Convenience function for deriving connection information from
|
|
54
|
-
* opensearch
|
|
55
|
-
*
|
|
56
|
-
* @param {object} shim The New Relic datastore-shim
|
|
57
|
-
* @returns {Function} captureInstanceAttributes method of shim
|
|
58
|
-
*/
|
|
59
|
-
function getConnection(shim) {
|
|
60
|
-
const connectionPool = this.connectionPool.connections[0]
|
|
61
|
-
const host = connectionPool.url.host.split(':')
|
|
62
|
-
const port = connectionPool.url.port || host?.[1]
|
|
63
|
-
return shim.captureInstanceAttributes(host[0], port)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
module.exports.getConnection = getConnection
|