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.
Files changed (100) hide show
  1. package/NEWS.md +40 -0
  2. package/api.js +3 -3
  3. package/lib/agent.js +2 -2
  4. package/lib/aggregators/log-aggregator.js +2 -2
  5. package/lib/collector/api.js +6 -6
  6. package/lib/collector/remote-method.js +1 -1
  7. package/lib/config/attribute-filter.js +7 -7
  8. package/lib/config/default.js +5 -5
  9. package/lib/config/hsm.js +1 -1
  10. package/lib/config/index.js +28 -26
  11. package/lib/config/merge-server-config.js +6 -6
  12. package/lib/core-instrumentation.js +4 -0
  13. package/lib/db/query-parsers/elasticsearch.js +5 -4
  14. package/lib/db/query-parsers/sql.js +1 -1
  15. package/lib/environment.js +16 -16
  16. package/lib/errors/index.js +1 -1
  17. package/lib/harvester.js +7 -5
  18. package/lib/header-attributes.js +3 -3
  19. package/lib/instrumentation/@google/genai.js +6 -9
  20. package/lib/instrumentation/aws-sdk/v3/bedrock.js +11 -9
  21. package/lib/instrumentation/bunyan.js +1 -5
  22. package/lib/instrumentation/core/child_process.js +4 -4
  23. package/lib/instrumentation/core/fs.js +2 -2
  24. package/lib/instrumentation/core/http2.js +266 -0
  25. package/lib/instrumentation/core/timers.js +4 -4
  26. package/lib/instrumentation/fastify.js +1 -1
  27. package/lib/instrumentation/grpc-js/grpc.js +7 -7
  28. package/lib/instrumentation/koa/route-instrumentation.js +16 -18
  29. package/lib/instrumentation/langchain/runnable.js +2 -2
  30. package/lib/instrumentation/langchain/vectorstore.js +3 -2
  31. package/lib/instrumentation/when/index.js +5 -5
  32. package/lib/instrumentation/winston.js +1 -1
  33. package/lib/instrumentation-tracker.js +2 -2
  34. package/lib/instrumentations.js +0 -1
  35. package/lib/llm-events/aws-bedrock/bedrock-response.js +9 -0
  36. package/lib/llm-events/aws-bedrock/chat-completion-message.js +32 -4
  37. package/lib/llm-events/aws-bedrock/chat-completion-summary.js +30 -1
  38. package/lib/llm-events/aws-bedrock/embedding.js +37 -4
  39. package/lib/llm-events/aws-bedrock/stream-handler.js +22 -0
  40. package/lib/llm-events/aws-bedrock/utils.js +59 -1
  41. package/lib/llm-events/google-genai/chat-completion-message.js +33 -5
  42. package/lib/llm-events/google-genai/chat-completion-summary.js +33 -0
  43. package/lib/llm-events/google-genai/embedding.js +34 -4
  44. package/lib/llm-events/google-genai/utils.js +32 -0
  45. package/lib/llm-events/openai/chat-completion-message.js +33 -6
  46. package/lib/llm-events/openai/chat-completion-summary.js +36 -0
  47. package/lib/llm-events/openai/embedding.js +34 -4
  48. package/lib/llm-events/openai/utils.js +34 -0
  49. package/lib/llm-events/utils.js +110 -0
  50. package/lib/metrics/index.js +7 -7
  51. package/lib/metrics/normalizer/tx_segment.js +26 -28
  52. package/lib/metrics/normalizer.js +9 -7
  53. package/lib/metrics/recorders/distributed-trace.js +2 -2
  54. package/lib/otel/traces/rules.js +2 -2
  55. package/lib/serverless/aws-lambda.js +7 -7
  56. package/lib/shim/datastore-shim.js +8 -8
  57. package/lib/shim/message-shim/index.js +8 -8
  58. package/lib/shim/message-shim/subscribe-consume.js +12 -10
  59. package/lib/shim/promise-shim.js +3 -3
  60. package/lib/shim/shim.js +11 -9
  61. package/lib/shim/transaction-shim.js +7 -7
  62. package/lib/shim/webframework-shim/index.js +8 -9
  63. package/lib/shimmer.js +19 -26
  64. package/lib/stats/apdex.js +1 -1
  65. package/lib/subscriber-configs.js +1 -0
  66. package/lib/subscribers/dc-base.js +6 -4
  67. package/lib/subscribers/express/config.js +102 -0
  68. package/lib/subscribers/express/param.js +22 -0
  69. package/lib/subscribers/express/render.js +28 -0
  70. package/lib/subscribers/express/route.js +55 -0
  71. package/lib/subscribers/express/router-param.js +24 -0
  72. package/lib/subscribers/express/router-route.js +24 -0
  73. package/lib/subscribers/express/router-use.js +24 -0
  74. package/lib/subscribers/express/use.js +44 -0
  75. package/lib/subscribers/fastify/add-hook.js +5 -6
  76. package/lib/subscribers/fastify/decorate.js +6 -8
  77. package/lib/subscribers/fastify/index.js +3 -2
  78. package/lib/subscribers/ioredis/config.js +3 -3
  79. package/lib/subscribers/ioredis/index.js +24 -1
  80. package/lib/subscribers/middleware-wrapper.js +269 -0
  81. package/lib/subscribers/middleware.js +20 -0
  82. package/lib/subscribers/openai/base.js +5 -6
  83. package/lib/subscribers/openai/utils.js +5 -4
  84. package/lib/system-metrics-sampler.js +5 -5
  85. package/lib/transaction/index.js +7 -7
  86. package/lib/transaction/name-state.js +4 -4
  87. package/lib/transaction/trace/aggregator.js +1 -1
  88. package/lib/transaction/trace/index.js +3 -1
  89. package/lib/transaction/trace/segment.js +2 -2
  90. package/lib/uninstrumented.js +4 -4
  91. package/lib/util/hashes.js +4 -4
  92. package/lib/util/label-parser.js +7 -7
  93. package/lib/util/logger.js +3 -3
  94. package/lib/util/process-version.js +1 -1
  95. package/lib/utilization/docker-info.js +1 -1
  96. package/lib/utilization/index.js +2 -2
  97. package/newrelic.js +1 -1
  98. package/package.json +1 -1
  99. package/lib/instrumentation/express.js +0 -176
  100. 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.forEach((aggregator) => {
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
- this.aggregators.forEach((aggregator) => aggregator.stop())
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.forEach((aggregator) => {
71
+ for (const aggregator of this.aggregators) {
70
72
  aggregator.reconfigure(config)
71
- })
73
+ }
72
74
  }
73
75
 
74
76
  /**
@@ -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).forEach(function forEachHeader(h) {
82
- dest[h] = prefix + HEADER_ATTR_NAMES[h]
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.forEach((message, index) => {
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 ({ agent, shim, request, response, segment, transaction }) {
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({ agent, shim, request, response, segment, transaction })
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 instace
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.forEach((contextMessage, promptIndex) => {
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: promptIndex,
151
+ index: i,
151
152
  completionId: summary.id
152
153
  })
153
154
  recordEvent({ agent, type: 'LlmChatCompletionMessage', msg })
154
- })
155
+ }
155
156
 
156
- bedrockResponse.completions.forEach((content, completionIndex) => {
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 + completionIndex,
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
- embeddings.forEach((embedding) => {
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.keys(metadata).forEach((m) => {
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).forEach((symbol) => {
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).forEach((symbol) => {
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).forEach((symbol) => {
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
- pkgs.forEach((nodule) => {
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).forEach((symbol) => {
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. dont be confused by the call to recordMiddleware -- we don't
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.keys(outboundAgentHeaders).forEach((key) => {
74
- nrMetadata.add(key, outboundAgentHeaders[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 onReceiveStatuts = shim.bindSegment(originalListener.onReceiveStatus, segment)
113
- onReceiveStatuts(status)
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()).forEach(([key, value]) => {
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
- METHODS.forEach(function wrap(method) {
15
- shim.wrap(route, method, function wrapMethod(shim, methodFn) {
16
- return function wrappedMethod() {
17
- const middleware = methodFn.apply(route, arguments)
18
- return shim.recordMiddleware(
19
- middleware,
20
- new MiddlewareSpec({
21
- route: arguments[0],
22
- next: shim.LAST,
23
- name: shim.getName(arguments[1]),
24
- promise: true,
25
- req: function getReq(shim, fn, fnName, args) {
26
- return args[0] && args[0].req
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 instace of 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 instace of 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.forEach((document, sequence) => {
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(