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.
- package/NEWS.md +22 -0
- package/THIRD_PARTY_NOTICES.md +11 -11
- package/esm-loader.mjs +2 -2
- package/lib/attributes.js +4 -4
- package/lib/collector/facts.js +1 -1
- package/lib/collector/http-agents.js +2 -2
- package/lib/collector/parse-response.js +9 -12
- package/lib/collector/remote-method.js +4 -5
- package/lib/config/formatters.js +2 -3
- package/lib/config/index.js +6 -6
- package/lib/harvester.js +1 -1
- package/lib/header-processing.js +3 -3
- package/lib/instrumentation/@node-redis/client.js +1 -1
- package/lib/instrumentation/aws-sdk/v2/instrumentation-helper.js +2 -2
- package/lib/instrumentation/aws-sdk/v3/common.js +5 -5
- package/lib/instrumentation/aws-sdk/v3/dynamodb.js +4 -4
- package/lib/instrumentation/aws-sdk/v3/sns.js +9 -9
- package/lib/instrumentation/aws-sdk/v3/sqs.js +6 -6
- package/lib/instrumentation/core/http-outbound.js +1 -2
- package/lib/instrumentation/fastify/spec-builders.js +11 -11
- package/lib/instrumentation/fastify.js +4 -4
- package/lib/instrumentation/memcached.js +4 -4
- package/lib/instrumentation/mongodb.js +2 -2
- package/lib/instrumentation/undici.js +3 -3
- package/lib/instrumentations.js +0 -2
- package/lib/llm-events/aws-bedrock/bedrock-response.js +6 -6
- package/lib/llm-events/aws-bedrock/converse-stream-handler.js +1 -1
- package/lib/llm-events/aws-bedrock/stream-handler.js +4 -4
- package/lib/metrics/index.js +2 -2
- package/lib/metrics/normalizer/rule.js +1 -1
- package/lib/metrics/normalizer.js +4 -5
- package/lib/metrics/recorders/message-transaction.js +3 -3
- package/lib/otel/context-manager.js +4 -4
- package/lib/otel/trace-propagator.js +1 -1
- package/lib/prioritized-attributes.js +2 -2
- package/lib/serverless/aws-lambda.js +4 -4
- package/lib/shim/datastore-shim.js +1 -1
- package/lib/shim/message-shim/consume.js +1 -1
- package/lib/shim/message-shim/subscribe-consume.js +1 -2
- package/lib/shim/promise-shim.js +1 -3
- package/lib/shim/shim.js +3 -4
- package/lib/shim/transaction-shim.js +0 -1
- package/lib/shim/webframework-shim/common.js +2 -4
- package/lib/shim/webframework-shim/middleware.js +8 -8
- package/lib/spans/span-context.js +1 -1
- package/lib/spans/span-event-aggregator.js +2 -2
- package/lib/spans/span-event.js +6 -5
- package/lib/spans/streaming-span-attributes.js +1 -1
- package/lib/spans/streaming-span-event-aggregator.js +1 -2
- package/lib/stats/index.js +1 -1
- package/lib/subscriber-configs.js +2 -0
- package/lib/subscribers/amqplib/accept-message.js +14 -0
- package/lib/subscribers/amqplib/callback-model.js +39 -0
- package/lib/subscribers/amqplib/channel-model.js +39 -0
- package/lib/subscribers/amqplib/config.js +695 -0
- package/lib/subscribers/amqplib/connect.js +74 -0
- package/lib/subscribers/amqplib/consume.js +54 -0
- package/lib/subscribers/amqplib/get-cb.js +16 -0
- package/lib/subscribers/amqplib/get.js +50 -0
- package/lib/subscribers/amqplib/purge-queue-cb.js +16 -0
- package/lib/subscribers/amqplib/purge-queue.js +37 -0
- package/lib/subscribers/amqplib/send-message.js +44 -0
- package/lib/subscribers/amqplib/send-or-enqueue.js +20 -0
- package/lib/subscribers/amqplib/utils.js +53 -0
- package/lib/subscribers/application-logs.js +1 -1
- package/lib/subscribers/base.js +67 -1
- package/lib/subscribers/cassandra-driver/client-batch.js +49 -0
- package/lib/subscribers/cassandra-driver/client-connect.js +35 -0
- package/lib/subscribers/cassandra-driver/client-each-row.js +36 -0
- package/lib/subscribers/cassandra-driver/client-execute.js +36 -0
- package/lib/subscribers/cassandra-driver/client-shutdown.js +32 -0
- package/lib/subscribers/cassandra-driver/config.js +145 -0
- package/lib/subscribers/cassandra-driver/legacy-client-batch.js +22 -0
- package/lib/subscribers/cassandra-driver/legacy-client-connect.js +22 -0
- package/lib/subscribers/cassandra-driver/legacy-client-each-row.js +22 -0
- package/lib/subscribers/cassandra-driver/legacy-client-execute.js +21 -0
- package/lib/subscribers/cassandra-driver/legacy-client-shutdown.js +32 -0
- package/lib/subscribers/create-config.js +2 -4
- package/lib/subscribers/db-query.js +5 -1
- package/lib/subscribers/message-consumer.js +146 -0
- package/lib/subscribers/message-producer.js +60 -0
- package/lib/subscribers/meta-subscriber.js +69 -0
- package/lib/subscribers/openai/base.js +27 -1
- package/lib/subscribers/openai/chat.js +0 -27
- package/lib/subscribers/openai/config.js +3 -3
- package/lib/subscribers/openai/utils.js +9 -11
- package/lib/subscribers/propagation.js +43 -0
- package/lib/timer.js +1 -1
- package/lib/transaction/dt-payload.js +1 -1
- package/lib/transaction/handle.js +8 -9
- package/lib/transaction/name-state.js +11 -11
- package/lib/transaction/trace/segment.js +2 -3
- package/lib/util/attribute-types.js +1 -1
- package/lib/util/byte-limit.js +1 -1
- package/lib/util/cat.js +13 -13
- package/lib/util/flatten.js +2 -3
- package/lib/util/is-absolute-path.js +1 -1
- package/lib/util/is-string.js +15 -0
- package/lib/util/logger.js +6 -6
- package/lib/util/stream-sink.js +1 -1
- package/lib/utilization/docker-info.js +4 -3
- package/lib/w3c/traceparent.js +1 -1
- package/package.json +2 -2
- package/lib/instrumentation/amqplib/amqplib.js +0 -122
- package/lib/instrumentation/amqplib/channel-model.js +0 -124
- package/lib/instrumentation/amqplib/channel.js +0 -72
- package/lib/instrumentation/amqplib/nr-hooks.js +0 -21
- package/lib/instrumentation/amqplib/utils.js +0 -143
- 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) {
|
package/lib/subscribers/base.js
CHANGED
|
@@ -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('
|
|
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
|