newrelic 12.14.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 CHANGED
@@ -1,46 +1,71 @@
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
-
20
- ### v12.13.0 (2025-02-12)
21
-
22
- #### Features
23
-
24
- * 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))
25
- * 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))
26
- * 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))
27
- * 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))
28
- * Added logic to handle properly naming and ending transactions for server spans.
29
- * 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))
30
-
31
- #### Bug fixes
32
-
33
- * 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))
34
-
35
- #### Documentation
36
-
37
- * Updated compatibility report ([#2920](https://github.com/newrelic/node-newrelic/pull/2920)) ([c7ae8be](https://github.com/newrelic/node-newrelic/commit/c7ae8befafa4c91fab6804cd95e20f5a93546ea4))
38
-
39
- #### Miscellaneous chores
40
-
41
- * Localized OTEL attribute constants ([#2928](https://github.com/newrelic/node-newrelic/pull/2928)) ([965c41b](https://github.com/newrelic/node-newrelic/commit/965c41b3e64805ac14ae4dd36120b018ec5899f4))
42
- * Updated import-in-the-middle version ([#2923](https://github.com/newrelic/node-newrelic/pull/2923)) ([aa2781f](https://github.com/newrelic/node-newrelic/commit/aa2781fd9c7bed08d590e33682729a92f21f43a5))
43
-
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
+
44
69
  ### v12.12.0 (2025-02-05)
45
70
 
46
71
  #### Features
@@ -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, parentContext) {
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
- * Required for bridging OTEL data into the agent.
23
+ * Constructs a new context from segment about to be bound to context manager
24
+ * along with the current transaction.
45
25
  *
46
- * @param {string} key Name for stored value.
47
- * @param {*} value Value to store.
48
- *
49
- * @returns {object} The context manager object.
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
- setValue(key, value) {
52
- let ctx
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
- * Required for bridging OTEL data into the agent.
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
- * @returns {object} The context manager object.
39
+ * @param {Transaction} transaction transaction to bind to context
40
+ * @returns {Context} a newly constructed context
72
41
  */
73
- deleteValue(key) {
74
- const ctx = new this.constructor(this._transaction, this._segment, this._otelCtx)
75
- ctx._otelCtx.delete(key)
76
- return ctx
42
+ enterTransaction(transaction) {
43
+ return new this.constructor(transaction, transaction.trace.root)
77
44
  }
78
45
  }
@@ -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
@@ -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
 
@@ -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
@@ -111,7 +111,14 @@ module.exports = {
111
111
  *
112
112
  * @example 200
113
113
  */
114
- ATTR_HTTP_STATUS_CODE: 'http.response.status_code',
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
@@ -252,6 +259,13 @@ module.exports = {
252
259
  */
253
260
  ATTR_URL_SCHEME: 'url.scheme',
254
261
 
262
+ /**
263
+ * The URI query string.
264
+ *
265
+ * @example q=foo
266
+ */
267
+ ATTR_URL_QUERY: 'url.query',
268
+
255
269
  /* !!! Miscellaneous !!! */
256
270
  /**
257
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
+ }
@@ -14,33 +14,33 @@ module.exports = createConsumerSegment
14
14
 
15
15
  const Transaction = require('../../transaction/')
16
16
  const recorder = require('../../metrics/recorders/message-transaction')
17
- const { DESTINATIONS, TYPES } = Transaction
17
+ const { TYPES } = Transaction
18
+ const { propagateTraceContext } = require('./utils')
18
19
 
19
20
  const {
20
21
  ATTR_MESSAGING_DESTINATION,
21
22
  ATTR_MESSAGING_DESTINATION_KIND,
23
+ ATTR_MESSAGING_DESTINATION_NAME,
22
24
  ATTR_MESSAGING_SYSTEM
23
25
  } = require('../constants')
24
26
 
25
27
  function createConsumerSegment(agent, otelSpan) {
26
- const transaction = new Transaction(agent)
28
+ const attrs = otelSpan.attributes
29
+ const spanContext = otelSpan.spanContext()
30
+ const transaction = new Transaction(agent, spanContext?.traceId)
27
31
  transaction.type = TYPES.MESSAGE
28
32
 
29
- const system = otelSpan.attributes[ATTR_MESSAGING_SYSTEM] ?? 'unknown'
30
- const destination = otelSpan.attributes[ATTR_MESSAGING_DESTINATION] ?? 'unknown'
31
- const destKind = otelSpan.attributes[ATTR_MESSAGING_DESTINATION_KIND] ?? 'unknown'
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'
32
37
  const segmentName = `${system}/${destKind}/Named/${destination}`
33
38
 
34
- const txAttrs = transaction.trace.attributes
35
- txAttrs.addAttribute(DESTINATIONS.TRANS_SCOPE, 'message.queueName', destination)
36
- // txAttrs.addAttribute(
37
- // DESTINATIONS.TRANS_SCOPE,
38
- // 'host',
39
- //
40
- // )
41
39
  transaction.setPartialName(segmentName)
40
+ propagateTraceContext({ transaction, otelSpan, transport: system })
42
41
 
43
42
  const segment = agent.tracer.createSegment({
43
+ id: spanContext?.spanId,
44
44
  recorder,
45
45
  name: transaction.getFullName(),
46
46
  parent: transaction.trace.root,
@@ -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
- ATTR_HTTP_HOST
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 host = otelSpan.attributes[ATTR_HTTP_HOST] || 'Unknown'
18
- const name = NAMES.EXTERNAL.PREFIX + host
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,
@@ -18,6 +18,7 @@ module.exports = function createProducerSegment(agent, otelSpan) {
18
18
  const name = setName(otelSpan)
19
19
 
20
20
  const segment = agent.tracer.createSegment({
21
+ id: otelSpan?.spanContext()?.spanId,
21
22
  name,
22
23
  recorder: genericRecorder,
23
24
  parent: context.segment,
@@ -10,6 +10,7 @@ const httpRecorder = require('../../metrics/recorders/http')
10
10
  const urltils = require('../../util/urltils')
11
11
  const url = require('node:url')
12
12
  const { NODEJS, ACTION_DELIMITER } = require('../../metrics/names')
13
+ const { propagateTraceContext } = require('./utils')
13
14
 
14
15
  const DESTINATION = Transaction.DESTINATIONS.TRANS_COMMON
15
16
  const {
@@ -22,10 +23,12 @@ const {
22
23
  } = require('../constants')
23
24
 
24
25
  module.exports = function createServerSegment(agent, otelSpan) {
25
- const transaction = new Transaction(agent)
26
+ const spanContext = otelSpan.spanContext()
27
+ const transaction = new Transaction(agent, spanContext?.traceId)
26
28
  transaction.type = 'web'
27
29
  transaction.nameState.setPrefix(NODEJS.PREFIX)
28
30
  transaction.nameState.setPrefix(ACTION_DELIMITER)
31
+ propagateTraceContext({ transaction, otelSpan, transport: 'HTTPS' })
29
32
  const rpcSystem = otelSpan.attributes[ATTR_RPC_SYSTEM]
30
33
  const httpMethod = otelSpan.attributes[ATTR_HTTP_METHOD] ?? otelSpan.attributes[ATTR_HTTP_REQUEST_METHOD]
31
34
  let segment
@@ -48,6 +51,7 @@ function rpcSegment({ agent, otelSpan, transaction, rpcSystem }) {
48
51
  transaction.nameState.setPrefix(rpcSystem)
49
52
  transaction.nameState.appendPath(transaction.url)
50
53
  const segment = agent.tracer.createSegment({
54
+ id: otelSpan?.spanContext()?.spanId,
51
55
  name,
52
56
  recorder: httpRecorder,
53
57
  parent: transaction.trace.root,
@@ -58,7 +62,7 @@ function rpcSegment({ agent, otelSpan, transaction, rpcSystem }) {
58
62
  }
59
63
 
60
64
  function httpSegment({ agent, otelSpan, transaction, httpMethod }) {
61
- const httpUrl = otelSpan.attributes[ATTR_HTTP_URL] || '/Unknown'
65
+ const httpUrl = otelSpan.attributes[ATTR_HTTP_URL] || '/unknown'
62
66
  transaction.nameState.setVerb(httpMethod)
63
67
  const requestUrl = url.parse(httpUrl, true)
64
68
  transaction.parsedUrl = requestUrl
@@ -71,6 +75,7 @@ function httpSegment({ agent, otelSpan, transaction, httpMethod }) {
71
75
  // accept dt headers?
72
76
  // synthetics.assignHeadersToTransaction(agent.config, transaction, )
73
77
  return agent.tracer.createSegment({
78
+ id: otelSpan?.spanContext()?.spanId,
74
79
  recorder: httpRecorder,
75
80
  name: requestUrl.pathname,
76
81
  parent: transaction.trace.root,