newrelic 13.5.0 → 13.6.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 -0
- package/api.js +3 -3
- package/lib/agent.js +2 -2
- package/lib/aggregators/log-aggregator.js +2 -2
- package/lib/collector/api.js +6 -6
- package/lib/collector/remote-method.js +1 -1
- package/lib/config/attribute-filter.js +7 -7
- package/lib/config/default.js +5 -5
- package/lib/config/hsm.js +1 -1
- package/lib/config/index.js +28 -26
- package/lib/config/merge-server-config.js +6 -6
- package/lib/core-instrumentation.js +4 -0
- package/lib/db/query-parsers/elasticsearch.js +5 -4
- package/lib/db/query-parsers/sql.js +1 -1
- package/lib/environment.js +16 -16
- package/lib/errors/index.js +1 -1
- package/lib/harvester.js +7 -5
- package/lib/header-attributes.js +3 -3
- package/lib/instrumentation/@google/genai.js +6 -9
- package/lib/instrumentation/aws-sdk/v3/bedrock.js +11 -9
- package/lib/instrumentation/bunyan.js +1 -5
- package/lib/instrumentation/core/child_process.js +4 -4
- package/lib/instrumentation/core/fs.js +2 -2
- package/lib/instrumentation/core/http2.js +266 -0
- package/lib/instrumentation/core/timers.js +4 -4
- package/lib/instrumentation/fastify.js +1 -1
- package/lib/instrumentation/grpc-js/grpc.js +7 -7
- package/lib/instrumentation/koa/route-instrumentation.js +16 -18
- package/lib/instrumentation/langchain/runnable.js +2 -2
- package/lib/instrumentation/langchain/vectorstore.js +3 -2
- package/lib/instrumentation/when/index.js +5 -5
- package/lib/instrumentation/winston.js +1 -1
- package/lib/instrumentation-tracker.js +2 -2
- package/lib/instrumentations.js +0 -1
- package/lib/llm-events/aws-bedrock/bedrock-response.js +9 -0
- package/lib/llm-events/aws-bedrock/chat-completion-message.js +32 -4
- package/lib/llm-events/aws-bedrock/chat-completion-summary.js +30 -1
- package/lib/llm-events/aws-bedrock/embedding.js +37 -4
- package/lib/llm-events/aws-bedrock/stream-handler.js +22 -0
- package/lib/llm-events/aws-bedrock/utils.js +59 -1
- package/lib/llm-events/google-genai/chat-completion-message.js +33 -5
- package/lib/llm-events/google-genai/chat-completion-summary.js +33 -0
- package/lib/llm-events/google-genai/embedding.js +34 -4
- package/lib/llm-events/google-genai/utils.js +32 -0
- package/lib/llm-events/openai/chat-completion-message.js +33 -6
- package/lib/llm-events/openai/chat-completion-summary.js +36 -0
- package/lib/llm-events/openai/embedding.js +34 -4
- package/lib/llm-events/openai/utils.js +34 -0
- package/lib/llm-events/utils.js +110 -0
- package/lib/metrics/index.js +7 -7
- package/lib/metrics/normalizer/tx_segment.js +26 -28
- package/lib/metrics/normalizer.js +9 -7
- package/lib/metrics/recorders/distributed-trace.js +2 -2
- package/lib/otel/traces/rules.js +2 -2
- package/lib/serverless/aws-lambda.js +7 -7
- package/lib/shim/datastore-shim.js +8 -8
- package/lib/shim/message-shim/index.js +8 -8
- package/lib/shim/message-shim/subscribe-consume.js +12 -10
- package/lib/shim/promise-shim.js +3 -3
- package/lib/shim/shim.js +11 -9
- package/lib/shim/transaction-shim.js +7 -7
- package/lib/shim/webframework-shim/index.js +8 -9
- package/lib/shimmer.js +19 -26
- package/lib/stats/apdex.js +1 -1
- package/lib/subscriber-configs.js +1 -0
- package/lib/subscribers/dc-base.js +6 -4
- package/lib/subscribers/express/config.js +102 -0
- package/lib/subscribers/express/param.js +22 -0
- package/lib/subscribers/express/render.js +28 -0
- package/lib/subscribers/express/route.js +55 -0
- package/lib/subscribers/express/router-param.js +24 -0
- package/lib/subscribers/express/router-route.js +24 -0
- package/lib/subscribers/express/router-use.js +24 -0
- package/lib/subscribers/express/use.js +44 -0
- package/lib/subscribers/fastify/add-hook.js +5 -6
- package/lib/subscribers/fastify/decorate.js +6 -8
- package/lib/subscribers/fastify/index.js +3 -2
- package/lib/subscribers/ioredis/config.js +3 -3
- package/lib/subscribers/ioredis/index.js +24 -1
- package/lib/subscribers/middleware-wrapper.js +269 -0
- package/lib/subscribers/middleware.js +20 -0
- package/lib/subscribers/openai/base.js +5 -6
- package/lib/subscribers/openai/utils.js +5 -4
- package/lib/system-metrics-sampler.js +5 -5
- package/lib/transaction/index.js +7 -7
- package/lib/transaction/name-state.js +4 -4
- package/lib/transaction/trace/aggregator.js +1 -1
- package/lib/transaction/trace/index.js +3 -1
- package/lib/transaction/trace/segment.js +2 -2
- package/lib/uninstrumented.js +4 -4
- package/lib/util/hashes.js +4 -4
- package/lib/util/label-parser.js +7 -7
- package/lib/util/logger.js +3 -3
- package/lib/util/process-version.js +1 -1
- package/lib/utilization/docker-info.js +1 -1
- package/lib/utilization/index.js +2 -2
- package/newrelic.js +1 -1
- package/package.json +1 -1
- package/lib/instrumentation/express.js +0 -176
- package/lib/subscribers/fastify/common.js +0 -121
package/lib/harvester.js
CHANGED
|
@@ -18,11 +18,11 @@ module.exports = class Harvester {
|
|
|
18
18
|
* Calls start on every registered aggregator that is enabled.
|
|
19
19
|
*/
|
|
20
20
|
start() {
|
|
21
|
-
this.aggregators
|
|
21
|
+
for (const aggregator of this.aggregators) {
|
|
22
22
|
if (aggregator.enabled) {
|
|
23
23
|
aggregator.start()
|
|
24
24
|
}
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
@@ -57,7 +57,9 @@ module.exports = class Harvester {
|
|
|
57
57
|
* Calls stop on every registered aggregator.
|
|
58
58
|
*/
|
|
59
59
|
stop() {
|
|
60
|
-
|
|
60
|
+
for (const aggregator of this.aggregators) {
|
|
61
|
+
aggregator.stop()
|
|
62
|
+
}
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
/**
|
|
@@ -66,9 +68,9 @@ module.exports = class Harvester {
|
|
|
66
68
|
* @param {object} config updated agent configuration
|
|
67
69
|
*/
|
|
68
70
|
update(config) {
|
|
69
|
-
this.aggregators
|
|
71
|
+
for (const aggregator of this.aggregators) {
|
|
70
72
|
aggregator.reconfigure(config)
|
|
71
|
-
}
|
|
73
|
+
}
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
/**
|
package/lib/header-attributes.js
CHANGED
|
@@ -78,9 +78,9 @@ _setHeaderAttrNames(REQUEST_HEADER_NAMES, REQUEST_HEADER_PREFIX)
|
|
|
78
78
|
_setHeaderAttrNames(RESPONSE_HEADER_NAMES, RESPONSE_HEADER_PREFIX)
|
|
79
79
|
|
|
80
80
|
function _setHeaderAttrNames(dest, prefix) {
|
|
81
|
-
Object.keys(HEADER_ATTR_NAMES)
|
|
82
|
-
dest[
|
|
83
|
-
}
|
|
81
|
+
for (const header of Object.keys(HEADER_ATTR_NAMES)) {
|
|
82
|
+
dest[header] = prefix + HEADER_ATTR_NAMES[header]
|
|
83
|
+
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
function _headerToCamelCase(header) {
|
|
@@ -57,7 +57,6 @@ function addLlmMeta({ agent, transaction }) {
|
|
|
57
57
|
*
|
|
58
58
|
* @param {object} params input params
|
|
59
59
|
* @param {Agent} params.agent NR agent instance
|
|
60
|
-
* @param {Shim} params.shim the current shim instance
|
|
61
60
|
* @param {TraceSegment} params.segment active segment from chat completion
|
|
62
61
|
* @param {object} params.request chat completion params
|
|
63
62
|
* @param {object} params.response chat completion response
|
|
@@ -66,7 +65,6 @@ function addLlmMeta({ agent, transaction }) {
|
|
|
66
65
|
*/
|
|
67
66
|
function recordChatCompletionMessages({
|
|
68
67
|
agent,
|
|
69
|
-
shim,
|
|
70
68
|
segment,
|
|
71
69
|
request,
|
|
72
70
|
response,
|
|
@@ -97,20 +95,21 @@ function recordChatCompletionMessages({
|
|
|
97
95
|
const inputMessages = Array.isArray(request.contents) ? request.contents : [request.contents]
|
|
98
96
|
const responseMessage = response?.candidates?.[0]?.content
|
|
99
97
|
const messages = responseMessage !== undefined ? [...inputMessages, responseMessage] : inputMessages
|
|
100
|
-
messages.
|
|
98
|
+
for (let i = 0; i < messages.length; i++) {
|
|
99
|
+
const message = messages[i]
|
|
101
100
|
const completionMsg = new LlmChatCompletionMessage({
|
|
102
101
|
agent,
|
|
103
102
|
segment,
|
|
104
103
|
transaction,
|
|
105
104
|
request,
|
|
106
105
|
response,
|
|
107
|
-
index,
|
|
106
|
+
index: i,
|
|
108
107
|
completionId: completionSummary.id,
|
|
109
108
|
message
|
|
110
109
|
})
|
|
111
110
|
|
|
112
111
|
recordEvent({ agent, type: 'LlmChatCompletionMessage', msg: completionMsg })
|
|
113
|
-
}
|
|
112
|
+
}
|
|
114
113
|
|
|
115
114
|
recordEvent({ agent, type: 'LlmChatCompletionSummary', msg: completionSummary })
|
|
116
115
|
|
|
@@ -120,7 +119,7 @@ function recordChatCompletionMessages({
|
|
|
120
119
|
}
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
function instrumentStream ({
|
|
122
|
+
function instrumentStream ({ shim, request, response, segment, transaction }) {
|
|
124
123
|
let err
|
|
125
124
|
let content
|
|
126
125
|
let modelVersion
|
|
@@ -167,7 +166,6 @@ function instrumentStream ({ agent, shim, request, response, segment, transactio
|
|
|
167
166
|
|
|
168
167
|
recordChatCompletionMessages({
|
|
169
168
|
agent: shim.agent,
|
|
170
|
-
shim,
|
|
171
169
|
segment,
|
|
172
170
|
transaction,
|
|
173
171
|
request,
|
|
@@ -208,7 +206,6 @@ module.exports = function initialize(agent, googleGenAi, moduleName, shim) {
|
|
|
208
206
|
after({ error: err, result: response, segment, transaction }) {
|
|
209
207
|
recordChatCompletionMessages({
|
|
210
208
|
agent,
|
|
211
|
-
shim,
|
|
212
209
|
segment,
|
|
213
210
|
transaction,
|
|
214
211
|
request,
|
|
@@ -241,7 +238,7 @@ module.exports = function initialize(agent, googleGenAi, moduleName, shim) {
|
|
|
241
238
|
name: GEMINI.COMPLETION,
|
|
242
239
|
promise: true,
|
|
243
240
|
after({ result: response, segment, transaction }) {
|
|
244
|
-
instrumentStream({
|
|
241
|
+
instrumentStream({ shim, request, response, segment, transaction })
|
|
245
242
|
addLlmMeta({ agent, transaction })
|
|
246
243
|
}
|
|
247
244
|
})
|
|
@@ -110,7 +110,7 @@ function addLlmMeta({ agent, segment, transaction }) {
|
|
|
110
110
|
* @param {BedrockCommand} params.bedrockCommand parsed input
|
|
111
111
|
* @param {Error|null} params.err error from request if exists
|
|
112
112
|
* @param {BedrockResponse} params.bedrockResponse parsed response
|
|
113
|
-
* @param {Shim} params.shim shim
|
|
113
|
+
* @param {Shim} params.shim shim instance
|
|
114
114
|
* @param {Transaction} params.transaction active transaction
|
|
115
115
|
*/
|
|
116
116
|
function recordChatCompletionMessages({
|
|
@@ -138,7 +138,8 @@ function recordChatCompletionMessages({
|
|
|
138
138
|
|
|
139
139
|
// Record context message(s)
|
|
140
140
|
const promptContextMessages = bedrockCommand.prompt
|
|
141
|
-
promptContextMessages.
|
|
141
|
+
for (let i = 0; i < promptContextMessages.length; i++) {
|
|
142
|
+
const contextMessage = promptContextMessages[i]
|
|
142
143
|
const msg = new LlmChatCompletionMessage({
|
|
143
144
|
agent,
|
|
144
145
|
segment,
|
|
@@ -147,13 +148,14 @@ function recordChatCompletionMessages({
|
|
|
147
148
|
content: contextMessage.content,
|
|
148
149
|
role: contextMessage.role,
|
|
149
150
|
bedrockResponse,
|
|
150
|
-
index:
|
|
151
|
+
index: i,
|
|
151
152
|
completionId: summary.id
|
|
152
153
|
})
|
|
153
154
|
recordEvent({ agent, type: 'LlmChatCompletionMessage', msg })
|
|
154
|
-
}
|
|
155
|
+
}
|
|
155
156
|
|
|
156
|
-
bedrockResponse.completions.
|
|
157
|
+
for (let i = 0; i < bedrockResponse.completions.length; i++) {
|
|
158
|
+
const content = bedrockResponse.completions[i]
|
|
157
159
|
const chatCompletionMessage = new LlmChatCompletionMessage({
|
|
158
160
|
agent,
|
|
159
161
|
segment,
|
|
@@ -161,13 +163,13 @@ function recordChatCompletionMessages({
|
|
|
161
163
|
bedrockCommand,
|
|
162
164
|
bedrockResponse,
|
|
163
165
|
isResponse: true,
|
|
164
|
-
index: promptContextMessages.length +
|
|
166
|
+
index: promptContextMessages.length + i,
|
|
165
167
|
content,
|
|
166
168
|
role: 'assistant',
|
|
167
169
|
completionId: summary.id
|
|
168
170
|
})
|
|
169
171
|
recordEvent({ agent, type: 'LlmChatCompletionMessage', msg: chatCompletionMessage })
|
|
170
|
-
}
|
|
172
|
+
}
|
|
171
173
|
|
|
172
174
|
recordEvent({ agent, type: 'LlmChatCompletionSummary', msg: summary })
|
|
173
175
|
|
|
@@ -214,9 +216,9 @@ function recordEmbeddingMessage({
|
|
|
214
216
|
isError: err !== null
|
|
215
217
|
}))
|
|
216
218
|
|
|
217
|
-
|
|
219
|
+
for (const embedding of embeddings) {
|
|
218
220
|
recordEvent({ agent, type: 'LlmEmbedding', msg: embedding })
|
|
219
|
-
}
|
|
221
|
+
}
|
|
220
222
|
|
|
221
223
|
if (err) {
|
|
222
224
|
const llmError = new LlmError({ bedrockResponse, err, embedding: embeddings.length === 1 ? embeddings[0] : undefined })
|
|
@@ -38,11 +38,7 @@ function augmentLogData(originalLog, agent, nameFromLevel) {
|
|
|
38
38
|
|
|
39
39
|
// Add the metadata to the object being logged
|
|
40
40
|
const metadata = agent.getLinkingMetadata(true)
|
|
41
|
-
Object.
|
|
42
|
-
newLog[m] = metadata[m]
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
return newLog
|
|
41
|
+
return Object.assign({}, newLog, metadata)
|
|
46
42
|
}
|
|
47
43
|
|
|
48
44
|
function createLoggerWrapper(shim, fn, fnName, bunyanLogger, nameFromLevel) {
|
|
@@ -56,12 +56,12 @@ function initialize(agent, childProcess, moduleName, shim) {
|
|
|
56
56
|
|
|
57
57
|
function makePromisifyCompatible(shim, childProcess) {
|
|
58
58
|
const originalExec = shim.getOriginal(childProcess.exec)
|
|
59
|
-
Object.getOwnPropertySymbols(originalExec)
|
|
59
|
+
for (const symbol of Object.getOwnPropertySymbols(originalExec)) {
|
|
60
60
|
childProcess.exec[symbol] = originalExec[symbol]
|
|
61
|
-
}
|
|
61
|
+
}
|
|
62
62
|
|
|
63
63
|
const originalExecFile = shim.getOriginal(childProcess.execFile)
|
|
64
|
-
Object.getOwnPropertySymbols(originalExecFile)
|
|
64
|
+
for (const symbol of Object.getOwnPropertySymbols(originalExecFile)) {
|
|
65
65
|
childProcess.execFile[symbol] = originalExecFile[symbol]
|
|
66
|
-
}
|
|
66
|
+
}
|
|
67
67
|
}
|
|
@@ -55,9 +55,9 @@ function initialize(agent, fs, moduleName, shim) {
|
|
|
55
55
|
shim.record(fs, methods, recordFs)
|
|
56
56
|
|
|
57
57
|
const originalExists = shim.getOriginal(fs.exists)
|
|
58
|
-
Object.getOwnPropertySymbols(originalExists)
|
|
58
|
+
for (const symbol of Object.getOwnPropertySymbols(originalExists)) {
|
|
59
59
|
fs.exists[symbol] = originalExists[symbol]
|
|
60
|
-
}
|
|
60
|
+
}
|
|
61
61
|
|
|
62
62
|
fs.realpath.native = shim.getOriginal(fs.realpath).native
|
|
63
63
|
|
|
@@ -0,0 +1,266 @@
|
|
|
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 NAMES = require('../../metrics/names')
|
|
9
|
+
const recordExternal = require('../../metrics/recorders/http_external')
|
|
10
|
+
const logger = require('../../logger').child({ component: 'http2' })
|
|
11
|
+
const synthetics = require('../../synthetics')
|
|
12
|
+
const urltils = require('../../util/urltils.js')
|
|
13
|
+
const cat = require('#agentlib/util/cat.js')
|
|
14
|
+
const http2ConnectUrl = Symbol('http2ConnectUrl')
|
|
15
|
+
module.exports = initialize
|
|
16
|
+
|
|
17
|
+
function initialize(agent, http2, moduleName, shim) {
|
|
18
|
+
shim.wrap(http2, 'connect', wrapConnect)
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Wraps the http2 connect method in instrumentation, and saves the connect URL.
|
|
22
|
+
* The .request() method's argument doesn't retain the protocol of the connection,
|
|
23
|
+
* but it's available here as arg[0]
|
|
24
|
+
*
|
|
25
|
+
* @param {object} shim the generic shim with which the agent instruments
|
|
26
|
+
* @param {Function} fn the original connect function
|
|
27
|
+
* @returns {Function} an instrumented connect function
|
|
28
|
+
*/
|
|
29
|
+
function wrapConnect(shim, fn) {
|
|
30
|
+
return function wrappedConnect(...args) {
|
|
31
|
+
const session = fn.apply(this, args)
|
|
32
|
+
session[http2ConnectUrl] = args[0]
|
|
33
|
+
return shim.wrap(session, ['request'], wrapRequest)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Wraps the request and creates an external segment for it
|
|
39
|
+
*
|
|
40
|
+
* @param {object} _shim shim passed in from wrapConnect
|
|
41
|
+
* @param {Function} fn the ClientHttp2Session returned from .connect()
|
|
42
|
+
* @returns {Function} wrapped request function, which returns a ClientHttp2Stream
|
|
43
|
+
*/
|
|
44
|
+
function wrapRequest(_shim, fn) {
|
|
45
|
+
return function wrappedRequest(...args) {
|
|
46
|
+
const txn = agent.tracer.getTransaction()
|
|
47
|
+
if (!txn) {
|
|
48
|
+
logger.trace('Not in a transaction; not recording http2 external')
|
|
49
|
+
return fn.apply(this, args)
|
|
50
|
+
}
|
|
51
|
+
const activeSegment = agent.tracer.getSegment()
|
|
52
|
+
|
|
53
|
+
const [headers] = args
|
|
54
|
+
args[0] = addDTHeaders({
|
|
55
|
+
transaction: txn,
|
|
56
|
+
config: agent.config,
|
|
57
|
+
headers
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// we are not creating the segment before calling the original function
|
|
61
|
+
// this is because we can pull of the origin from the symbols on stream
|
|
62
|
+
// to parse the outgoing URL more accurately
|
|
63
|
+
// We are not missing much by starting the segment after the stream is created
|
|
64
|
+
const stream = fn.apply(this, args)
|
|
65
|
+
const externalAttrs = extractExternalAttrs({ stream, config: agent.config, headers, authority: this[http2ConnectUrl] })
|
|
66
|
+
const name = NAMES.EXTERNAL.PREFIX + externalAttrs.host + externalAttrs.path
|
|
67
|
+
|
|
68
|
+
const segment = agent.tracer.createSegment({
|
|
69
|
+
name,
|
|
70
|
+
recorder: recordExternal(externalAttrs.host, 'http2'),
|
|
71
|
+
parent: activeSegment,
|
|
72
|
+
transaction: txn
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
if (segment) {
|
|
76
|
+
segment.start()
|
|
77
|
+
segment.captureExternalAttributes(externalAttrs)
|
|
78
|
+
instrumentStream(stream, segment)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return stream
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Wraps the ClientHttp2Stream to listen for response/header or for errors.
|
|
87
|
+
* Of the events emitted by Http2Stream, we're interested in 'error', 'frameError', and 'timeout'
|
|
88
|
+
* Of the events emitted by ClientHttp2Stream, we're interested in 'response', to get headers
|
|
89
|
+
*
|
|
90
|
+
* @param {object} stream the ClientHttp2Stream returned from .request()
|
|
91
|
+
* @param {Segment} segment the external segment created for this request
|
|
92
|
+
*/
|
|
93
|
+
function instrumentStream(stream, segment) {
|
|
94
|
+
shim.wrap(stream, 'emit', function wrapStreamEmit(_shim, emit) {
|
|
95
|
+
const context = agent.tracer.getContext()
|
|
96
|
+
const transaction = agent.tracer.getTransaction()
|
|
97
|
+
const newContext = context.enterSegment({ segment })
|
|
98
|
+
const boundEmit = agent.tracer.bindFunction(emit, newContext)
|
|
99
|
+
|
|
100
|
+
return function wrappedEmit(event, arg) {
|
|
101
|
+
const isErrorEvent = ['error', 'frameError', 'timeout'].indexOf(event) > -1
|
|
102
|
+
if (isErrorEvent) {
|
|
103
|
+
handleError({ transaction, stream: this, error: arg })
|
|
104
|
+
} else if (event === 'response') {
|
|
105
|
+
handleResponse({ segment, response: arg })
|
|
106
|
+
}
|
|
107
|
+
if (isErrorEvent || event === 'end' || event === 'close') {
|
|
108
|
+
segment.touch()
|
|
109
|
+
}
|
|
110
|
+
return boundEmit.apply(this, arguments)
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Parses out the :path and :method from the headers,
|
|
118
|
+
* handle whether headers are raw (array) or object
|
|
119
|
+
*
|
|
120
|
+
* @param {object|Array} headers request headers
|
|
121
|
+
* @param {object} externalAttrs object to populate with path and method
|
|
122
|
+
*/
|
|
123
|
+
function parseAttrsFromHeaders(headers, externalAttrs) {
|
|
124
|
+
// headers can be raw which are an array in the format of
|
|
125
|
+
// [<key>, <value>, <key-2>, <value-2>]
|
|
126
|
+
// attempt to extract `:path` and `:method` from raw headers
|
|
127
|
+
// otherwise fallback to extracting from object
|
|
128
|
+
if (Array.isArray(headers)) {
|
|
129
|
+
for (let i = 0; i < headers.length; i += 2) {
|
|
130
|
+
const header = headers[i]
|
|
131
|
+
const value = headers[i + 1]
|
|
132
|
+
if (header === ':path') {
|
|
133
|
+
externalAttrs.path = value
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (header === ':method') {
|
|
137
|
+
externalAttrs.method = value
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
if (headers[':path']) {
|
|
142
|
+
externalAttrs.path = headers[':path']
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (headers[':method']) {
|
|
146
|
+
externalAttrs.method = headers[':method']
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Extracts the authority, path, method from the session.request headers
|
|
153
|
+
* It then constructs a URL and parses, obfuscates, and scrubs it
|
|
154
|
+
* and returns the necessary keys to capture it as an external web request
|
|
155
|
+
*
|
|
156
|
+
* @param {object} params to function
|
|
157
|
+
* @param {object} params.config agent config
|
|
158
|
+
* @param {object|Array} params.headers request headers
|
|
159
|
+
* @param {string} params.authority the authority (protocol + host + port) from the session.connect
|
|
160
|
+
* @param {object} params.stream the ClientHttp2Stream
|
|
161
|
+
* @returns {object} externalAttrs with protocol, hostname, host, method, port, path, queryParams
|
|
162
|
+
*/
|
|
163
|
+
function extractExternalAttrs({ config, headers, authority, stream }) {
|
|
164
|
+
for (const symbol of Object.getOwnPropertySymbols(stream)) {
|
|
165
|
+
if (symbol.toString() === 'Symbol(origin)') {
|
|
166
|
+
authority = stream[symbol]
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const externalAttrs = {
|
|
171
|
+
protocol: 'https:',
|
|
172
|
+
hostname: null,
|
|
173
|
+
host: 'unknown',
|
|
174
|
+
method: 'GET',
|
|
175
|
+
port: 443,
|
|
176
|
+
path: '/',
|
|
177
|
+
queryParams: {},
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
parseAttrsFromHeaders(headers, externalAttrs)
|
|
181
|
+
|
|
182
|
+
let parsedUrl
|
|
183
|
+
try {
|
|
184
|
+
parsedUrl = new URL(`${authority}${externalAttrs.path}`)
|
|
185
|
+
} catch (err) {
|
|
186
|
+
logger.trace('Could not parse URL from request headers: %s', err.message)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (parsedUrl) {
|
|
190
|
+
externalAttrs.host = parsedUrl.host
|
|
191
|
+
externalAttrs.hostname = parsedUrl.hostname
|
|
192
|
+
externalAttrs.port = parseInt(parsedUrl.port, 10)
|
|
193
|
+
externalAttrs.protocol = parsedUrl.protocol
|
|
194
|
+
const scrubbedUrl = urltils.scrubAndParseParameters(parsedUrl) // to get parameters in the format we want
|
|
195
|
+
externalAttrs.path = urltils.obfuscatePath(config, scrubbedUrl.path)
|
|
196
|
+
externalAttrs.queryParams = scrubbedUrl?.parameters
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return externalAttrs
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Notices the given error if there is no listener for the `error` event on the
|
|
204
|
+
* request object.
|
|
205
|
+
*
|
|
206
|
+
* @param {object} params to function
|
|
207
|
+
* @param {Transaction} params.transaction active transaction
|
|
208
|
+
* @param {object} params.stream wrapped ClientHttp2Stream, the closest analog of request
|
|
209
|
+
* @param {Error} params.error If provided, unhandled error that occurred during request
|
|
210
|
+
* @returns {void}
|
|
211
|
+
*/
|
|
212
|
+
function handleError({ transaction, stream, error }) {
|
|
213
|
+
if (stream?.listenerCount('error') > 0) {
|
|
214
|
+
logger.trace(error, 'Not capturing http2 client error because user has already handled it.')
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
logger.trace(error, 'Captured outbound error on behalf of the user.')
|
|
219
|
+
transaction.agent.errors.add(transaction, error)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Ties the ClientHttp2Stream response to the session.request() segment.
|
|
224
|
+
*
|
|
225
|
+
* @param {object} params to function
|
|
226
|
+
* @param {object} params.response ClientHttp2Stream response header
|
|
227
|
+
* @param {TraceSegment} params.segment external segment created for this request
|
|
228
|
+
*/
|
|
229
|
+
function handleResponse({ segment, response }) {
|
|
230
|
+
// Add response attributes for spans
|
|
231
|
+
const status = response?.[':status']
|
|
232
|
+
const statusHeader = 'http.statusCode'
|
|
233
|
+
segment.addSpanAttribute(statusHeader, status)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Injects relevant tracing headers for the external request
|
|
238
|
+
*
|
|
239
|
+
* @param {object} params object to fn
|
|
240
|
+
* @param {Shim} params.transaction current transaction
|
|
241
|
+
* @param {object} params.headers outbound ClientHttp2Session request headers
|
|
242
|
+
* @param {object} params.config agent config
|
|
243
|
+
* @returns {object} headers with DT inserted, if enabled, or the original headers
|
|
244
|
+
*/
|
|
245
|
+
function addDTHeaders({ transaction, config, headers }) {
|
|
246
|
+
const outboundHeaders = Object.create(null)
|
|
247
|
+
synthetics.assignHeadersToOutgoingRequest(config, transaction, outboundHeaders)
|
|
248
|
+
|
|
249
|
+
if (config.distributed_tracing.enabled) {
|
|
250
|
+
transaction.insertDistributedTraceHeaders(outboundHeaders)
|
|
251
|
+
} else if (config.cross_application_tracer.enabled) {
|
|
252
|
+
cat.addCatHeaders(config, transaction, outboundHeaders)
|
|
253
|
+
} else {
|
|
254
|
+
logger.trace('Both DT and CAT are disabled, not adding headers!')
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
for (const key in outboundHeaders) {
|
|
258
|
+
if (Array.isArray(headers)) {
|
|
259
|
+
headers.push(key)
|
|
260
|
+
headers.push(outboundHeaders[key])
|
|
261
|
+
} else {
|
|
262
|
+
headers[key] = outboundHeaders[key]
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return headers
|
|
266
|
+
}
|
|
@@ -22,7 +22,7 @@ function initialize(_agent, timers, _moduleName, shim) {
|
|
|
22
22
|
* @param {Array<Timers,global>} pkgs array with references to timers and global
|
|
23
23
|
*/
|
|
24
24
|
function instrumentTimerMethods(shim, pkgs) {
|
|
25
|
-
|
|
25
|
+
for (const nodule of pkgs) {
|
|
26
26
|
if (shim.isWrapped(nodule.setTimeout)) {
|
|
27
27
|
return
|
|
28
28
|
}
|
|
@@ -31,7 +31,7 @@ function instrumentTimerMethods(shim, pkgs) {
|
|
|
31
31
|
shim.record(nodule, asynchronizers, recordAsynchronizers)
|
|
32
32
|
shim.wrap(nodule, 'clearTimeout', wrapClearTimeout)
|
|
33
33
|
makeWrappedPromisifyCompatible(shim, nodule)
|
|
34
|
-
}
|
|
34
|
+
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
@@ -86,7 +86,7 @@ function makeWrappedPromisifyCompatible(shim, nodule) {
|
|
|
86
86
|
*/
|
|
87
87
|
function copySymbols(shim, nodule, name) {
|
|
88
88
|
const originalFunction = shim.getOriginal(nodule[name])
|
|
89
|
-
Object.getOwnPropertySymbols(originalFunction)
|
|
89
|
+
for (const symbol of Object.getOwnPropertySymbols(originalFunction)) {
|
|
90
90
|
nodule[name][symbol] = originalFunction[symbol]
|
|
91
|
-
}
|
|
91
|
+
}
|
|
92
92
|
}
|
|
@@ -35,7 +35,7 @@ function setupRouteHandler(shim, fastify) {
|
|
|
35
35
|
* recordMiddleware handler call
|
|
36
36
|
*
|
|
37
37
|
* The WebFramework shim treats the main route handler like any other
|
|
38
|
-
* i.e.
|
|
38
|
+
* i.e. don't be confused by the call to recordMiddleware -- we don't
|
|
39
39
|
* have a recordRouteHandler, everything goes through recordMiddleware
|
|
40
40
|
*/
|
|
41
41
|
const newRouteHandler = shim.recordMiddleware(
|
|
@@ -70,9 +70,9 @@ function wrapStart(shim, original) {
|
|
|
70
70
|
const outboundAgentHeaders = Object.create(null)
|
|
71
71
|
if (shim.agent.config.distributed_tracing.enabled) {
|
|
72
72
|
transaction.insertDistributedTraceHeaders(outboundAgentHeaders)
|
|
73
|
-
Object.
|
|
74
|
-
nrMetadata.add(key,
|
|
75
|
-
}
|
|
73
|
+
for (const [key, value] of Object.entries(outboundAgentHeaders)) {
|
|
74
|
+
nrMetadata.add(key, value)
|
|
75
|
+
}
|
|
76
76
|
} else {
|
|
77
77
|
shim.logger.debug('Distributed tracing disabled by instrumentation.')
|
|
78
78
|
}
|
|
@@ -109,8 +109,8 @@ function wrapStart(shim, original) {
|
|
|
109
109
|
})
|
|
110
110
|
|
|
111
111
|
if (originalListener && originalListener.onReceiveStatus) {
|
|
112
|
-
const
|
|
113
|
-
|
|
112
|
+
const onReceiveStatus = shim.bindSegment(originalListener.onReceiveStatus, segment)
|
|
113
|
+
onReceiveStatus(status)
|
|
114
114
|
}
|
|
115
115
|
segment.end()
|
|
116
116
|
}
|
|
@@ -192,9 +192,9 @@ function wrapRegister(shim, original) {
|
|
|
192
192
|
|
|
193
193
|
function acceptDTHeaders(stream, transaction) {
|
|
194
194
|
const metadata = stream.metadata
|
|
195
|
-
Object.entries(metadata.getMap())
|
|
195
|
+
for (const [key, value] of Object.entries(metadata.getMap())) {
|
|
196
196
|
transaction.trace.attributes.addAttribute(DESTINATION, `request.headers.${key}`, value)
|
|
197
|
-
}
|
|
197
|
+
}
|
|
198
198
|
|
|
199
199
|
const headers = Object.create(null)
|
|
200
200
|
headers.tracestate = metadata.get('tracestate').join(',')
|
|
@@ -11,23 +11,21 @@ const { MiddlewareSpec } = require('../../shim/specs')
|
|
|
11
11
|
module.exports = function instrumentRoute(shim, route) {
|
|
12
12
|
shim.setFramework(shim.KOA)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
})
|
|
14
|
+
shim.wrap(route, METHODS, function wrapMethod(shim, methodFn) {
|
|
15
|
+
return function wrappedMethod() {
|
|
16
|
+
const middleware = methodFn.apply(route, arguments)
|
|
17
|
+
return shim.recordMiddleware(
|
|
18
|
+
middleware,
|
|
19
|
+
new MiddlewareSpec({
|
|
20
|
+
route: arguments[0],
|
|
21
|
+
next: shim.LAST,
|
|
22
|
+
name: shim.getName(arguments[1]),
|
|
23
|
+
promise: true,
|
|
24
|
+
req: function getReq(shim, fn, fnName, args) {
|
|
25
|
+
return args[0] && args[0].req
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
)
|
|
29
|
+
}
|
|
32
30
|
})
|
|
33
31
|
}
|
|
@@ -48,7 +48,7 @@ module.exports = function initialize(shim, langchain) {
|
|
|
48
48
|
*
|
|
49
49
|
* @param {object} params function params
|
|
50
50
|
* @param {object} params.langchain `@langchain/core/runnables/base` export
|
|
51
|
-
* @param {Shim} params.shim
|
|
51
|
+
* @param {Shim} params.shim instance of shim
|
|
52
52
|
*/
|
|
53
53
|
function instrumentInvokeChain({ langchain, shim }) {
|
|
54
54
|
shim.record(
|
|
@@ -84,7 +84,7 @@ function instrumentInvokeChain({ langchain, shim }) {
|
|
|
84
84
|
*
|
|
85
85
|
* @param {object} params function params
|
|
86
86
|
* @param {object} params.langchain `@langchain/core/runnables/base` export
|
|
87
|
-
* @param {Shim} params.shim
|
|
87
|
+
* @param {Shim} params.shim instance of shim
|
|
88
88
|
*/
|
|
89
89
|
function instrumentStream({ langchain, shim }) {
|
|
90
90
|
shim.record(
|
|
@@ -52,7 +52,8 @@ function recordVectorSearch({
|
|
|
52
52
|
|
|
53
53
|
recordEvent({ agent, shim, type: 'LlmVectorSearch', pkgVersion, msg: vectorSearch })
|
|
54
54
|
|
|
55
|
-
output.
|
|
55
|
+
for (let sequence = 0; sequence < output.length; sequence++) {
|
|
56
|
+
const document = output[sequence]
|
|
56
57
|
const vectorSearchResult = new LangChainVectorSearchResult({
|
|
57
58
|
agent,
|
|
58
59
|
segment,
|
|
@@ -70,7 +71,7 @@ function recordVectorSearch({
|
|
|
70
71
|
pkgVersion,
|
|
71
72
|
msg: vectorSearchResult
|
|
72
73
|
})
|
|
73
|
-
}
|
|
74
|
+
}
|
|
74
75
|
|
|
75
76
|
if (err) {
|
|
76
77
|
agent.errors.add(
|