newrelic 12.13.0 → 12.15.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 +68 -24
- package/api.js +0 -1
- package/lib/context-manager/async-local-context-manager.js +4 -2
- package/lib/context-manager/context.js +15 -48
- package/lib/feature_flags.js +2 -1
- package/lib/instrumentation/undici.js +3 -2
- package/lib/llm-events/aws-bedrock/bedrock-command.js +2 -2
- package/lib/metrics/recorders/message-transaction.js +5 -0
- package/lib/otel/attr-reconciler.js +52 -0
- package/lib/otel/constants.js +45 -1
- package/lib/otel/context.js +89 -0
- package/lib/otel/fake-span.js +28 -0
- package/lib/otel/segments/consumer.js +20 -18
- package/lib/otel/segments/database.js +1 -0
- package/lib/otel/segments/http-external.js +33 -3
- package/lib/otel/segments/internal.js +1 -0
- package/lib/otel/segments/producer.js +1 -0
- package/lib/otel/segments/server.js +7 -2
- package/lib/otel/segments/utils.js +30 -0
- package/lib/otel/setup.js +7 -8
- package/lib/otel/span-key-interceptor.js +30 -0
- package/lib/otel/span-processor.js +162 -67
- package/lib/otel/trace-propagator.js +12 -16
- package/lib/shim/message-shim/subscribe-consume.js +0 -1
- package/lib/spans/span-event.js +1 -1
- package/lib/transaction/index.js +6 -5
- package/lib/transaction/trace/segment.js +3 -2
- package/lib/transaction/tracecontext.js +13 -6
- package/lib/transaction/tracer/index.js +18 -2
- package/lib/util/logger.js +6 -3
- package/package.json +1 -1
package/NEWS.md
CHANGED
|
@@ -1,27 +1,71 @@
|
|
|
1
|
-
### v12.
|
|
2
|
-
|
|
3
|
-
#### Features
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Added
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
|
|
1
|
+
### v12.15.0 (2025-03-03)
|
|
2
|
+
|
|
3
|
+
#### Features
|
|
4
|
+
|
|
5
|
+
* Allowed undici error reporting to be disabled with feature flag `undici_error_tracking` ([#2956](https://github.com/newrelic/node-newrelic/pull/2956)) ([6278107](https://github.com/newrelic/node-newrelic/commit/6278107727678c8b45769190c6972c33057067e5))
|
|
6
|
+
* Thanks for your contribution @Voziv :tada:
|
|
7
|
+
* Added ability to propagate traceparent and tracestate on incoming server/consumer spans and outgoing client http and producer spans ([#2958](https://github.com/newrelic/node-newrelic/pull/2958)) ([258ad7d](https://github.com/newrelic/node-newrelic/commit/258ad7de7751df59cf6d68334e342f751bb3a934))
|
|
8
|
+
* Added server span transaction naming fallback to the url.path ([#2966](https://github.com/newrelic/node-newrelic/pull/2966)) ([770bf6f](https://github.com/newrelic/node-newrelic/commit/770bf6fd6ce26642e3fe08b2c1557dd826270516))
|
|
9
|
+
* Updated Context class to ensure bi-directional context propagation with opentelemetry bridge ([#2962](https://github.com/newrelic/node-newrelic/pull/2962)) ([dfeec5a](https://github.com/newrelic/node-newrelic/commit/dfeec5a0443723e7f799db4b9ea7bf4aed5b3bcc))
|
|
10
|
+
|
|
11
|
+
#### Code refactoring
|
|
12
|
+
|
|
13
|
+
* Separated context classes for agent in standard and opentelemetry bridge mode ([#2967](https://github.com/newrelic/node-newrelic/pull/2967)) ([d11c071](https://github.com/newrelic/node-newrelic/commit/d11c071be2504ef442e5aa19fed769ca7a20ceb5))
|
|
14
|
+
|
|
15
|
+
#### Documentation
|
|
16
|
+
|
|
17
|
+
* Updated compatibility report ([#2973](https://github.com/newrelic/node-newrelic/pull/2973)) ([7106c81](https://github.com/newrelic/node-newrelic/commit/7106c818b7add3220d256646cd816dee64efb80a))
|
|
18
|
+
|
|
19
|
+
#### Miscellaneous chores
|
|
20
|
+
|
|
21
|
+
* Added consumer attribute reconciliation ([#2957](https://github.com/newrelic/node-newrelic/pull/2957)) ([8943672](https://github.com/newrelic/node-newrelic/commit/894367239e5e8050da0a38347b81d7186cac0a1f))
|
|
22
|
+
* Added http external span attributes ([#2955](https://github.com/newrelic/node-newrelic/pull/2955)) ([ef697a5](https://github.com/newrelic/node-newrelic/commit/ef697a505ee23f5c2fb2bbe306e90561e66d75c1))
|
|
23
|
+
* Limit nestjs to enable successful CI ([#2972](https://github.com/newrelic/node-newrelic/pull/2972)) ([e9699cf](https://github.com/newrelic/node-newrelic/commit/e9699cf31ff758ff79f44c470bc5f11630117f1c))
|
|
24
|
+
* Refactored otel attribute reconciling ([#2964](https://github.com/newrelic/node-newrelic/pull/2964)) ([1b5ed2c](https://github.com/newrelic/node-newrelic/commit/1b5ed2c2f7c78cb413603124757b9b1c3412ab0a))
|
|
25
|
+
|
|
26
|
+
### v12.14.0 (2025-02-18)
|
|
27
|
+
|
|
28
|
+
#### Features
|
|
29
|
+
|
|
30
|
+
* 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))
|
|
31
|
+
* 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))
|
|
32
|
+
* 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))
|
|
33
|
+
|
|
34
|
+
#### Bug fixes
|
|
35
|
+
|
|
36
|
+
* 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))
|
|
37
|
+
* 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))
|
|
38
|
+
* 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))
|
|
39
|
+
* Thanks for your contribution @rChaoz 🎉
|
|
40
|
+
|
|
41
|
+
#### Documentation
|
|
42
|
+
|
|
43
|
+
* Updated compatibility report ([#2948](https://github.com/newrelic/node-newrelic/pull/2948)) ([8e32ed7](https://github.com/newrelic/node-newrelic/commit/8e32ed7d7bc456a0693485e7bdef955c25ead29a))
|
|
44
|
+
|
|
45
|
+
### v12.13.0 (2025-02-12)
|
|
46
|
+
|
|
47
|
+
#### Features
|
|
48
|
+
|
|
49
|
+
* Added timeslice metrics for synthesized server segments ([#2924](https://github.com/newrelic/node-newrelic/pull/2924)) ([f404585](https://github.com/newrelic/node-newrelic/commit/f4045855a1cdbfb74e3217daf8bfa125aa6fe2e4))
|
|
50
|
+
* Added timeslice metrics for synthesized producer segments ([#2939](https://github.com/newrelic/node-newrelic/pull/2939)) ([6832637](https://github.com/newrelic/node-newrelic/commit/68326377dcd23b574abae7c323ff93bc05c525ed))
|
|
51
|
+
* Added timeslice metrics for synthesized database segments ([#2922](https://github.com/newrelic/node-newrelic/pull/2922)) ([8606f78](https://github.com/newrelic/node-newrelic/commit/8606f789772b7651d0c46ad50dad3a1da74e5e9c))
|
|
52
|
+
* Propagate agent root context when opentelemetry `ROOT_CONTEXT` is passed in to trace propagator.([#2940](https://github.com/newrelic/node-newrelic/pull/2940)) ([b85111c](https://github.com/newrelic/node-newrelic/commit/b85111c46797dfbf399faf973e7a3e0ea6bbdc28))
|
|
53
|
+
* Added logic to handle properly naming and ending transactions for server spans.
|
|
54
|
+
* Provided ability to disable instrumentation for core Node.js libraries ([#2927](https://github.com/newrelic/node-newrelic/pull/2927)) ([2d232f1](https://github.com/newrelic/node-newrelic/commit/2d232f16c167e5f84b7b7898a6c5410d9cece55e))
|
|
55
|
+
|
|
56
|
+
#### Bug fixes
|
|
57
|
+
|
|
58
|
+
* Fixed `api.getTraceMetadata` to handle when there is an active transaction but not active segment ([#2944](https://github.com/newrelic/node-newrelic/pull/2944)) ([6db3b4d](https://github.com/newrelic/node-newrelic/commit/6db3b4d53a077a9738dd72d46e1ba1cee0d6af3f))
|
|
59
|
+
|
|
60
|
+
#### Documentation
|
|
61
|
+
|
|
62
|
+
* Updated compatibility report ([#2920](https://github.com/newrelic/node-newrelic/pull/2920)) ([c7ae8be](https://github.com/newrelic/node-newrelic/commit/c7ae8befafa4c91fab6804cd95e20f5a93546ea4))
|
|
63
|
+
|
|
64
|
+
#### Miscellaneous chores
|
|
65
|
+
|
|
66
|
+
* Localized OTEL attribute constants ([#2928](https://github.com/newrelic/node-newrelic/pull/2928)) ([965c41b](https://github.com/newrelic/node-newrelic/commit/965c41b3e64805ac14ae4dd36120b018ec5899f4))
|
|
67
|
+
* Updated import-in-the-middle version ([#2923](https://github.com/newrelic/node-newrelic/pull/2923)) ([aa2781f](https://github.com/newrelic/node-newrelic/commit/aa2781fd9c7bed08d590e33682729a92f21f43a5))
|
|
68
|
+
|
|
25
69
|
### v12.12.0 (2025-02-05)
|
|
26
70
|
|
|
27
71
|
#### 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')
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const { AsyncLocalStorage } = require('async_hooks')
|
|
9
9
|
const Context = require('./context')
|
|
10
|
+
const OtelContext = require('../otel/context')
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Class for managing state in the agent.
|
|
@@ -18,7 +19,8 @@ const Context = require('./context')
|
|
|
18
19
|
* @class
|
|
19
20
|
*/
|
|
20
21
|
class AsyncLocalContextManager {
|
|
21
|
-
constructor() {
|
|
22
|
+
constructor(isOtelBridgeMode) {
|
|
23
|
+
this.isOtelBridgeMode = isOtelBridgeMode
|
|
22
24
|
this._asyncLocalStorage = new AsyncLocalStorage()
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -28,7 +30,7 @@ class AsyncLocalContextManager {
|
|
|
28
30
|
* @returns {object} The current active context.
|
|
29
31
|
*/
|
|
30
32
|
getContext() {
|
|
31
|
-
return this._asyncLocalStorage.getStore() || new Context()
|
|
33
|
+
return this._asyncLocalStorage.getStore() || (this.isOtelBridgeMode ? new OtelContext() : new Context())
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/**
|
|
@@ -4,13 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
|
-
const { otelSynthesis } = require('../symbols')
|
|
8
7
|
|
|
9
8
|
module.exports = class Context {
|
|
10
|
-
constructor(transaction, segment
|
|
9
|
+
constructor(transaction, segment) {
|
|
11
10
|
this._transaction = transaction
|
|
12
11
|
this._segment = segment
|
|
13
|
-
this._otelCtx = parentContext ? new Map(parentContext) : new Map()
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
get segment() {
|
|
@@ -21,58 +19,27 @@ module.exports = class Context {
|
|
|
21
19
|
return this._transaction
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
enterSegment({ segment, transaction = this._transaction }) {
|
|
25
|
-
return new this.constructor(transaction, segment)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
enterTransaction(transaction) {
|
|
29
|
-
return new this.constructor(transaction, transaction.trace.root)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Required for bridging OTEL data into the agent.
|
|
34
|
-
*
|
|
35
|
-
* @param {string} key Stored entity name to retrieve.
|
|
36
|
-
*
|
|
37
|
-
* @returns {*} The stored value.
|
|
38
|
-
*/
|
|
39
|
-
getValue(key) {
|
|
40
|
-
return this._otelCtx.get(key)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
22
|
/**
|
|
44
|
-
*
|
|
23
|
+
* Constructs a new context from segment about to be bound to context manager
|
|
24
|
+
* along with the current transaction.
|
|
45
25
|
*
|
|
46
|
-
* @param {
|
|
47
|
-
* @param {
|
|
48
|
-
*
|
|
49
|
-
* @returns {
|
|
26
|
+
* @param {object} params to function
|
|
27
|
+
* @param {TraceSegment} params.segment segment to bind to context
|
|
28
|
+
* @param {Transaction} params.transaction active transaction
|
|
29
|
+
* @returns {Context} a newly constructed context
|
|
50
30
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (value[otelSynthesis] && value[otelSynthesis].segment && value[otelSynthesis].transaction) {
|
|
55
|
-
const { segment, transaction } = value[otelSynthesis]
|
|
56
|
-
segment.start()
|
|
57
|
-
ctx = new this.constructor(transaction, segment, this._otelCtx)
|
|
58
|
-
} else {
|
|
59
|
-
ctx = new this.constructor(this._transaction, this._segment, this._otelCtx)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
ctx._otelCtx.set(key, value)
|
|
63
|
-
return ctx
|
|
31
|
+
enterSegment({ segment, transaction = this._transaction }) {
|
|
32
|
+
return new this.constructor(transaction, segment, this._otelCtx)
|
|
64
33
|
}
|
|
65
34
|
|
|
66
35
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* @param {string} key Named value to remove from the store.
|
|
36
|
+
* Constructs a new context from transaction about to be bound to context manager.
|
|
37
|
+
* It uses the trace root segment as the segment in context.
|
|
70
38
|
*
|
|
71
|
-
* @
|
|
39
|
+
* @param {Transaction} transaction transaction to bind to context
|
|
40
|
+
* @returns {Context} a newly constructed context
|
|
72
41
|
*/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
ctx._otelCtx.delete(key)
|
|
76
|
-
return ctx
|
|
42
|
+
enterTransaction(transaction) {
|
|
43
|
+
return new this.constructor(transaction, transaction.trace.root)
|
|
77
44
|
}
|
|
78
45
|
}
|
package/lib/feature_flags.js
CHANGED
|
@@ -14,7 +14,8 @@ exports.prerelease = {
|
|
|
14
14
|
promise_segments: false,
|
|
15
15
|
reverse_naming_rules: false,
|
|
16
16
|
unresolved_promise_cleanup: true,
|
|
17
|
-
kafkajs_instrumentation: false
|
|
17
|
+
kafkajs_instrumentation: false,
|
|
18
|
+
undici_error_tracking: true
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
// flags that are no longer used for released features
|
|
@@ -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
|
}
|
|
@@ -192,6 +192,7 @@ function requestHeadersHook(shim, { request, response }) {
|
|
|
192
192
|
* @param {Error} params.error error from undici request
|
|
193
193
|
*/
|
|
194
194
|
function endAndRestoreSegment(shim, { request, error }) {
|
|
195
|
+
const { config } = shim.agent
|
|
195
196
|
const activeSegment = request[symbols.segment]
|
|
196
197
|
const parentSegment = request[symbols.parentSegment]
|
|
197
198
|
const tx = request[symbols.transaction]
|
|
@@ -199,7 +200,7 @@ function endAndRestoreSegment(shim, { request, error }) {
|
|
|
199
200
|
activeSegment.end()
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
if (error && tx) {
|
|
203
|
+
if (error && tx && config.feature_flag.undici_error_tracking === true) {
|
|
203
204
|
handleError(shim, tx, error)
|
|
204
205
|
}
|
|
205
206
|
|
|
@@ -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
|
|
@@ -0,0 +1,52 @@
|
|
|
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 urltils = require('../util/urltils')
|
|
9
|
+
const constants = require('./constants')
|
|
10
|
+
|
|
11
|
+
const hostKeys = [
|
|
12
|
+
constants.ATTR_NET_HOST_NAME,
|
|
13
|
+
constants.ATTR_NET_PEER_NAME,
|
|
14
|
+
constants.ATTR_SERVER_ADDRESS
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
class AttributeReconciler {
|
|
18
|
+
#agent
|
|
19
|
+
|
|
20
|
+
constructor({ agent }) {
|
|
21
|
+
this.#agent = agent
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#resolveHost(hostname) {
|
|
25
|
+
if (urltils.isLocalhost(hostname)) {
|
|
26
|
+
return this.#agent.config.getHostnameSafe(hostname)
|
|
27
|
+
}
|
|
28
|
+
return hostname
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#isHostnameKey(key) {
|
|
32
|
+
return hostKeys.includes(key)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
reconcile({ segment, otelSpan, mapper = {} }) {
|
|
36
|
+
for (const [key, srcValue] of Object.entries(otelSpan.attributes)) {
|
|
37
|
+
let value = srcValue
|
|
38
|
+
|
|
39
|
+
if (this.#isHostnameKey(key) === true) {
|
|
40
|
+
value = this.#resolveHost(srcValue)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (Object.prototype.hasOwnProperty.call(mapper, key) === true) {
|
|
44
|
+
mapper[key](value)
|
|
45
|
+
} else {
|
|
46
|
+
segment.addAttribute(key, value)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = AttributeReconciler
|
package/lib/otel/constants.js
CHANGED
|
@@ -111,7 +111,14 @@ module.exports = {
|
|
|
111
111
|
*
|
|
112
112
|
* @example 200
|
|
113
113
|
*/
|
|
114
|
-
|
|
114
|
+
ATTR_HTTP_RES_STATUS_CODE: 'http.response.status_code',
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The http response status code
|
|
118
|
+
*
|
|
119
|
+
* {@link ATTR_HTTP_RES_STATUS_CODE} is new and should be used instead.
|
|
120
|
+
*/
|
|
121
|
+
ATTR_HTTP_STATUS_CODE: 'http.status_code',
|
|
115
122
|
|
|
116
123
|
/**
|
|
117
124
|
* The http response status text
|
|
@@ -120,6 +127,13 @@ module.exports = {
|
|
|
120
127
|
*/
|
|
121
128
|
ATTR_HTTP_STATUS_TEXT: 'http.status_text',
|
|
122
129
|
|
|
130
|
+
/**
|
|
131
|
+
* The correlation id
|
|
132
|
+
*
|
|
133
|
+
* @example MyConversationId
|
|
134
|
+
*/
|
|
135
|
+
ATTR_MESSAGING_MESSAGE_CONVERSATION_ID: 'messaging.message.conversation_id',
|
|
136
|
+
|
|
123
137
|
/**
|
|
124
138
|
* The message destination name.
|
|
125
139
|
*
|
|
@@ -141,6 +155,29 @@ module.exports = {
|
|
|
141
155
|
*/
|
|
142
156
|
ATTR_MESSAGING_DESTINATION_NAME: 'messaging.destination.name',
|
|
143
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Identifies the type of messaging consumer operation.
|
|
160
|
+
*
|
|
161
|
+
* {@link ATTR_MESSAGING_OPERATION_NAME} is newer and should be used.
|
|
162
|
+
*/
|
|
163
|
+
ATTR_MESSAGING_OPERATION: 'messaging.operation',
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Name of the operation being performed. Value is specific to the
|
|
167
|
+
* target messaging system.
|
|
168
|
+
*
|
|
169
|
+
* @example ack
|
|
170
|
+
* @example send
|
|
171
|
+
*/
|
|
172
|
+
ATTR_MESSAGING_OPERATION_NAME: 'messaging.operation.name',
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* RabbitMQ message routing key
|
|
176
|
+
*
|
|
177
|
+
* @example myKey
|
|
178
|
+
*/
|
|
179
|
+
ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY: 'messaging.rabbitmq.destination.routing_key',
|
|
180
|
+
|
|
144
181
|
/**
|
|
145
182
|
* Target messaging system name.
|
|
146
183
|
*
|
|
@@ -222,6 +259,13 @@ module.exports = {
|
|
|
222
259
|
*/
|
|
223
260
|
ATTR_URL_SCHEME: 'url.scheme',
|
|
224
261
|
|
|
262
|
+
/**
|
|
263
|
+
* The URI query string.
|
|
264
|
+
*
|
|
265
|
+
* @example q=foo
|
|
266
|
+
*/
|
|
267
|
+
ATTR_URL_QUERY: 'url.query',
|
|
268
|
+
|
|
225
269
|
/* !!! Miscellaneous !!! */
|
|
226
270
|
/**
|
|
227
271
|
* Database system names.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
const { otelSynthesis } = require('../symbols')
|
|
8
|
+
const FakeSpan = require('./fake-span')
|
|
9
|
+
const Context = require('../context-manager/context')
|
|
10
|
+
|
|
11
|
+
module.exports = class OtelContext extends Context {
|
|
12
|
+
constructor(transaction, segment, parentContext) {
|
|
13
|
+
super(transaction, segment)
|
|
14
|
+
this._otelCtx = parentContext ? new Map(parentContext) : new Map()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Constructs a new context from segment about to be bound to context manager
|
|
19
|
+
* along with the current transaction. It will also bind a FakeSpan to the `_otelCtx`
|
|
20
|
+
*
|
|
21
|
+
* @param {object} params to function
|
|
22
|
+
* @param {TraceSegment} params.segment segment to bind to context
|
|
23
|
+
* @param {Transaction} params.transaction active transaction
|
|
24
|
+
* @returns {OtelContext} a newly constructed context
|
|
25
|
+
*/
|
|
26
|
+
enterSegment({ segment, transaction = this._transaction }) {
|
|
27
|
+
this._otelCtx.set(transaction.agent.otelSpanKey, new FakeSpan(segment, transaction))
|
|
28
|
+
return new this.constructor(transaction, segment, this._otelCtx)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Constructs a new context from transaction about to be bound to context manager.
|
|
33
|
+
* It uses the trace root segment as the segment in context. It will also bind a FakeSpan to the `_otelCtx`.
|
|
34
|
+
*
|
|
35
|
+
* @param {Transaction} transaction transaction to bind to context
|
|
36
|
+
* @returns {OtelContext} a newly constructed context
|
|
37
|
+
*/
|
|
38
|
+
enterTransaction(transaction) {
|
|
39
|
+
this._otelCtx.set(transaction.agent.otelSpanKey, new FakeSpan(transaction.trace.root, transaction))
|
|
40
|
+
return new this.constructor(transaction, transaction.trace.root, this._otelCtx)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Used to retrieve data from `_otelCtx`
|
|
45
|
+
*
|
|
46
|
+
* @param {string} key Stored entity name to retrieve.
|
|
47
|
+
*
|
|
48
|
+
* @returns {*} The stored value.
|
|
49
|
+
*/
|
|
50
|
+
getValue(key) {
|
|
51
|
+
return this._otelCtx.get(key)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Used to set data on `_otelCtx`
|
|
56
|
+
*
|
|
57
|
+
* @param {string} key Name for stored value.
|
|
58
|
+
* @param {*} value Value to store.
|
|
59
|
+
*
|
|
60
|
+
* @returns {OtelContext} The context object.
|
|
61
|
+
*/
|
|
62
|
+
setValue(key, value) {
|
|
63
|
+
let ctx
|
|
64
|
+
|
|
65
|
+
if (value[otelSynthesis] && value[otelSynthesis].segment && value[otelSynthesis].transaction) {
|
|
66
|
+
const { segment, transaction } = value[otelSynthesis]
|
|
67
|
+
segment.start()
|
|
68
|
+
ctx = new this.constructor(transaction, segment, this._otelCtx)
|
|
69
|
+
} else {
|
|
70
|
+
ctx = new this.constructor(this._transaction, this._segment, this._otelCtx)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
ctx._otelCtx.set(key, value)
|
|
74
|
+
return ctx
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Used to remove data from `_otelCtx`
|
|
79
|
+
*
|
|
80
|
+
* @param {string} key Named value to remove from the store.
|
|
81
|
+
*
|
|
82
|
+
* @returns {OtelContext} The context object.
|
|
83
|
+
*/
|
|
84
|
+
deleteValue(key) {
|
|
85
|
+
const ctx = new this.constructor(this._transaction, this._segment, this._otelCtx)
|
|
86
|
+
ctx._otelCtx.delete(key)
|
|
87
|
+
return ctx
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* In order to be able to return the appropriate span context
|
|
10
|
+
* wihtin otel bridge. We have to create fake spans for new relic
|
|
11
|
+
* segments. The only thing needed is a method for `spanContext`
|
|
12
|
+
* which should return the spanId(segment id) and traceId(transaction trace id).
|
|
13
|
+
* We hardcode traceFlags to 1.
|
|
14
|
+
*/
|
|
15
|
+
module.exports = class FakeSpan {
|
|
16
|
+
constructor(segment, transaction) {
|
|
17
|
+
this.segment = segment
|
|
18
|
+
this.transaction = transaction
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
spanContext() {
|
|
22
|
+
return {
|
|
23
|
+
spanId: this.segment.id,
|
|
24
|
+
traceId: this.transaction.traceId,
|
|
25
|
+
traceFlags: 1
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -13,34 +13,36 @@ module.exports = createConsumerSegment
|
|
|
13
13
|
// attributes according to our own internal specs.
|
|
14
14
|
|
|
15
15
|
const Transaction = require('../../transaction/')
|
|
16
|
-
const
|
|
16
|
+
const recorder = require('../../metrics/recorders/message-transaction')
|
|
17
|
+
const { TYPES } = Transaction
|
|
18
|
+
const { propagateTraceContext } = require('./utils')
|
|
17
19
|
|
|
18
20
|
const {
|
|
19
21
|
ATTR_MESSAGING_DESTINATION,
|
|
20
22
|
ATTR_MESSAGING_DESTINATION_KIND,
|
|
23
|
+
ATTR_MESSAGING_DESTINATION_NAME,
|
|
21
24
|
ATTR_MESSAGING_SYSTEM
|
|
22
25
|
} = require('../constants')
|
|
23
26
|
|
|
24
27
|
function createConsumerSegment(agent, otelSpan) {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
// )
|
|
40
|
-
transaction.name = segmentName
|
|
28
|
+
const attrs = otelSpan.attributes
|
|
29
|
+
const spanContext = otelSpan.spanContext()
|
|
30
|
+
const transaction = new Transaction(agent, spanContext?.traceId)
|
|
31
|
+
transaction.type = TYPES.MESSAGE
|
|
32
|
+
|
|
33
|
+
const system = attrs[ATTR_MESSAGING_SYSTEM] ?? 'unknown'
|
|
34
|
+
// _NAME is the current preferred attribute with semantic conventions >=1.3.0.
|
|
35
|
+
const destination = attrs[ATTR_MESSAGING_DESTINATION_NAME] ?? attrs[ATTR_MESSAGING_DESTINATION] ?? 'unknown'
|
|
36
|
+
const destKind = attrs[ATTR_MESSAGING_DESTINATION_KIND] ?? 'unknown'
|
|
37
|
+
const segmentName = `${system}/${destKind}/Named/${destination}`
|
|
38
|
+
|
|
39
|
+
transaction.setPartialName(segmentName)
|
|
40
|
+
propagateTraceContext({ transaction, otelSpan, transport: system })
|
|
41
41
|
|
|
42
42
|
const segment = agent.tracer.createSegment({
|
|
43
|
-
|
|
43
|
+
id: spanContext?.spanId,
|
|
44
|
+
recorder,
|
|
45
|
+
name: transaction.getFullName(),
|
|
44
46
|
parent: transaction.trace.root,
|
|
45
47
|
transaction
|
|
46
48
|
})
|
|
@@ -29,6 +29,7 @@ module.exports = function createDbSegment(agent, otelSpan) {
|
|
|
29
29
|
const parsed = parseStatement(agent.config, otelSpan, system)
|
|
30
30
|
const { name, operation } = setName(parsed)
|
|
31
31
|
const segment = agent.tracer.createSegment({
|
|
32
|
+
id: otelSpan?.spanContext()?.spanId,
|
|
32
33
|
name,
|
|
33
34
|
recorder: getRecorder({ operation, parsed, system }),
|
|
34
35
|
parent: context.segment,
|
|
@@ -7,20 +7,50 @@
|
|
|
7
7
|
|
|
8
8
|
const NAMES = require('../../metrics/names')
|
|
9
9
|
const recordExternal = require('../../metrics/recorders/http_external')
|
|
10
|
+
const urltils = require('../../util/urltils')
|
|
10
11
|
|
|
11
12
|
const {
|
|
12
|
-
|
|
13
|
+
ATTR_FULL_URL,
|
|
14
|
+
ATTR_HTTP_URL,
|
|
15
|
+
ATTR_HTTP_METHOD,
|
|
16
|
+
ATTR_HTTP_REQUEST_METHOD,
|
|
17
|
+
ATTR_NET_PEER_NAME,
|
|
18
|
+
ATTR_SERVER_ADDRESS
|
|
13
19
|
} = require('../constants')
|
|
14
20
|
|
|
15
21
|
module.exports = function createHttpExternalSegment(agent, otelSpan) {
|
|
16
22
|
const context = agent.tracer.getContext()
|
|
17
|
-
const
|
|
18
|
-
const
|
|
23
|
+
const method = otelSpan.attributes[ATTR_HTTP_REQUEST_METHOD] || otelSpan.attributes[ATTR_HTTP_METHOD]
|
|
24
|
+
const host = otelSpan.attributes[ATTR_SERVER_ADDRESS] || otelSpan.attributes[ATTR_NET_PEER_NAME] || 'Unknown'
|
|
25
|
+
|
|
26
|
+
const url = otelSpan.attributes[ATTR_FULL_URL] || otelSpan.attributes[ATTR_HTTP_URL]
|
|
27
|
+
let name = NAMES.EXTERNAL.PREFIX + host
|
|
28
|
+
let parsedUrl
|
|
29
|
+
let obfuscatedPath
|
|
30
|
+
if (url) {
|
|
31
|
+
parsedUrl = new URL(url)
|
|
32
|
+
obfuscatedPath = urltils.obfuscatePath(agent.config, parsedUrl.pathname)
|
|
33
|
+
name += obfuscatedPath
|
|
34
|
+
}
|
|
35
|
+
|
|
19
36
|
const segment = agent.tracer.createSegment({
|
|
37
|
+
id: otelSpan?.spanContext()?.spanId,
|
|
20
38
|
name,
|
|
21
39
|
recorder: recordExternal(host, 'http'),
|
|
22
40
|
parent: context.segment,
|
|
23
41
|
transaction: context.transaction
|
|
24
42
|
})
|
|
43
|
+
|
|
44
|
+
if (parsedUrl && segment) {
|
|
45
|
+
segment.captureExternalAttributes({
|
|
46
|
+
protocol: parsedUrl.protocol,
|
|
47
|
+
hostname: parsedUrl.hostname,
|
|
48
|
+
host: parsedUrl.host,
|
|
49
|
+
method,
|
|
50
|
+
port: parsedUrl.port,
|
|
51
|
+
path: obfuscatedPath,
|
|
52
|
+
queryParams: Object.fromEntries(parsedUrl.searchParams.entries())
|
|
53
|
+
})
|
|
54
|
+
}
|
|
25
55
|
return { segment, transaction: context.transaction }
|
|
26
56
|
}
|
|
@@ -10,6 +10,7 @@ module.exports = function createInternalSegment(agent, otelSpan) {
|
|
|
10
10
|
const context = agent.tracer.getContext()
|
|
11
11
|
const name = otelSpan.name
|
|
12
12
|
const segment = agent.tracer.createSegment({
|
|
13
|
+
id: otelSpan?.spanContext()?.spanId,
|
|
13
14
|
name,
|
|
14
15
|
parent: context.segment,
|
|
15
16
|
recorder: customRecorder,
|