newrelic 13.2.0 → 13.3.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 +44 -0
- package/THIRD_PARTY_NOTICES.md +219 -8
- package/api.js +1 -1
- package/esm-loader.mjs +6 -1
- package/lib/instrumentation/@azure/functions.js +8 -46
- package/lib/instrumentation/core/http-outbound.js +26 -10
- package/lib/instrumentation/core/http.js +20 -118
- package/lib/instrumentations.js +0 -14
- package/lib/otel/logs/index.js +80 -0
- package/lib/otel/metrics/index.js +119 -0
- package/lib/otel/setup-signal.js +37 -0
- package/lib/otel/setup.js +15 -25
- package/lib/otel/{attr-reconciler.js → traces/attr-reconciler.js} +2 -2
- package/lib/otel/{exception-mapping.js → traces/exception-mapping.js} +1 -1
- package/lib/otel/traces/index.js +40 -0
- package/lib/otel/{rules.js → traces/rules.js} +1 -1
- package/lib/otel/{segment-synthesis.js → traces/segment-synthesis.js} +5 -4
- package/lib/otel/{segments → traces/segments}/consumer.js +4 -4
- package/lib/otel/{segments → traces/segments}/database.js +6 -6
- package/lib/otel/{segments → traces/segments}/http-external.js +11 -8
- package/lib/otel/traces/segments/index.js +22 -0
- package/lib/otel/{segments → traces/segments}/internal.js +1 -1
- package/lib/otel/{segments → traces/segments}/producer.js +2 -2
- package/lib/otel/{segments → traces/segments}/server.js +4 -4
- package/lib/otel/{segments → traces/segments}/utils.js +1 -1
- package/lib/otel/{span-processor.js → traces/span-processor.js} +13 -19
- package/lib/otel/{transformation-rules.json → traces/transformation-rules.json} +3 -2
- package/lib/otel/{utils.js → traces/utils.js} +2 -1
- package/lib/serverless/aws-lambda.js +7 -68
- package/lib/shim/webframework-shim/middleware.js +1 -1
- package/lib/shimmer.js +2 -2
- package/lib/subscriber-configs.js +2 -1
- package/lib/subscribers/base.js +30 -0
- package/lib/subscribers/elasticsearch/config.js +1 -0
- package/lib/subscribers/mcp-sdk/client-request.js +66 -0
- package/lib/subscribers/mcp-sdk/config.js +13 -75
- package/lib/subscribers/openai/base.js +21 -0
- package/lib/subscribers/openai/chat-responses.js +15 -0
- package/lib/subscribers/openai/chat.js +85 -0
- package/lib/subscribers/openai/client.js +31 -0
- package/lib/subscribers/openai/config.js +85 -0
- package/lib/subscribers/openai/embeddings.js +54 -0
- package/lib/subscribers/openai/utils.js +349 -0
- package/lib/transaction/index.js +157 -56
- package/lib/transaction/trace/segment.js +5 -2
- package/lib/util/urltils.js +158 -192
- package/lib/utilization/common.js +0 -6
- package/package.json +3 -4
- package/lib/instrumentation/openai.js +0 -482
- package/lib/otel/logs/bootstrap-logs.js +0 -84
- package/lib/otel/metrics/bootstrap-metrics.js +0 -117
- package/lib/otel/segments/index.js +0 -22
- package/lib/patch-module.js +0 -70
- package/lib/subscribers/mcp-sdk/client-prompt.js +0 -23
- package/lib/subscribers/mcp-sdk/client-resource.js +0 -24
- package/lib/subscribers/mcp-sdk/client-tool.js +0 -23
- package/lib/subscribers/mcp-sdk/client.js +0 -29
- /package/lib/otel/{constants.js → traces/constants.js} +0 -0
- /package/lib/otel/{sampler.js → traces/sampler.js} +0 -0
package/lib/util/urltils.js
CHANGED
|
@@ -5,9 +5,17 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
|
-
const url = require('url')
|
|
9
8
|
const logger = require('../logger').child({ component: 'urltils' })
|
|
9
|
+
/**
|
|
10
|
+
* Utility functions for enforcing New Relic naming conditions on URLs,
|
|
11
|
+
* and extracting and setting parameters on traces / web trace segments.
|
|
12
|
+
*/
|
|
10
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Dictionary whose keys are all synonyms for localhost.
|
|
16
|
+
*
|
|
17
|
+
* @constant
|
|
18
|
+
*/
|
|
11
19
|
const LOCALHOST_NAMES = {
|
|
12
20
|
localhost: true,
|
|
13
21
|
'127.0.0.1': true,
|
|
@@ -19,214 +27,161 @@ const LOCALHOST_NAMES = {
|
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
30
|
+
* Checks if the given name is in the dictionary of localhost names.
|
|
31
|
+
*
|
|
32
|
+
* @param {string} host - The hostname to lookup.
|
|
33
|
+
* @returns {boolean} - True if the given hostname is a synonym for localhost.
|
|
24
34
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*
|
|
29
|
-
* @constant
|
|
30
|
-
*/
|
|
31
|
-
LOCALHOST_NAMES,
|
|
35
|
+
function isLocalhost(host) {
|
|
36
|
+
return LOCALHOST_NAMES[host] != null
|
|
37
|
+
}
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Returns true if the status code is configured to be expected
|
|
69
|
-
*
|
|
70
|
-
* @param {Config} config The configuration containing the error list.
|
|
71
|
-
* @param {string} code The HTTP status code to check.
|
|
72
|
-
* @returns {boolean} Whether the status code is expected.
|
|
73
|
-
*/
|
|
74
|
-
isExpectedError: function isExpectedError(config, code) {
|
|
75
|
-
return isExpectedStatusCodeForErrors(config, code)
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Get back the pieces of the URL that New Relic cares about. Apply these
|
|
80
|
-
* restrictions, in order:
|
|
81
|
-
*
|
|
82
|
-
* 1. Ensure that after parsing the URL, there's at least '/'
|
|
83
|
-
* 2. Strip off session trackers after ';' (a New Relic convention)
|
|
84
|
-
* 3. Remove trailing slash.
|
|
85
|
-
*
|
|
86
|
-
* @param {string|url.URL} requestURL The URL fragment to be scrubbed.
|
|
87
|
-
* @returns {string} The cleaned URL.
|
|
88
|
-
*/
|
|
89
|
-
scrub: function scrub(requestURL) {
|
|
90
|
-
if (typeof requestURL === 'string') {
|
|
91
|
-
requestURL = url.parse(requestURL)
|
|
92
|
-
}
|
|
39
|
+
/**
|
|
40
|
+
* This was handed down from the prototype as the canonical list of status
|
|
41
|
+
* codes that short-circuit naming and normalization. The agent can be
|
|
42
|
+
* configured to mark HTTP status codes as not being errors.
|
|
43
|
+
*
|
|
44
|
+
* @param {Config} config The configuration containing the error list.
|
|
45
|
+
* @param {string} code The HTTP status code to check.
|
|
46
|
+
* @returns {boolean} Whether the status code should be ignored.
|
|
47
|
+
*/
|
|
48
|
+
function isError(config, code) {
|
|
49
|
+
return code >= 400 && !isIgnoredStatusCodeForErrors(config, code)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns true if the status code is an HTTP error, and it is configured to be ignored.
|
|
54
|
+
*
|
|
55
|
+
* @param {Config} config The configuration containing the error list.
|
|
56
|
+
* @param {string} code The HTTP status code to check.
|
|
57
|
+
* @returns {boolean} Whether the status code should be ignored.
|
|
58
|
+
*/
|
|
59
|
+
function isIgnoredError(config, code) {
|
|
60
|
+
return code >= 400 && isIgnoredStatusCodeForErrors(config, code)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Returns true if the status code is configured to be expected
|
|
65
|
+
*
|
|
66
|
+
* @param {Config} config The configuration containing the error list.
|
|
67
|
+
* @param {string} code The HTTP status code to check.
|
|
68
|
+
* @returns {boolean} Whether the status code is expected.
|
|
69
|
+
*/
|
|
70
|
+
function isExpectedError(config, code) {
|
|
71
|
+
return isExpectedStatusCodeForErrors(config, code)
|
|
72
|
+
}
|
|
93
73
|
|
|
94
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Get back the pieces of the URL that New Relic cares about. Apply these
|
|
76
|
+
* restrictions, in order:
|
|
77
|
+
*
|
|
78
|
+
* 1. Ensure that after parsing the URL, there's at least '/'
|
|
79
|
+
* 2. Strip off session trackers after ';' (a New Relic convention)
|
|
80
|
+
* 3. Remove trailing slash.
|
|
81
|
+
*
|
|
82
|
+
* @param {url.URL} requestURL The URL fragment to be scrubbed.
|
|
83
|
+
* @returns {string} The cleaned URL.
|
|
84
|
+
* @private
|
|
85
|
+
*/
|
|
86
|
+
function scrub(requestURL) {
|
|
87
|
+
let path = requestURL.pathname
|
|
95
88
|
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
if (path) {
|
|
90
|
+
path = path.split(';')[0]
|
|
98
91
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
path = '/'
|
|
92
|
+
if (path !== '/' && path.charAt(path.length - 1) === '/') {
|
|
93
|
+
path = path.substring(0, path.length - 1)
|
|
104
94
|
}
|
|
95
|
+
} else {
|
|
96
|
+
path = '/'
|
|
97
|
+
}
|
|
105
98
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
99
|
+
return path
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Extract query parameters, dealing with bare parameters and parameters with
|
|
104
|
+
* no value as appropriate:
|
|
105
|
+
*
|
|
106
|
+
* 'var1&var2=value' is not necessarily the same as 'var1=&var2=value'
|
|
107
|
+
*
|
|
108
|
+
* In my world, one is an assertion of presence, and the other is an empty
|
|
109
|
+
* variable. Some web frameworks behave this way as well, so don't lose
|
|
110
|
+
* information.
|
|
111
|
+
*
|
|
112
|
+
* @param {url.URL} requestURL The URL to be parsed.
|
|
113
|
+
* @returns {object} The parameters parsed from the request
|
|
114
|
+
* @private
|
|
115
|
+
*/
|
|
116
|
+
function parseParameters(requestURL) {
|
|
117
|
+
const parsed = requestURL
|
|
118
|
+
const parameters = Object.create(null)
|
|
119
|
+
|
|
120
|
+
if (parsed.searchParams) {
|
|
121
|
+
for (const [key, value] of parsed.searchParams) {
|
|
122
|
+
if (value === '' && parsed.search.indexOf(`?${key}=`) === -1 && parsed.search.indexOf(`&${key}=`) === -1) {
|
|
128
123
|
parameters[key] = true
|
|
129
124
|
} else {
|
|
130
125
|
parameters[key] = value
|
|
131
126
|
}
|
|
132
127
|
}
|
|
128
|
+
}
|
|
133
129
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
130
|
+
return parameters
|
|
131
|
+
}
|
|
137
132
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Performs the logic of `scrub` and `parseParameters` with
|
|
135
|
+
* only a single parse of the given URL.
|
|
136
|
+
*
|
|
137
|
+
* @param {url.URL} requestURL - The URL to scrub and extra parameters from.
|
|
138
|
+
* @returns {object} An object containing the scrubbed url at `.path` and the
|
|
139
|
+
* parsed parameters at `.parameters` and `.protocol`.
|
|
140
|
+
*/
|
|
141
|
+
function scrubAndParseParameters(requestURL) {
|
|
142
|
+
return {
|
|
143
|
+
protocol: requestURL.protocol,
|
|
144
|
+
path: scrub(requestURL),
|
|
145
|
+
parameters: parseParameters(requestURL)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
143
148
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Obfuscates path parameters with regex from config
|
|
151
|
+
*
|
|
152
|
+
* @param {Config} config The configuration containing the regex
|
|
153
|
+
* @param {string} path The path to be obfuscated
|
|
154
|
+
* @returns {string} The obfuscated path or the original path
|
|
155
|
+
*/
|
|
156
|
+
function obfuscatePath(config, path) {
|
|
157
|
+
const { enabled, regex } = config.url_obfuscation
|
|
158
|
+
if (typeof path !== 'string' || !enabled || !regex) {
|
|
159
|
+
return path
|
|
160
|
+
}
|
|
149
161
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
* parsed parameters at `.parameters`.
|
|
160
|
-
*/
|
|
161
|
-
scrubAndParseParameters: function scrubAndParseParameters(requestURL) {
|
|
162
|
-
if (typeof requestURL === 'string') {
|
|
163
|
-
requestURL = url.parse(requestURL, true)
|
|
164
|
-
}
|
|
165
|
-
return {
|
|
166
|
-
protocol: requestURL.protocol,
|
|
167
|
-
path: this.scrub(requestURL),
|
|
168
|
-
parameters: this.parseParameters(requestURL)
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Obfuscates path parameters with regex from config
|
|
174
|
-
*
|
|
175
|
-
* @param {Config} config The configuration containing the regex
|
|
176
|
-
* @param {string} path The path to be obfuscated
|
|
177
|
-
* @returns {string} The obfuscated path or the original path
|
|
178
|
-
*/
|
|
179
|
-
obfuscatePath: function obfuscatePath(config, path) {
|
|
180
|
-
const { enabled, regex } = config.url_obfuscation
|
|
181
|
-
if (typeof path !== 'string' || !enabled || !regex) {
|
|
182
|
-
return path
|
|
183
|
-
}
|
|
162
|
+
const { pattern, flags = '', replacement = '' } = regex
|
|
163
|
+
try {
|
|
164
|
+
const regexPattern = new RegExp(pattern, flags)
|
|
165
|
+
return path.replace(regexPattern, replacement)
|
|
166
|
+
} catch {
|
|
167
|
+
logger.warn('Invalid regular expression for url_obfuscation.regex.pattern', pattern)
|
|
168
|
+
return path
|
|
169
|
+
}
|
|
170
|
+
}
|
|
184
171
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
* including parameters set to null or undefined.
|
|
199
|
-
*
|
|
200
|
-
* @param {object} source Parameters to be copied (not changed).
|
|
201
|
-
* @param {object} destination Dictionary to which parameters are copied
|
|
202
|
-
* (mutated in place).
|
|
203
|
-
*/
|
|
204
|
-
copyParameters: function copyParameters(source, destination) {
|
|
205
|
-
if (source && destination) {
|
|
206
|
-
const keys = Object.keys(source)
|
|
207
|
-
for (let i = 0; i < keys.length; i++) {
|
|
208
|
-
const key = keys[i]
|
|
209
|
-
if (!(key in destination)) {
|
|
210
|
-
destination[key] = source[key]
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Copy a set of request parameters from one object to another.
|
|
218
|
-
* Existing attributes on the `destination` will be overwritten.
|
|
219
|
-
*
|
|
220
|
-
* @param {object} source Parameters to be copied (not changed).
|
|
221
|
-
* @param {object} destination Dictionary to which parameters are copied
|
|
222
|
-
* (mutated in place).
|
|
223
|
-
*/
|
|
224
|
-
overwriteParameters: function overwriteParameters(source, destination) {
|
|
225
|
-
const keys = Object.keys(source)
|
|
226
|
-
for (let i = 0; i < keys.length; i++) {
|
|
227
|
-
const key = keys[i]
|
|
228
|
-
destination[key] = source[key]
|
|
229
|
-
}
|
|
172
|
+
/**
|
|
173
|
+
* Copy a set of request parameters from one object to another.
|
|
174
|
+
* Existing attributes on the `destination` will be overwritten.
|
|
175
|
+
*
|
|
176
|
+
* @param {object} source Parameters to be copied (not changed).
|
|
177
|
+
* @param {object} destination Dictionary to which parameters are copied
|
|
178
|
+
* (mutated in place).
|
|
179
|
+
*/
|
|
180
|
+
function overwriteParameters(source, destination) {
|
|
181
|
+
const keys = Object.keys(source)
|
|
182
|
+
for (let i = 0; i < keys.length; i++) {
|
|
183
|
+
const key = keys[i]
|
|
184
|
+
destination[key] = source[key]
|
|
230
185
|
}
|
|
231
186
|
}
|
|
232
187
|
|
|
@@ -245,3 +200,14 @@ function isExpectedStatusCodeForErrors(config, code) {
|
|
|
245
200
|
}
|
|
246
201
|
return codes.indexOf(parseInt(code, 10)) >= 0
|
|
247
202
|
}
|
|
203
|
+
|
|
204
|
+
module.exports = {
|
|
205
|
+
LOCALHOST_NAMES,
|
|
206
|
+
isLocalhost,
|
|
207
|
+
isError,
|
|
208
|
+
isIgnoredError,
|
|
209
|
+
isExpectedError,
|
|
210
|
+
scrubAndParseParameters,
|
|
211
|
+
obfuscatePath,
|
|
212
|
+
overwriteParameters
|
|
213
|
+
}
|
|
@@ -10,7 +10,6 @@ const http = require('http')
|
|
|
10
10
|
const logger = require('../logger').child({ component: 'utilization-request' })
|
|
11
11
|
const fs = require('../util/unwrapped-core').fs
|
|
12
12
|
const properties = require('../util/properties')
|
|
13
|
-
const url = require('url')
|
|
14
13
|
|
|
15
14
|
exports.checkValueString = checkValueString
|
|
16
15
|
function checkValueString(str) {
|
|
@@ -65,11 +64,6 @@ exports.getKeys = function getKeys(data, keys, allValuesMustBeValid = true) {
|
|
|
65
64
|
|
|
66
65
|
exports.request = function request(opts, agent, cb) {
|
|
67
66
|
// Add default timeout of a second to the request
|
|
68
|
-
|
|
69
|
-
if (typeof opts === 'string') {
|
|
70
|
-
opts = url.parse(opts)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
67
|
opts.timeout = opts.timeout || 1000
|
|
74
68
|
|
|
75
69
|
// This can make more that GET requests if you pass in an object
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.3.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"contributors": [
|
|
@@ -201,7 +201,7 @@
|
|
|
201
201
|
"#test/assert": "./test/lib/custom-assertions/index.js"
|
|
202
202
|
},
|
|
203
203
|
"dependencies": {
|
|
204
|
-
"@apm-js-collab/
|
|
204
|
+
"@apm-js-collab/tracing-hooks": "^0.1.0",
|
|
205
205
|
"@grpc/grpc-js": "^1.13.2",
|
|
206
206
|
"@grpc/proto-loader": "^0.7.5",
|
|
207
207
|
"@newrelic/security-agent": "^2.4.2",
|
|
@@ -272,8 +272,7 @@
|
|
|
272
272
|
"self-cert": "^2.0.0",
|
|
273
273
|
"should": "*",
|
|
274
274
|
"sinon": "^5.1.1",
|
|
275
|
-
"superagent": "^9.0.1"
|
|
276
|
-
"testdouble": "^3.20.2"
|
|
275
|
+
"superagent": "^9.0.1"
|
|
277
276
|
},
|
|
278
277
|
"repository": {
|
|
279
278
|
"type": "git",
|