newrelic 13.3.2 → 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 (111) hide show
  1. package/NEWS.md +43 -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/metrics/index.js +3 -3
  35. package/lib/otel/setup.js +6 -0
  36. package/lib/otel/trace-propagator.js +1 -1
  37. package/lib/otel/traces/index.js +1 -1
  38. package/lib/prioritized-attributes.js +2 -2
  39. package/lib/serverless/aws-lambda.js +4 -4
  40. package/lib/shim/datastore-shim.js +1 -1
  41. package/lib/shim/message-shim/consume.js +1 -1
  42. package/lib/shim/message-shim/subscribe-consume.js +1 -2
  43. package/lib/shim/promise-shim.js +1 -3
  44. package/lib/shim/shim.js +3 -4
  45. package/lib/shim/transaction-shim.js +0 -1
  46. package/lib/shim/webframework-shim/common.js +2 -4
  47. package/lib/shim/webframework-shim/middleware.js +8 -8
  48. package/lib/spans/span-context.js +1 -1
  49. package/lib/spans/span-event-aggregator.js +2 -2
  50. package/lib/spans/span-event.js +6 -5
  51. package/lib/spans/streaming-span-attributes.js +1 -1
  52. package/lib/spans/streaming-span-event-aggregator.js +1 -2
  53. package/lib/stats/index.js +1 -1
  54. package/lib/subscriber-configs.js +2 -0
  55. package/lib/subscribers/amqplib/accept-message.js +14 -0
  56. package/lib/subscribers/amqplib/callback-model.js +39 -0
  57. package/lib/subscribers/amqplib/channel-model.js +39 -0
  58. package/lib/subscribers/amqplib/config.js +695 -0
  59. package/lib/subscribers/amqplib/connect.js +74 -0
  60. package/lib/subscribers/amqplib/consume.js +54 -0
  61. package/lib/subscribers/amqplib/get-cb.js +16 -0
  62. package/lib/subscribers/amqplib/get.js +50 -0
  63. package/lib/subscribers/amqplib/purge-queue-cb.js +16 -0
  64. package/lib/subscribers/amqplib/purge-queue.js +37 -0
  65. package/lib/subscribers/amqplib/send-message.js +44 -0
  66. package/lib/subscribers/amqplib/send-or-enqueue.js +20 -0
  67. package/lib/subscribers/amqplib/utils.js +53 -0
  68. package/lib/subscribers/application-logs.js +1 -1
  69. package/lib/subscribers/base.js +167 -9
  70. package/lib/subscribers/cassandra-driver/client-batch.js +49 -0
  71. package/lib/subscribers/cassandra-driver/client-connect.js +35 -0
  72. package/lib/subscribers/cassandra-driver/client-each-row.js +36 -0
  73. package/lib/subscribers/cassandra-driver/client-execute.js +36 -0
  74. package/lib/subscribers/cassandra-driver/client-shutdown.js +32 -0
  75. package/lib/subscribers/cassandra-driver/config.js +145 -0
  76. package/lib/subscribers/cassandra-driver/legacy-client-batch.js +22 -0
  77. package/lib/subscribers/cassandra-driver/legacy-client-connect.js +22 -0
  78. package/lib/subscribers/cassandra-driver/legacy-client-each-row.js +22 -0
  79. package/lib/subscribers/cassandra-driver/legacy-client-execute.js +21 -0
  80. package/lib/subscribers/cassandra-driver/legacy-client-shutdown.js +32 -0
  81. package/lib/subscribers/create-config.js +2 -4
  82. package/lib/subscribers/db-query.js +5 -1
  83. package/lib/subscribers/message-consumer.js +146 -0
  84. package/lib/subscribers/message-producer.js +60 -0
  85. package/lib/subscribers/meta-subscriber.js +69 -0
  86. package/lib/subscribers/openai/base.js +27 -1
  87. package/lib/subscribers/openai/config.js +6 -6
  88. package/lib/subscribers/openai/utils.js +9 -11
  89. package/lib/subscribers/propagation.js +43 -0
  90. package/lib/timer.js +1 -1
  91. package/lib/transaction/dt-payload.js +1 -1
  92. package/lib/transaction/handle.js +8 -9
  93. package/lib/transaction/name-state.js +11 -11
  94. package/lib/transaction/trace/segment.js +2 -3
  95. package/lib/util/attribute-types.js +1 -1
  96. package/lib/util/byte-limit.js +1 -1
  97. package/lib/util/cat.js +13 -13
  98. package/lib/util/flatten.js +2 -3
  99. package/lib/util/is-absolute-path.js +1 -1
  100. package/lib/util/is-string.js +15 -0
  101. package/lib/util/logger.js +6 -6
  102. package/lib/util/stream-sink.js +1 -1
  103. package/lib/utilization/docker-info.js +4 -3
  104. package/lib/w3c/traceparent.js +1 -1
  105. package/package.json +2 -2
  106. package/lib/instrumentation/amqplib/amqplib.js +0 -122
  107. package/lib/instrumentation/amqplib/channel-model.js +0 -124
  108. package/lib/instrumentation/amqplib/channel.js +0 -72
  109. package/lib/instrumentation/amqplib/nr-hooks.js +0 -21
  110. package/lib/instrumentation/amqplib/utils.js +0 -143
  111. package/lib/instrumentation/cassandra-driver.js +0 -131
@@ -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
@@ -0,0 +1,145 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ // there are diff subscribers for handling callback based code. In 4.4.0+ you can specify callbacks but they're all wrapped around promises so you don't have to wrap the callback in that case
7
+ const legacyBatchConfig = {
8
+ path: './cassandra-driver/legacy-client-batch.js',
9
+ instrumentations: [
10
+ {
11
+ channelName: 'nr_legacyBatch',
12
+ module: { name: 'cassandra-driver', versionRange: '>=3.4.0 <4.4.0', filePath: 'lib/client.js' },
13
+ functionQuery: {
14
+ expressionName: 'batch',
15
+ kind: 'Async'
16
+ }
17
+ },
18
+ ]
19
+ }
20
+
21
+ const legacyConnectConfig = {
22
+ path: './cassandra-driver/legacy-client-connect.js',
23
+ instrumentations: [
24
+ {
25
+ channelName: 'nr_legacyConnect',
26
+ module: { name: 'cassandra-driver', versionRange: '>=3.4.0 <4.4.0', filePath: 'lib/client.js' },
27
+ functionQuery: {
28
+ expressionName: 'connect',
29
+ kind: 'Async'
30
+ }
31
+ },
32
+ ]
33
+ }
34
+
35
+ const legacyExecuteConfig = {
36
+ path: './cassandra-driver/legacy-client-execute.js',
37
+ instrumentations: [
38
+ {
39
+ channelName: 'nr_legacyExecute',
40
+ module: { name: 'cassandra-driver', versionRange: '>=3.4.0 <4.4.0', filePath: 'lib/client.js' },
41
+ functionQuery: {
42
+ expressionName: 'execute',
43
+ kind: 'Async'
44
+ }
45
+ }
46
+ ]
47
+ }
48
+
49
+ const legacyEachRowConfig = {
50
+ path: './cassandra-driver/legacy-client-each-row.js',
51
+ instrumentations: [
52
+ {
53
+ channelName: 'nr_legacyEachRow',
54
+ module: { name: 'cassandra-driver', versionRange: '>=3.4.0 <4.4.0', filePath: 'lib/client.js' },
55
+ functionQuery: {
56
+ expressionName: 'eachRow',
57
+ kind: 'Async'
58
+ }
59
+ },
60
+ ]
61
+ }
62
+
63
+ const legacyShutdownConfig = {
64
+ path: './cassandra-driver/legacy-client-shutdown.js',
65
+ instrumentations: [
66
+ {
67
+ channelName: 'nr_legacyShutdown',
68
+ module: { name: 'cassandra-driver', versionRange: '>=3.4.0 <4.4.0', filePath: 'lib/client.js' },
69
+ functionQuery: {
70
+ expressionName: 'shutdown',
71
+ kind: 'Async'
72
+ }
73
+ },
74
+ ]
75
+ }
76
+
77
+ const batchConfig = {
78
+ path: './cassandra-driver/client-batch.js',
79
+ instrumentations: [
80
+ {
81
+ channelName: 'nr_batch',
82
+ module: { name: 'cassandra-driver', versionRange: '>=4.4.0', filePath: 'lib/client.js' },
83
+ functionQuery: {
84
+ expressionName: '_batch',
85
+ kind: 'Async'
86
+ }
87
+ },
88
+ ]
89
+ }
90
+
91
+ const connectConfig = {
92
+ path: './cassandra-driver/client-connect.js',
93
+ instrumentations: [
94
+ {
95
+ channelName: 'nr_connect',
96
+ module: { name: 'cassandra-driver', versionRange: '>=4.4.0', filePath: 'lib/client.js' },
97
+ functionQuery: {
98
+ expressionName: 'connect',
99
+ kind: 'Async'
100
+ }
101
+ },
102
+ ]
103
+ }
104
+
105
+ const executeConfig = {
106
+ path: './cassandra-driver/client-execute.js',
107
+ instrumentations: [
108
+ {
109
+ channelName: 'nr_execute',
110
+ module: { name: 'cassandra-driver', versionRange: '>=4.4.0', filePath: 'lib/client.js' },
111
+ functionQuery: {
112
+ expressionName: '_execute',
113
+ kind: 'Async'
114
+ }
115
+ }
116
+ ]
117
+ }
118
+
119
+ const shutdownConfig = {
120
+ path: './cassandra-driver/client-shutdown.js',
121
+ instrumentations: [
122
+ {
123
+ channelName: 'nr_shutdown',
124
+ module: { name: 'cassandra-driver', versionRange: '>=4.4.0', filePath: 'lib/client.js' },
125
+ functionQuery: {
126
+ expressionName: 'shutdown',
127
+ kind: 'Async'
128
+ }
129
+ },
130
+ ]
131
+ }
132
+
133
+ module.exports = {
134
+ 'cassandra-driver': [
135
+ batchConfig,
136
+ connectConfig,
137
+ executeConfig,
138
+ shutdownConfig,
139
+ legacyBatchConfig,
140
+ legacyEachRowConfig,
141
+ legacyConnectConfig,
142
+ legacyExecuteConfig,
143
+ legacyShutdownConfig
144
+ ]
145
+ }
@@ -0,0 +1,22 @@
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 CassandraBatchSubscriber = require('./client-batch')
9
+
10
+ /**
11
+ * Subscribes to the `batch` event in `cassandra-driver`
12
+ * and extracts relevant information from the queries.
13
+ */
14
+ class LegacyCassandraBatchSubscriber extends CassandraBatchSubscriber {
15
+ constructor({ agent, logger }) {
16
+ super({ agent, logger, channelName: 'nr_legacyBatch' })
17
+ this.events = ['asyncStart', 'asyncEnd']
18
+ this.callback = -1
19
+ }
20
+ }
21
+
22
+ module.exports = LegacyCassandraBatchSubscriber
@@ -0,0 +1,22 @@
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 CassandraConnectSubscriber = require('./client-connect')
9
+
10
+ /**
11
+ * Subscribes to the `connect` event in `cassandra-driver`'s `Client`.
12
+ */
13
+ class LegacyCassandraConnectSubscriber extends CassandraConnectSubscriber {
14
+ constructor({ agent, logger }) {
15
+ super({ agent, logger, channelName: 'nr_legacyConnect' })
16
+ this.events = ['asyncStart', 'asyncEnd']
17
+ this.internal = true
18
+ this.callback = -1
19
+ }
20
+ }
21
+
22
+ module.exports = LegacyCassandraConnectSubscriber
@@ -0,0 +1,22 @@
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 CassandraEachRowSubscriber = require('./client-each-row')
9
+
10
+ /**
11
+ * Subscribes to the `execute` event in `cassandra-driver`
12
+ * and extracts relevant information from the query.
13
+ */
14
+ class LegacyCassandraEachRowSubscriber extends CassandraEachRowSubscriber {
15
+ constructor({ agent, logger }) {
16
+ super({ agent, logger, channelName: 'nr_legacyEachRow' })
17
+ this.events = ['asyncStart', 'asyncEnd']
18
+ this.callback = -1
19
+ }
20
+ }
21
+
22
+ module.exports = LegacyCassandraEachRowSubscriber
@@ -0,0 +1,21 @@
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 CassandraExecuteSubscriber = require('./client-execute')
9
+ /**
10
+ * Subscribes to the `execute` event in `cassandra-driver`
11
+ * and extracts relevant information from the query.
12
+ */
13
+ class LegacyCassandraExecuteSubscriber extends CassandraExecuteSubscriber {
14
+ constructor({ agent, logger }) {
15
+ super({ agent, logger, channelName: 'nr_legacyExecute' })
16
+ this.events = ['asyncStart', 'asyncEnd']
17
+ this.callback = -1
18
+ }
19
+ }
20
+
21
+ module.exports = LegacyCassandraExecuteSubscriber
@@ -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 CassandraShutdownSubscriber = require('./client-shutdown')
9
+
10
+ /**
11
+ * Subscribes to the `shutdown` event in `cassandra-driver`.
12
+ */
13
+ class LegacyCassandraShutdownSubscriber extends CassandraShutdownSubscriber {
14
+ constructor({ agent, logger }) {
15
+ super({ agent, logger, channelName: 'nr_legacyShutdown' })
16
+ this.events = ['asyncStart', 'asyncEnd']
17
+ this.callback = -1
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 = LegacyCassandraShutdownSubscriber
@@ -12,16 +12,14 @@
12
12
  * @returns {Object} An object containing a Set of unique package names and an array of instrumentations.
13
13
  */
14
14
  function createSubscribersConfig (subscribers = []) {
15
- const packages = new Set()
16
15
  const instrumentations = []
17
- for (const [packageName, subscriberList] of Object.entries(subscribers)) {
18
- packages.add(packageName)
16
+ for (const subscriberList of Object.values(subscribers)) {
19
17
  for (const subscriber of subscriberList) {
20
18
  instrumentations.push(...subscriber.instrumentations)
21
19
  }
22
20
  }
23
21
 
24
- return { packages, instrumentations }
22
+ return { instrumentations }
25
23
  }
26
24
 
27
25
  module.exports = createSubscribersConfig
@@ -13,7 +13,11 @@ class DbQuerySubscriber extends DbSubscriber {
13
13
  handler(data, ctx) {
14
14
  const queryString = this.queryString
15
15
  const parsed = this.parseQueryString(queryString)
16
- const name = `${DB.STATEMENT}/${this.system}/${parsed.collection}/${parsed.operation}`
16
+ let name = `${DB.STATEMENT}/${this.system}/${parsed.collection}/${parsed.operation}`
17
+ if (this.isBatch) {
18
+ name += '/batch'
19
+ }
20
+
17
21
  return this.createSegment({
18
22
  name,
19
23
  ctx,
@@ -0,0 +1,146 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+ const Subscriber = require('./base')
8
+ const Transaction = require('#agentlib/transaction/index.js')
9
+ const messageTransactionRecorder = require('#agentlib/metrics/recorders/message-transaction.js')
10
+ const isString = require('#agentlib/util/is-string.js')
11
+
12
+ /**
13
+ * A message consumer does the following:
14
+ * 1. Calling consume creates a segment if in an active transaction
15
+ * 2. For every consumption, typically registered as a callback, it will create a transaction of type `message`, create a baseSegment, add both segment and trace attributes, and assign the `message-transaction` timeslice metrics
16
+ *
17
+ * @typedef {object} MessageConsumerParams
18
+ * @property {object} agent A New Relic Node.js agent instance.
19
+ * @property {object} logger An agent logger instance.
20
+ * @property {string} packageName The package name being instrumented.
21
+ * This is what a developer would provide to the `require` function.
22
+ * @property {string} channelName A unique name for the diagnostics channel
23
+ * that will be created and monitored.
24
+ * @property {string} system canonical mapping of system(i.e. - Kafka, RabbitMq, SNS, SQS)
25
+ * @property {string} type destinationType: Exchange, Queue, Topic
26
+ * @property {number} callback if consumer is callback based, indicates index of callback
27
+ * @property {string} transport identifier of the transport(see Transaction.TRANSPORT_TYPES)
28
+ */
29
+ class MessageConsumer extends Subscriber {
30
+ constructor({ agent, logger, packageName, channelName, system, type, callback, transport }) {
31
+ super({ agent, logger, packageName, channelName })
32
+ this.system = system
33
+ this.type = type
34
+ this.callback = callback
35
+ this.transport = transport
36
+ this.events = ['asyncStart', 'asyncEnd']
37
+ }
38
+
39
+ /**
40
+ * Used to create the consume segment if in an active transaction
41
+ * @param {object} data event passed to handler
42
+ * @param {Context} ctx agent context
43
+ * @returns {Context} new context or existing if not in active transaction
44
+ */
45
+ handler(data, ctx) {
46
+ return this.createSegment({
47
+ name: this.segmentName,
48
+ ctx
49
+ })
50
+ }
51
+
52
+ /**
53
+ * Used to add parameters to the consume segment
54
+ *
55
+ * @param {TraceSegment} segment to add attributes to
56
+ */
57
+ addAttributes(segment) {
58
+ for (const [key, value] of Object.entries(this.parameters)) {
59
+ segment.addAttribute(key, value)
60
+ }
61
+ }
62
+
63
+ enable() {
64
+ super.enable()
65
+ this.channel.asyncStart.bindStore(this.store, (data) => {
66
+ const ctx = this.agent.tracer.getContext()
67
+ const transaction = new Transaction(this.agent)
68
+ transaction.type = 'message'
69
+ return ctx.enterTransaction(transaction)
70
+ })
71
+ }
72
+
73
+ disable() {
74
+ super.disable()
75
+ this.channel.asyncStart.unbindStore(this.store)
76
+ }
77
+
78
+ /**
79
+ * Used to create a transaction for every consumption callback.
80
+ *
81
+ * @param {object} data event passed to asyncStart handler
82
+ */
83
+ asyncStart(data) {
84
+ const ctx = this.agent.tracer.getContext()
85
+ const tx = ctx?.transaction
86
+ tx.setPartialName(this.name)
87
+ // Note: this is not using `Subscriber.createSegment`
88
+ // this is because it enters the segment and returns a new ctx
89
+ // since this isn't in bindStore entering a new context doesn't
90
+ // bind it to the store
91
+ tx.baseSegment = this.agent.tracer.createSegment({
92
+ name: tx.getFullName(),
93
+ recorder: messageTransactionRecorder,
94
+ parent: tx.trace.root,
95
+ transaction: tx
96
+ })
97
+
98
+ this.addConsumeParameters(tx)
99
+
100
+ if (this.headers) {
101
+ tx.addDtCatHeaders({ headers: this.headers, transport: this.transport })
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Used to add parameters to trace and baseSegment of a consumption callback.
107
+ *
108
+ * @param {Transaction} tx to add attributes to
109
+ */
110
+ addConsumeParameters(tx) {
111
+ for (const [key, value] of Object.entries(this.consumerParameters)) {
112
+ if (['host', 'port'].includes(key)) {
113
+ tx.baseSegment.addAttribute(key, value)
114
+ } else {
115
+ tx.trace.attributes.addAttribute(Transaction.DESTINATIONS.NONE, `message.parameters.${key}`, value)
116
+ tx.baseSegment.attributes.addAttribute(Transaction.DESTINATIONS.NONE, `message.parameters.${key}`, value)
117
+ }
118
+
119
+ if (key === 'routing_key' && this.config.high_security === false) {
120
+ tx.trace.attributes.addAttribute(Transaction.DESTINATIONS.TRANS_COMMON, 'message.routingKey', value)
121
+ tx.baseSegment.addSpanAttribute('message.routingKey', value)
122
+ }
123
+ }
124
+ if (isString(this.queue) && this.config.high_security === false) {
125
+ tx.trace.attributes.addAttribute(Transaction.DESTINATIONS.TRANS_COMMON, 'message.queueName', this.queue)
126
+ tx.baseSegment.addSpanAttribute('message.queueName', this.queue)
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Used to name the transaction for every consumption callback.
132
+ *
133
+ * @returns {string} partial transaction name
134
+ */
135
+ get name() {
136
+ let name = `${this.system}/${this.type}`
137
+ if (this.destination) {
138
+ name += `/Named/${this.destination}`
139
+ } else {
140
+ name += '/Temp'
141
+ }
142
+ return name
143
+ }
144
+ }
145
+
146
+ module.exports = MessageConsumer
@@ -0,0 +1,60 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+ const Subscriber = require('./base')
8
+ const genericRecorder = require('#agentlib/metrics/recorders/generic.js')
9
+
10
+ /**
11
+ *
12
+ * Creates the segment for a message producer call. Injects appropriate DT/CAT
13
+ * headers if enabled.
14
+ *
15
+ * @typedef {object} MessageProducerParams
16
+ * @property {object} agent A New Relic Node.js agent instance.
17
+ * @property {object} logger An agent logger instance.
18
+ * @property {string} packageName The package name being instrumented.
19
+ * This is what a developer would provide to the `require` function.
20
+ * @property {string} channelName A unique name for the diagnostics channel
21
+ * that will be created and monitored.
22
+ * @property {string} system canonical mapping of system(i.e. - Kafka, RabbitMq, SNS, SQS)
23
+ * @property {string} type destinationType: Exchange, Queue, Topic
24
+ */
25
+ class MessageProducerSubscriber extends Subscriber {
26
+ constructor({ agent, logger, packageName, channelName, system, type }) {
27
+ super({ agent, logger, packageName, channelName })
28
+ this.system = system
29
+ this.prefix = 'MessageBroker'
30
+ this.type = type
31
+ }
32
+
33
+ handler(data, ctx) {
34
+ const newCtx = this.createSegment({
35
+ name: this.name,
36
+ ctx,
37
+ recorder: genericRecorder
38
+ })
39
+ this.insertDTHeaders({ ctx: newCtx, headers: this.headers, useMqNames: true })
40
+ return newCtx
41
+ }
42
+
43
+ get name() {
44
+ let name = `${this.prefix}/${this.system}/${this.type}/Produce`
45
+ if (this.destination) {
46
+ name += `/Named/${this.destination}`
47
+ } else {
48
+ name += '/Temp'
49
+ }
50
+ return name
51
+ }
52
+
53
+ addAttributes(segment) {
54
+ for (const [key, value] of Object.entries(this.parameters)) {
55
+ segment.addAttribute(key, value)
56
+ }
57
+ }
58
+ }
59
+
60
+ module.exports = MessageProducerSubscriber