newrelic 13.3.3 → 13.4.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 (109) hide show
  1. package/NEWS.md +22 -0
  2. package/THIRD_PARTY_NOTICES.md +11 -11
  3. package/esm-loader.mjs +2 -2
  4. package/lib/attributes.js +4 -4
  5. package/lib/collector/facts.js +1 -1
  6. package/lib/collector/http-agents.js +2 -2
  7. package/lib/collector/parse-response.js +9 -12
  8. package/lib/collector/remote-method.js +4 -5
  9. package/lib/config/formatters.js +2 -3
  10. package/lib/config/index.js +6 -6
  11. package/lib/harvester.js +1 -1
  12. package/lib/header-processing.js +3 -3
  13. package/lib/instrumentation/@node-redis/client.js +1 -1
  14. package/lib/instrumentation/aws-sdk/v2/instrumentation-helper.js +2 -2
  15. package/lib/instrumentation/aws-sdk/v3/common.js +5 -5
  16. package/lib/instrumentation/aws-sdk/v3/dynamodb.js +4 -4
  17. package/lib/instrumentation/aws-sdk/v3/sns.js +9 -9
  18. package/lib/instrumentation/aws-sdk/v3/sqs.js +6 -6
  19. package/lib/instrumentation/core/http-outbound.js +1 -2
  20. package/lib/instrumentation/fastify/spec-builders.js +11 -11
  21. package/lib/instrumentation/fastify.js +4 -4
  22. package/lib/instrumentation/memcached.js +4 -4
  23. package/lib/instrumentation/mongodb.js +2 -2
  24. package/lib/instrumentation/undici.js +3 -3
  25. package/lib/instrumentations.js +0 -2
  26. package/lib/llm-events/aws-bedrock/bedrock-response.js +6 -6
  27. package/lib/llm-events/aws-bedrock/converse-stream-handler.js +1 -1
  28. package/lib/llm-events/aws-bedrock/stream-handler.js +4 -4
  29. package/lib/metrics/index.js +2 -2
  30. package/lib/metrics/normalizer/rule.js +1 -1
  31. package/lib/metrics/normalizer.js +4 -5
  32. package/lib/metrics/recorders/message-transaction.js +3 -3
  33. package/lib/otel/context-manager.js +4 -4
  34. package/lib/otel/trace-propagator.js +1 -1
  35. package/lib/prioritized-attributes.js +2 -2
  36. package/lib/serverless/aws-lambda.js +4 -4
  37. package/lib/shim/datastore-shim.js +1 -1
  38. package/lib/shim/message-shim/consume.js +1 -1
  39. package/lib/shim/message-shim/subscribe-consume.js +1 -2
  40. package/lib/shim/promise-shim.js +1 -3
  41. package/lib/shim/shim.js +3 -4
  42. package/lib/shim/transaction-shim.js +0 -1
  43. package/lib/shim/webframework-shim/common.js +2 -4
  44. package/lib/shim/webframework-shim/middleware.js +8 -8
  45. package/lib/spans/span-context.js +1 -1
  46. package/lib/spans/span-event-aggregator.js +2 -2
  47. package/lib/spans/span-event.js +6 -5
  48. package/lib/spans/streaming-span-attributes.js +1 -1
  49. package/lib/spans/streaming-span-event-aggregator.js +1 -2
  50. package/lib/stats/index.js +1 -1
  51. package/lib/subscriber-configs.js +2 -0
  52. package/lib/subscribers/amqplib/accept-message.js +14 -0
  53. package/lib/subscribers/amqplib/callback-model.js +39 -0
  54. package/lib/subscribers/amqplib/channel-model.js +39 -0
  55. package/lib/subscribers/amqplib/config.js +695 -0
  56. package/lib/subscribers/amqplib/connect.js +74 -0
  57. package/lib/subscribers/amqplib/consume.js +54 -0
  58. package/lib/subscribers/amqplib/get-cb.js +16 -0
  59. package/lib/subscribers/amqplib/get.js +50 -0
  60. package/lib/subscribers/amqplib/purge-queue-cb.js +16 -0
  61. package/lib/subscribers/amqplib/purge-queue.js +37 -0
  62. package/lib/subscribers/amqplib/send-message.js +44 -0
  63. package/lib/subscribers/amqplib/send-or-enqueue.js +20 -0
  64. package/lib/subscribers/amqplib/utils.js +53 -0
  65. package/lib/subscribers/application-logs.js +1 -1
  66. package/lib/subscribers/base.js +67 -1
  67. package/lib/subscribers/cassandra-driver/client-batch.js +49 -0
  68. package/lib/subscribers/cassandra-driver/client-connect.js +35 -0
  69. package/lib/subscribers/cassandra-driver/client-each-row.js +36 -0
  70. package/lib/subscribers/cassandra-driver/client-execute.js +36 -0
  71. package/lib/subscribers/cassandra-driver/client-shutdown.js +32 -0
  72. package/lib/subscribers/cassandra-driver/config.js +145 -0
  73. package/lib/subscribers/cassandra-driver/legacy-client-batch.js +22 -0
  74. package/lib/subscribers/cassandra-driver/legacy-client-connect.js +22 -0
  75. package/lib/subscribers/cassandra-driver/legacy-client-each-row.js +22 -0
  76. package/lib/subscribers/cassandra-driver/legacy-client-execute.js +21 -0
  77. package/lib/subscribers/cassandra-driver/legacy-client-shutdown.js +32 -0
  78. package/lib/subscribers/create-config.js +2 -4
  79. package/lib/subscribers/db-query.js +5 -1
  80. package/lib/subscribers/message-consumer.js +146 -0
  81. package/lib/subscribers/message-producer.js +60 -0
  82. package/lib/subscribers/meta-subscriber.js +69 -0
  83. package/lib/subscribers/openai/base.js +27 -1
  84. package/lib/subscribers/openai/chat.js +0 -27
  85. package/lib/subscribers/openai/config.js +3 -3
  86. package/lib/subscribers/openai/utils.js +9 -11
  87. package/lib/subscribers/propagation.js +43 -0
  88. package/lib/timer.js +1 -1
  89. package/lib/transaction/dt-payload.js +1 -1
  90. package/lib/transaction/handle.js +8 -9
  91. package/lib/transaction/name-state.js +11 -11
  92. package/lib/transaction/trace/segment.js +2 -3
  93. package/lib/util/attribute-types.js +1 -1
  94. package/lib/util/byte-limit.js +1 -1
  95. package/lib/util/cat.js +13 -13
  96. package/lib/util/flatten.js +2 -3
  97. package/lib/util/is-absolute-path.js +1 -1
  98. package/lib/util/is-string.js +15 -0
  99. package/lib/util/logger.js +6 -6
  100. package/lib/util/stream-sink.js +1 -1
  101. package/lib/utilization/docker-info.js +4 -3
  102. package/lib/w3c/traceparent.js +1 -1
  103. package/package.json +2 -2
  104. package/lib/instrumentation/amqplib/amqplib.js +0 -122
  105. package/lib/instrumentation/amqplib/channel-model.js +0 -124
  106. package/lib/instrumentation/amqplib/channel.js +0 -72
  107. package/lib/instrumentation/amqplib/nr-hooks.js +0 -21
  108. package/lib/instrumentation/amqplib/utils.js +0 -143
  109. package/lib/instrumentation/cassandra-driver.js +0 -131
@@ -0,0 +1,74 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ const Subscriber = require('../base')
7
+ const { amqpConnection } = require('#agentlib/symbols.js')
8
+ const isString = require('#agentlib/util/is-string.js')
9
+
10
+ /**
11
+ * This subscriber is two-fold:
12
+ * 1. capture the connection arguments and assign to symbol on connection to be used for all other amqplib subscribers
13
+ * 2. create a segment for the connect. Not this will attempt to create a segment but the base subscriber will decide if it is created or not, otherwise it'll return the current context
14
+ */
15
+ class ConnectSubscriber extends Subscriber {
16
+ constructor({ agent, logger }) {
17
+ super({ agent, logger, packageName: 'amqplib', channelName: 'nr_connect' })
18
+ this.events = ['asyncStart', 'asyncEnd']
19
+ this.requireActiveTx = false
20
+ this.callback = -1
21
+ }
22
+
23
+ handler(data, ctx) {
24
+ this.parameters = this.parseConnectionArgs(data.arguments)
25
+ return this.createSegment({
26
+ name: 'amqplib.connect',
27
+ ctx
28
+ })
29
+ }
30
+
31
+ asyncStart(data) {
32
+ if (this.parameters && data.result) {
33
+ data.result[amqpConnection] = this.parameters
34
+ }
35
+ super.asyncStart(data)
36
+ }
37
+
38
+ /**
39
+ * Parses the connection args to return host/port
40
+ *
41
+ * @param {array} args arguments passed to connect
42
+ * @returns {object} {host, port }
43
+ */
44
+ parseConnectionArgs(args = []) {
45
+ const [connArgs] = args
46
+ const params = {}
47
+ if (isString(connArgs)) {
48
+ try {
49
+ const parsedUrl = new URL(connArgs)
50
+ params.host = parsedUrl.hostname
51
+ if (parsedUrl.port) {
52
+ params.port = parseInt(parsedUrl.port, 10)
53
+ }
54
+ } catch (err) {
55
+ this.logger.error('Failed to parse connection string: %s', err.message)
56
+ }
57
+ } else {
58
+ params.port = connArgs.port || (connArgs.protocol === 'amqp' ? 5672 : 5671)
59
+ params.host = connArgs.hostname
60
+ }
61
+
62
+ return params
63
+ }
64
+
65
+ addAttributes(segment) {
66
+ for (let [key, value] of Object.entries(this.parameters)) {
67
+ // eslint-disable-next-line sonarjs/updated-loop-counter
68
+ key = key === 'port' ? 'port_path_or_id' : key
69
+ segment.addAttribute(key, value)
70
+ }
71
+ }
72
+ }
73
+
74
+ module.exports = ConnectSubscriber
@@ -0,0 +1,54 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ const MessageConsumer = require('../message-consumer')
7
+ const { getParameters, getParametersFromMessage, TEMP_RE } = require('./utils')
8
+
9
+ class ConsumeSubscriber extends MessageConsumer {
10
+ constructor({ agent, logger, channelName = 'nr_consume' }) {
11
+ super({ agent, logger, packageName: 'amqplib', channelName, system: 'RabbitMQ', type: 'Exchange', callback: 1, transport: 'AMQP' })
12
+ this.segmentName = 'amqplib.Channel#consume'
13
+ }
14
+
15
+ /**
16
+ * Handles the consumption of a message(s),
17
+ * this will create a new transaction every time this function is called.
18
+ * @param {object} data the data associated with the `asyncStart` event
19
+ */
20
+ asyncStart(data) {
21
+ const [queue] = data?.arguments
22
+ const message = data?.error
23
+ this.consumerParameters = getParametersFromMessage.call(this, { channel: data?.self, message })
24
+ this.headers = message?.properties?.headers
25
+ this.destination = this.getExchange(message?.fields)
26
+ this.queue = queue
27
+
28
+ super.asyncStart(data)
29
+ }
30
+
31
+ getExchange(fields = {}) {
32
+ const isDefault = fields.exchange === ''
33
+ let exchange = 'Default'
34
+ if (!isDefault) {
35
+ exchange = TEMP_RE.test(fields.exchange) ? null : fields.exchange
36
+ }
37
+
38
+ return exchange
39
+ }
40
+
41
+ /**
42
+ * Handles the creation of the segment if `channel.consume` is made within
43
+ * an active transaction.
44
+ * @param {object} data the data associated with the subscribed event
45
+ * @param {object} ctx context
46
+ */
47
+ handler(data, ctx) {
48
+ this.parameters = {}
49
+ getParameters.call(this, { params: this.parameters, channel: data.self })
50
+ return super.handler(data, ctx)
51
+ }
52
+ }
53
+
54
+ module.exports = ConsumeSubscriber
@@ -0,0 +1,16 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ const GetMessageSubscriber = require('./get')
7
+
8
+ class GetMessageCbSubscriber extends GetMessageSubscriber {
9
+ constructor({ agent, logger }) {
10
+ super({ agent, logger, packageName: 'amqplib', channelName: 'cb_get' })
11
+ this.events = ['asyncStart', 'asyncEnd']
12
+ this.callback = -1
13
+ }
14
+ }
15
+
16
+ module.exports = GetMessageCbSubscriber
@@ -0,0 +1,50 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ const Subscriber = require('../base')
7
+ const genericRecorder = require('#agentlib/metrics/recorders/generic.js')
8
+ const { getParametersFromMessage, EXCHANGE_PREFIX } = require('./utils')
9
+
10
+ class GetMessageSubscriber extends Subscriber {
11
+ constructor({ agent, logger, channelName = 'nr_get' }) {
12
+ super({ agent, logger, packageName: 'amqplib', channelName })
13
+ this.events = ['asyncEnd']
14
+ }
15
+
16
+ handler(data, ctx) {
17
+ const queue = this.extractQueue(data.arguments)
18
+ const name = `${EXCHANGE_PREFIX}/Consume/${queue}`
19
+ return this.createSegment({
20
+ name,
21
+ ctx,
22
+ recorder: genericRecorder
23
+ })
24
+ }
25
+
26
+ asyncEnd(data) {
27
+ const ctx = this.agent.tracer.getContext()
28
+ const segment = ctx?.segment
29
+ const { result: message } = data
30
+ if (!(message?.fields || message?.properties) || !segment) {
31
+ this.logger.trace('No message from consume, not capturing segment parameters')
32
+ super.asyncEnd(data)
33
+ return
34
+ }
35
+
36
+ const params = getParametersFromMessage.call(this, { message, channel: data?.self })
37
+ for (const [key, value] of Object.entries(params)) {
38
+ segment.addAttribute(key, value)
39
+ }
40
+
41
+ super.asyncEnd(data)
42
+ }
43
+
44
+ extractQueue(args) {
45
+ const [queue] = args
46
+ return `Named/${queue}`
47
+ }
48
+ }
49
+
50
+ module.exports = GetMessageSubscriber
@@ -0,0 +1,16 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ const PurgeQueueSubscriber = require('./purge-queue')
7
+
8
+ class PurgeQueueCbSubscriber extends PurgeQueueSubscriber {
9
+ constructor({ agent, logger }) {
10
+ super({ agent, logger, channelName: 'cb_purgeQueue' })
11
+ this.events = ['asyncStart', 'asyncEnd']
12
+ this.callback = -1
13
+ }
14
+ }
15
+
16
+ module.exports = PurgeQueueCbSubscriber
@@ -0,0 +1,37 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ const Subscriber = require('../base')
7
+ const genericRecorder = require('#agentlib/metrics/recorders/generic.js')
8
+ const { TEMP_RE, QUEUE_PREFIX } = require('./utils')
9
+
10
+ class PurgeQueueSubscriber extends Subscriber {
11
+ constructor({ agent, logger, channelName = 'nr_purgeQueue' }) {
12
+ super({ agent, logger, packageName: 'amqplib', channelName })
13
+ this.events = ['asyncEnd']
14
+ }
15
+
16
+ handler(data, ctx) {
17
+ const queue = this.extractQueue(data.arguments)
18
+ const name = `${QUEUE_PREFIX}/Purge/${queue}`
19
+ return this.createSegment({
20
+ name,
21
+ ctx,
22
+ recorder: genericRecorder
23
+ })
24
+ }
25
+
26
+ extractQueue(args) {
27
+ let [queue] = args
28
+
29
+ if (TEMP_RE.test(queue)) {
30
+ queue = null
31
+ }
32
+
33
+ return queue ? `Named/${queue}` : 'Temp'
34
+ }
35
+ }
36
+
37
+ module.exports = PurgeQueueSubscriber
@@ -0,0 +1,44 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ const MessageProducerSubscriber = require('../message-producer')
7
+ const { getParameters, TEMP_RE } = require('./utils')
8
+
9
+ class ChannelSubscriber extends MessageProducerSubscriber {
10
+ constructor({ agent, logger }) {
11
+ super({ agent, logger, packageName: 'amqplib', channelName: 'nr_sendMessage', system: 'RabbitMQ', type: 'Exchange' })
12
+ this.events = ['end']
13
+ }
14
+
15
+ handler(data, ctx) {
16
+ const [fields] = data.arguments
17
+
18
+ if (!fields) {
19
+ return ctx
20
+ }
21
+
22
+ this.destination = this.getExchange(fields)
23
+ this.parameters = this.getParameters({ fields, channel: data.self })
24
+ this.headers = fields?.headers
25
+ return super.handler(data, ctx)
26
+ }
27
+
28
+ getParameters({ fields = {}, channel = {} }) {
29
+ const params = {}
30
+ getParameters.call(this, { fields, channel, params })
31
+ return params
32
+ }
33
+
34
+ getExchange(fields = {}) {
35
+ const isDefault = fields.exchange === ''
36
+ let exchange = 'Default'
37
+ if (!isDefault) {
38
+ exchange = TEMP_RE.test(fields.exchange) ? null : fields.exchange
39
+ }
40
+ return exchange
41
+ }
42
+ }
43
+
44
+ module.exports = ChannelSubscriber
@@ -0,0 +1,20 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ const PropagationSubscriber = require('../propagation')
7
+
8
+ /**
9
+ * This subscriber is purely for propagation of async context.
10
+ * It breaks without binding the transaction and segment from the result of the previous handler.
11
+ * It attaches transaction because `propagateTx` is true
12
+ *
13
+ */
14
+ class SendOrEnqueueSubscriber extends PropagationSubscriber {
15
+ constructor({ agent, logger }) {
16
+ super({ agent, logger, packageName: 'amqplib', channelName: 'nr_sendOrEnqueue', callback: -1 })
17
+ }
18
+ }
19
+
20
+ module.exports = SendOrEnqueueSubscriber
@@ -0,0 +1,53 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+ const { amqpConnection } = require('#agentlib/symbols.js')
8
+
9
+ const TEMP_RE = /^amq\./
10
+ const RMQ_PREFIX = 'MessageBroker/RabbitMQ'
11
+ const EXCHANGE_PREFIX = `${RMQ_PREFIX}/Exchange`
12
+ const QUEUE_PREFIX = `${RMQ_PREFIX}/Queue`
13
+
14
+ function getParametersFromMessage({ message, channel }) {
15
+ const params = {}
16
+ getParameters.call(this, { channel, fields: message.fields, params })
17
+ getParameters.call(this, { channel, fields: message.properties, params })
18
+ return params
19
+ }
20
+
21
+ function getParameters({ fields = {}, channel = {}, params }) {
22
+ if (this.config.message_tracer.segment_parameters.enabled === false) {
23
+ this.logger.trace('Not capturing segment parameters, `message_tracer.segments_parameters.enabled` is false')
24
+ return
25
+ }
26
+
27
+ const { host, port } = channel?.connection?.[amqpConnection]
28
+
29
+ if (fields.routingKey) {
30
+ params.routing_key = fields.routingKey
31
+ }
32
+ if (fields.correlationId) {
33
+ params.correlation_id = fields.correlationId
34
+ }
35
+ if (fields.replyTo) {
36
+ params.reply_to = fields.replyTo
37
+ }
38
+ if (host) {
39
+ params.host = host
40
+ }
41
+
42
+ if (port) {
43
+ params.port = port
44
+ }
45
+ }
46
+
47
+ module.exports = {
48
+ getParameters,
49
+ getParametersFromMessage,
50
+ EXCHANGE_PREFIX,
51
+ QUEUE_PREFIX,
52
+ TEMP_RE
53
+ }
@@ -22,7 +22,7 @@ class ApplicationLogsSubscriber extends Subscriber {
22
22
  * but only once. You should call this in your handler.
23
23
  * We cannot recreate creating this metric on require of a logging library
24
24
  * because we no longer monkey patch but subscribe to events.
25
- * @param lib
25
+ * @param {string} lib name of library
26
26
  */
27
27
  createModuleUsageMetric(lib) {
28
28
  if (this.libMetricCreated === false) {
@@ -7,7 +7,8 @@
7
7
  // eslint-disable-next-line n/no-unsupported-features/node-builtins
8
8
  const { tracingChannel } = require('node:diagnostics_channel')
9
9
  const semver = require('semver')
10
- const NAMES = require('../metrics/names')
10
+ const NAMES = require('#agentlib/metrics/names.js')
11
+ const cat = require('#agentlib/util/cat.js')
11
12
  // Used for the `traceCallback` work.
12
13
  // This can be removed when we add true support into orchestrion
13
14
  const makeCall = (fn) => (...args) => fn.call(...args)
@@ -46,6 +47,7 @@ const ArrayPrototypeSplice = makeCall(Array.prototype.splice)
46
47
  * channel event names. This provides a namespace for the events we are
47
48
  * injecting into a module.
48
49
  * @property {boolean} [requireActiveTx=true] If true, the subscriber will only handle events when there is an active transaction.
50
+ * @property {boolean} [propagateTx=false] If true, it will assign the transaction from the `start.bindStore` to the context that is passed to all tracing channel events
49
51
  * @property {string} id A unique identifier for the subscriber, combining the prefix, package name, and channel name.
50
52
  * @property {TracingChannel} channel The tracing channel instance this subscriber will be monitoring.
51
53
  * @property {AsyncLocalStorage} store The async local storage instance used for context management.
@@ -63,6 +65,7 @@ class Subscriber {
63
65
  this.internal = false
64
66
  this.prefix = 'orchestrion:'
65
67
  this.requireActiveTx = true
68
+ this.propagateTx = false
66
69
  this.id = `${this.prefix}${this.packageName}:${this.channelName}`
67
70
  this.channel = tracingChannel(this.id)
68
71
  this.store = agent.tracer._contextManager._asyncLocalStorage
@@ -192,6 +195,17 @@ class Subscriber {
192
195
 
193
196
  }
194
197
 
198
+ /**
199
+ * Not all subscribers need change the context on `start`.
200
+ * This is defined on base to fulfill those use cases
201
+ * @param {object} data event passed to handler
202
+ * @param {Context} ctx context passed to handler
203
+ * @returns {Context} either new context or existing
204
+ */
205
+ handler(data, ctx) {
206
+ return ctx
207
+ }
208
+
195
209
  /**
196
210
  * Checks if the subscriber is enabled based on the agent's configuration.
197
211
  * @returns boolean if subscriber is enabled
@@ -227,6 +241,10 @@ class Subscriber {
227
241
  data.segment = result?.segment
228
242
  this.logger.trace('Adding segment %s to event context', data?.segment?.name)
229
243
  }
244
+
245
+ if (this.propagateTx) {
246
+ data.transaction = result?.transaction
247
+ }
230
248
  return result
231
249
  })
232
250
  }
@@ -275,6 +293,54 @@ class Subscriber {
275
293
  }
276
294
  }
277
295
 
296
+ end() {
297
+ const ctx = this.agent.tracer.getContext()
298
+ ctx?.segment?.touch()
299
+ }
300
+
301
+ /**
302
+ * Handles injecting w3c tracecontext in outgoing headers. If DT is disabled, and CAT is enabled
303
+ * it properly handles CAT.
304
+ *
305
+ * **Note**: This passes in the trace, segment and trace flags manually because this is called in the `start`
306
+ * right before a function is bound to context but segment is created for the function.
307
+ *
308
+ * @param {object} params to function
309
+ * @param {Context} params.ctx current context, not yet bound to context manager
310
+ * @param {object} params.headers headers for outgoing call
311
+ * @param {boolean} params.useMqNames flag to indicate use the MQ specific CAT header names
312
+ * @returns {void}
313
+ */
314
+ insertDTHeaders({ ctx, headers, useMqNames } = {}) {
315
+ const crossAppTracingEnabled = this.config.cross_application_tracer.enabled
316
+ const distributedTracingEnabled = this.config.distributed_tracing.enabled
317
+
318
+ if (!distributedTracingEnabled && !crossAppTracingEnabled) {
319
+ this.logger.trace('Distributed Tracing and CAT are both disabled, not adding headers.')
320
+ return
321
+ }
322
+
323
+ if (!headers) {
324
+ this.logger.debug('Missing headers object, not adding headers!')
325
+ return
326
+ }
327
+
328
+ const tx = ctx?.transaction
329
+ if (!tx?.isActive()) {
330
+ this.logger.trace('No active transaction found, not adding headers.')
331
+ return
332
+ }
333
+
334
+ if (distributedTracingEnabled) {
335
+ // we have to pass in traceId, segment id, and hard code traceFlags to 1
336
+ // because we're inserting headers right before the original function is bound.
337
+ const traceFlags = tx.sampled === true ? 1 : 0
338
+ tx.insertDistributedTraceHeaders(headers, null, { traceId: tx.traceId, spanId: ctx?.segment?.id, traceFlags })
339
+ } else {
340
+ cat.addCatHeaders(this.config, tx, headers, useMqNames)
341
+ }
342
+ }
343
+
278
344
  /*
279
345
  * Subscribes to the events defined in the `events` array.
280
346
  */
@@ -0,0 +1,49 @@
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 DbQuerySubscriber = require('../db-query')
9
+
10
+ /**
11
+ * Given the arguments for Cassandra's `batch` method, this finds the first
12
+ * query in the batch.
13
+ *
14
+ * @param {Array} args - original arguments passed to the batch function
15
+ * @returns {string} The query for this batch request.
16
+ */
17
+ function findBatchQueryArg(args) {
18
+ const sql = (args[0] && args[0][0]) || ''
19
+ return sql.query || sql
20
+ }
21
+
22
+ /**
23
+ * Subscribes to the `batch` event in `cassandra-driver`
24
+ * and extracts relevant information from the queries.
25
+ */
26
+ class CassandraBatchSubscriber extends DbQuerySubscriber {
27
+ constructor({ agent, logger, channelName = 'nr_batch' }) {
28
+ super({ agent, logger, channelName, packageName: 'cassandra-driver', system: 'Cassandra' })
29
+ this.events = ['asyncEnd']
30
+ this.isBatch = true
31
+ }
32
+
33
+ handler(data, ctx) {
34
+ const { self, arguments: args } = data
35
+ this.queryString = findBatchQueryArg(args)
36
+ this.setParameters(self)
37
+ return super.handler(data, ctx)
38
+ }
39
+
40
+ setParameters(self) {
41
+ this.parameters = {}
42
+ this.parameters.product = this.system
43
+ this.parameters.database_name = self?.keyspace
44
+ this.parameters.host = self?.options?.contactPoints?.[0]
45
+ this.parameters.port_path_or_id = self?.options?.protocolOptions?.port
46
+ }
47
+ }
48
+
49
+ module.exports = CassandraBatchSubscriber
@@ -0,0 +1,35 @@
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 DbOperationSubscriber = require('../db-operation')
9
+
10
+ /**
11
+ * Subscribes to the `connect` event in `cassandra-driver`'s `Client`.
12
+ */
13
+ class CassandraConnectSubscriber extends DbOperationSubscriber {
14
+ constructor({ agent, logger, channelName = 'nr_connect' }) {
15
+ super({ agent, logger, channelName, packageName: 'cassandra-driver', system: 'Cassandra' })
16
+ this.events = ['asyncEnd']
17
+ this.operation = 'connect'
18
+ }
19
+
20
+ handler(data, ctx) {
21
+ const { self } = data
22
+ this.setParameters(self)
23
+ return super.handler(data, ctx)
24
+ }
25
+
26
+ setParameters(self) {
27
+ this.parameters = {}
28
+ this.parameters.product = this.system
29
+ this.parameters.database_name = self?.keyspace
30
+ this.parameters.host = self?.options?.contactPoints?.[0]
31
+ this.parameters.port_path_or_id = self?.options?.protocolOptions?.port
32
+ }
33
+ }
34
+
35
+ module.exports = CassandraConnectSubscriber
@@ -0,0 +1,36 @@
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 DbQuerySubscriber = require('../db-query')
9
+
10
+ /**
11
+ * Subscribes to the `execute` event in `cassandra-driver`
12
+ * and extracts relevant information from the query.
13
+ */
14
+ class CassandraEachRowSubscriber extends DbQuerySubscriber {
15
+ constructor({ agent, logger, channelName = 'nr_eachRow' }) {
16
+ super({ agent, logger, channelName, packageName: 'cassandra-driver', system: 'Cassandra' })
17
+ this.events = ['asyncEnd']
18
+ }
19
+
20
+ handler(data, ctx) {
21
+ const { self, arguments: args } = data
22
+ this.queryString = args?.[0]
23
+ this.setParameters(self)
24
+ return super.handler(data, ctx)
25
+ }
26
+
27
+ setParameters(self) {
28
+ this.parameters = {}
29
+ this.parameters.product = this.system
30
+ this.parameters.database_name = self?.keyspace
31
+ this.parameters.host = self?.controlConnection?.connection?.address
32
+ this.parameters.port_path_or_id = self?.controlConnection?.connection?.port
33
+ }
34
+ }
35
+
36
+ module.exports = CassandraEachRowSubscriber
@@ -0,0 +1,36 @@
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 DbQuerySubscriber = require('../db-query')
9
+
10
+ /**
11
+ * Subscribes to the `execute` event in `cassandra-driver`
12
+ * and extracts relevant information from the query.
13
+ */
14
+ class CassandraExecuteSubscriber extends DbQuerySubscriber {
15
+ constructor({ agent, logger, channelName = 'nr_execute' }) {
16
+ super({ agent, logger, channelName, packageName: 'cassandra-driver', system: 'Cassandra' })
17
+ this.events = ['asyncEnd']
18
+ }
19
+
20
+ handler(data, ctx) {
21
+ const { self, arguments: args } = data
22
+ this.queryString = args?.[0]
23
+ this.setParameters(self)
24
+ return super.handler(data, ctx)
25
+ }
26
+
27
+ setParameters(self) {
28
+ this.parameters = {}
29
+ this.parameters.product = this.system
30
+ this.parameters.database_name = self?.keyspace
31
+ this.parameters.host = self?.controlConnection?.connection?.address
32
+ this.parameters.port_path_or_id = self?.controlConnection?.connection?.port
33
+ }
34
+ }
35
+
36
+ module.exports = CassandraExecuteSubscriber
@@ -0,0 +1,32 @@
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 DbOperationSubscriber = require('../db-operation')
9
+
10
+ /**
11
+ * Subscribes to the `shutdown` event in `cassandra-driver`.
12
+ */
13
+ class CassandraShutdownSubscriber extends DbOperationSubscriber {
14
+ constructor({ agent, logger, channelName = 'nr_shutdown' }) {
15
+ super({ agent, logger, channelName, packageName: 'cassandra-driver', system: 'Cassandra' })
16
+ this.events = ['asyncStart', 'asyncEnd']
17
+ this.operation = 'shutdown'
18
+ }
19
+
20
+ handler(data, ctx) {
21
+ const { self } = data
22
+ this.setParameters(self)
23
+ return super.handler(data, ctx)
24
+ }
25
+
26
+ setParameters() {
27
+ this.parameters = {}
28
+ this.parameters.product = this.system
29
+ }
30
+ }
31
+
32
+ module.exports = CassandraShutdownSubscriber