newrelic 12.13.0 → 12.14.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 +19 -0
- package/api.js +0 -1
- package/lib/instrumentation/undici.js +1 -1
- package/lib/llm-events/aws-bedrock/bedrock-command.js +2 -2
- package/lib/metrics/recorders/message-transaction.js +5 -0
- package/lib/otel/constants.js +30 -0
- package/lib/otel/segments/consumer.js +6 -4
- package/lib/otel/span-processor.js +26 -0
- package/lib/util/logger.js +6 -3
- package/package.json +1 -1
package/NEWS.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
### v12.14.0 (2025-02-18)
|
|
2
|
+
|
|
3
|
+
#### Features
|
|
4
|
+
|
|
5
|
+
* Added support for region-prefixed Bedrock models ([#2947](https://github.com/newrelic/node-newrelic/pull/2947)) ([6acf535](https://github.com/newrelic/node-newrelic/commit/6acf5354d1e7f6786da88c0078699e82a714551d))
|
|
6
|
+
* Added attribute reconciliation for message producer spans ([#2942](https://github.com/newrelic/node-newrelic/pull/2942)) ([a9ba396](https://github.com/newrelic/node-newrelic/commit/a9ba39613c78b89c047ca35a218c2eedbb01e3d2))
|
|
7
|
+
* Added timeslice metrics for synthesized consumer segments ([#2938](https://github.com/newrelic/node-newrelic/pull/2938)) ([acfe953](https://github.com/newrelic/node-newrelic/commit/acfe953cf7a656fafb69104384c72f94ddc13000))
|
|
8
|
+
|
|
9
|
+
#### Bug fixes
|
|
10
|
+
|
|
11
|
+
* Fixed queueing of logs from child loggers ([#2945](https://github.com/newrelic/node-newrelic/pull/2945)) ([888cfe8](https://github.com/newrelic/node-newrelic/commit/888cfe84a1ffaf0287682cc9099a2fa762f7a37f))
|
|
12
|
+
* Fixed undici/fetch instrumentation to properly assign the parent-id portion of the `traceparent` header on outgoing requests to the active http external span id ([#2951](https://github.com/newrelic/node-newrelic/pull/2951)) ([bc714cf](https://github.com/newrelic/node-newrelic/commit/bc714cfa8fe6754abf25eb556b8f804c9982f986))
|
|
13
|
+
* Updated `api.recordLogEvent` to no longer truncate the message key in log event ([#2949](https://github.com/newrelic/node-newrelic/pull/2949)) ([8dd557b](https://github.com/newrelic/node-newrelic/commit/8dd557b4dfca2505b7e9f7f030ab094a6ae8019a))
|
|
14
|
+
* Thanks for your contribution @rChaoz 🎉
|
|
15
|
+
|
|
16
|
+
#### Documentation
|
|
17
|
+
|
|
18
|
+
* Updated compatibility report ([#2948](https://github.com/newrelic/node-newrelic/pull/2948)) ([8e32ed7](https://github.com/newrelic/node-newrelic/commit/8e32ed7d7bc456a0693485e7bdef955c25ead29a))
|
|
19
|
+
|
|
1
20
|
### v12.13.0 (2025-02-12)
|
|
2
21
|
|
|
3
22
|
#### Features
|
package/api.js
CHANGED
|
@@ -513,7 +513,6 @@ API.prototype.recordLogEvent = function recordLogEvent(logEvent = {}) {
|
|
|
513
513
|
)
|
|
514
514
|
return
|
|
515
515
|
}
|
|
516
|
-
logEvent.message = applicationLogging.truncate(logEvent.message)
|
|
517
516
|
|
|
518
517
|
if (!logEvent.level) {
|
|
519
518
|
logger.debug('no log level set, setting it to UNKNOWN')
|
|
@@ -136,8 +136,8 @@ function requestCreateHook(shim, { request }) {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
try {
|
|
139
|
-
addDTHeaders({ transaction, config, request })
|
|
140
139
|
createExternalSegment({ shim, request, segment })
|
|
140
|
+
addDTHeaders({ transaction, config, request })
|
|
141
141
|
} catch (err) {
|
|
142
142
|
logger.warn(err, 'Unable to create external segment')
|
|
143
143
|
}
|
|
@@ -124,11 +124,11 @@ class BedrockCommand {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
isClaude() {
|
|
127
|
-
return this.#modelId.startsWith('anthropic.claude-v')
|
|
127
|
+
return this.#modelId.split('.').slice(-2).join('.').startsWith('anthropic.claude-v')
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
isClaude3() {
|
|
131
|
-
return this.#modelId.startsWith('anthropic.claude-3')
|
|
131
|
+
return this.#modelId.split('.').slice(-2).join('.').startsWith('anthropic.claude-3')
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
isCohere() {
|
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
const NAMES = require('../../metrics/names.js')
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @param {TraceSegment} segment
|
|
12
|
+
* @param {object} scope
|
|
13
|
+
* @param {Transaction} tx
|
|
14
|
+
*/
|
|
10
15
|
function recordMessageTransaction(segment, scope, tx) {
|
|
11
16
|
if (tx.type !== 'message' || tx.baseSegment !== segment) {
|
|
12
17
|
return
|
package/lib/otel/constants.js
CHANGED
|
@@ -120,6 +120,13 @@ module.exports = {
|
|
|
120
120
|
*/
|
|
121
121
|
ATTR_HTTP_STATUS_TEXT: 'http.status_text',
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* The correlation id
|
|
125
|
+
*
|
|
126
|
+
* @example MyConversationId
|
|
127
|
+
*/
|
|
128
|
+
ATTR_MESSAGING_MESSAGE_CONVERSATION_ID: 'messaging.message.conversation_id',
|
|
129
|
+
|
|
123
130
|
/**
|
|
124
131
|
* The message destination name.
|
|
125
132
|
*
|
|
@@ -141,6 +148,29 @@ module.exports = {
|
|
|
141
148
|
*/
|
|
142
149
|
ATTR_MESSAGING_DESTINATION_NAME: 'messaging.destination.name',
|
|
143
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Identifies the type of messaging consumer operation.
|
|
153
|
+
*
|
|
154
|
+
* {@link ATTR_MESSAGING_OPERATION_NAME} is newer and should be used.
|
|
155
|
+
*/
|
|
156
|
+
ATTR_MESSAGING_OPERATION: 'messaging.operation',
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Name of the operation being performed. Value is specific to the
|
|
160
|
+
* target messaging system.
|
|
161
|
+
*
|
|
162
|
+
* @example ack
|
|
163
|
+
* @example send
|
|
164
|
+
*/
|
|
165
|
+
ATTR_MESSAGING_OPERATION_NAME: 'messaging.operation.name',
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* RabbitMQ message routing key
|
|
169
|
+
*
|
|
170
|
+
* @example myKey
|
|
171
|
+
*/
|
|
172
|
+
ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY: 'messaging.rabbitmq.destination.routing_key',
|
|
173
|
+
|
|
144
174
|
/**
|
|
145
175
|
* Target messaging system name.
|
|
146
176
|
*
|
|
@@ -13,6 +13,7 @@ module.exports = createConsumerSegment
|
|
|
13
13
|
// attributes according to our own internal specs.
|
|
14
14
|
|
|
15
15
|
const Transaction = require('../../transaction/')
|
|
16
|
+
const recorder = require('../../metrics/recorders/message-transaction')
|
|
16
17
|
const { DESTINATIONS, TYPES } = Transaction
|
|
17
18
|
|
|
18
19
|
const {
|
|
@@ -23,12 +24,12 @@ const {
|
|
|
23
24
|
|
|
24
25
|
function createConsumerSegment(agent, otelSpan) {
|
|
25
26
|
const transaction = new Transaction(agent)
|
|
26
|
-
transaction.type = TYPES.
|
|
27
|
+
transaction.type = TYPES.MESSAGE
|
|
27
28
|
|
|
28
29
|
const system = otelSpan.attributes[ATTR_MESSAGING_SYSTEM] ?? 'unknown'
|
|
29
30
|
const destination = otelSpan.attributes[ATTR_MESSAGING_DESTINATION] ?? 'unknown'
|
|
30
31
|
const destKind = otelSpan.attributes[ATTR_MESSAGING_DESTINATION_KIND] ?? 'unknown'
|
|
31
|
-
const segmentName =
|
|
32
|
+
const segmentName = `${system}/${destKind}/Named/${destination}`
|
|
32
33
|
|
|
33
34
|
const txAttrs = transaction.trace.attributes
|
|
34
35
|
txAttrs.addAttribute(DESTINATIONS.TRANS_SCOPE, 'message.queueName', destination)
|
|
@@ -37,10 +38,11 @@ function createConsumerSegment(agent, otelSpan) {
|
|
|
37
38
|
// 'host',
|
|
38
39
|
//
|
|
39
40
|
// )
|
|
40
|
-
transaction.
|
|
41
|
+
transaction.setPartialName(segmentName)
|
|
41
42
|
|
|
42
43
|
const segment = agent.tracer.createSegment({
|
|
43
|
-
|
|
44
|
+
recorder,
|
|
45
|
+
name: transaction.getFullName(),
|
|
44
46
|
parent: transaction.trace.root,
|
|
45
47
|
transaction
|
|
46
48
|
})
|
|
@@ -17,6 +17,8 @@ const {
|
|
|
17
17
|
ATTR_HTTP_ROUTE,
|
|
18
18
|
ATTR_HTTP_STATUS_CODE,
|
|
19
19
|
ATTR_HTTP_STATUS_TEXT,
|
|
20
|
+
ATTR_MESSAGING_MESSAGE_CONVERSATION_ID,
|
|
21
|
+
ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY,
|
|
20
22
|
ATTR_NET_PEER_NAME,
|
|
21
23
|
ATTR_NET_PEER_PORT,
|
|
22
24
|
ATTR_NET_HOST_NAME,
|
|
@@ -69,6 +71,8 @@ module.exports = class NrSpanProcessor {
|
|
|
69
71
|
this.reconcileServerAttributes({ segment, span, transaction })
|
|
70
72
|
} else if (span.kind === SpanKind.CLIENT && span.attributes[ATTR_DB_SYSTEM]) {
|
|
71
73
|
this.reconcileDbAttributes({ segment, span })
|
|
74
|
+
} else if (span.kind === SpanKind.PRODUCER) {
|
|
75
|
+
this.reconcileProducerAttributes({ segment, span })
|
|
72
76
|
}
|
|
73
77
|
// TODO: add http external checks
|
|
74
78
|
}
|
|
@@ -159,4 +163,26 @@ module.exports = class NrSpanProcessor {
|
|
|
159
163
|
segment.addAttribute(key, sanitized)
|
|
160
164
|
}
|
|
161
165
|
}
|
|
166
|
+
|
|
167
|
+
reconcileProducerAttributes({ segment, span }) {
|
|
168
|
+
for (const [prop, value] of Object.entries(span.attributes)) {
|
|
169
|
+
let key = prop
|
|
170
|
+
let sanitized = value
|
|
171
|
+
|
|
172
|
+
if (prop === ATTR_SERVER_ADDRESS) {
|
|
173
|
+
key = 'host'
|
|
174
|
+
if (urltils.isLocalhost(sanitized)) {
|
|
175
|
+
sanitized = this.agent.config.getHostnameSafe(sanitized)
|
|
176
|
+
}
|
|
177
|
+
} else if (prop === ATTR_SERVER_PORT) {
|
|
178
|
+
key = 'port'
|
|
179
|
+
} else if (prop === ATTR_MESSAGING_MESSAGE_CONVERSATION_ID) {
|
|
180
|
+
key = 'correlation_id'
|
|
181
|
+
} else if (prop === ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY) {
|
|
182
|
+
key = 'routing_key'
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
segment.addAttribute(key, sanitized)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
162
188
|
}
|
package/lib/util/logger.js
CHANGED
|
@@ -94,7 +94,8 @@ Object.keys(LEVELS).forEach(function buildLevel(_level) {
|
|
|
94
94
|
|
|
95
95
|
function log(extra) {
|
|
96
96
|
if (!this.options.configured) {
|
|
97
|
-
|
|
97
|
+
// queue a log line with level, args passed to logger and their respective extras
|
|
98
|
+
this.logQueue.unshift({ level: _level, args: arguments, extra: this.extra })
|
|
98
99
|
return
|
|
99
100
|
}
|
|
100
101
|
|
|
@@ -175,8 +176,9 @@ Object.keys(LEVELS).forEach(function buildLevel(_level) {
|
|
|
175
176
|
|
|
176
177
|
Logger.prototype._flushQueuedLogs = function _flushQueuedLogs() {
|
|
177
178
|
while (this.logQueue.length) {
|
|
178
|
-
const { level, args } = this.logQueue.shift()
|
|
179
|
-
|
|
179
|
+
const { level, args, extra } = this.logQueue.shift()
|
|
180
|
+
// log an entry now that the logger has been configured
|
|
181
|
+
this[level](extra, ...args)
|
|
180
182
|
}
|
|
181
183
|
}
|
|
182
184
|
|
|
@@ -188,6 +190,7 @@ Logger.prototype.child = function child(extra) {
|
|
|
188
190
|
childLogger.extra = Object.assign(Object.create(null), this.extra, extra)
|
|
189
191
|
|
|
190
192
|
const parent = this
|
|
193
|
+
childLogger.logQueue = parent.logQueue
|
|
191
194
|
childLogger.options = parent.options
|
|
192
195
|
|
|
193
196
|
childLogger.write = function write(level, args, _extra) {
|