newrelic 12.22.0 → 12.23.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,3 +1,24 @@
1
+ ### v12.23.0 (2025-06-30)
2
+
3
+ #### Features
4
+
5
+ * Added ability to report only on entry and exit spans ([#3184](https://github.com/newrelic/node-newrelic/pull/3184)) ([1f909d3](https://github.com/newrelic/node-newrelic/commit/1f909d389f790733c8787a9db8b0ee71c26bb5ed))
6
+ * Added Node.js 24 support ([#3080](https://github.com/newrelic/node-newrelic/pull/3080)) ([a538c2a](https://github.com/newrelic/node-newrelic/commit/a538c2a5e23b96be40fa3c014e60b912f695423e))
7
+
8
+ #### Documentation
9
+
10
+ * Updated compatibility report ([#3186](https://github.com/newrelic/node-newrelic/pull/3186)) ([5498c15](https://github.com/newrelic/node-newrelic/commit/5498c15bb6cf3329fe50595da46ab4f2b1385170))
11
+
12
+ #### Miscellaneous chores
13
+
14
+ * Fix issues on Node 24 ([#3181](https://github.com/newrelic/node-newrelic/pull/3181)) ([24832df](https://github.com/newrelic/node-newrelic/commit/24832df2984df960a4e44375525a8127a00a637c))
15
+ * Removed force labels on main CI workflow ([#3183](https://github.com/newrelic/node-newrelic/pull/3183)) ([3aaee17](https://github.com/newrelic/node-newrelic/commit/3aaee17586f817ae1a2ab0573570a740bd6397ef))
16
+ * Updated undici tests to unblock CI ([#3185](https://github.com/newrelic/node-newrelic/pull/3185)) ([08261eb](https://github.com/newrelic/node-newrelic/commit/08261ebcdbafdb625a451f4ccec9b0cba7641cc2))
17
+
18
+ #### Continuous integration
19
+
20
+ * Fixed the name of todo colum in add to board reusable workflow ([#3178](https://github.com/newrelic/node-newrelic/pull/3178)) ([f216e0c](https://github.com/newrelic/node-newrelic/commit/f216e0c48adf0291bfa38b3edbb04c9dcc96f92c))
21
+
1
22
  ### v12.22.0 (2025-06-24)
2
23
 
3
24
  #### Features
package/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- const HealthReporter = require('./lib/health-reporter')
8
+ const HealthReporter = require('./lib/health-reporter.js')
9
9
 
10
10
  // Record opening times before loading any other files.
11
11
  const preAgentTime = process.uptime()
@@ -64,8 +64,8 @@ function initialize() {
64
64
  throw new Error(message)
65
65
  }
66
66
 
67
- // TODO: Update this check when Node v24 support is added
68
- if (psemver.satisfies('>=23.0.0')) {
67
+ // TODO: Update this check when Node v26 support is added
68
+ if (psemver.satisfies('>=25.0.0')) {
69
69
  logger.warn(
70
70
  'New Relic for Node.js %s has not been tested on Node.js %s. Please ' +
71
71
  'update the agent or downgrade your version of Node.js',
@@ -1120,6 +1120,19 @@ defaultConfig.definition = () => ({
1120
1120
  default: false
1121
1121
  },
1122
1122
 
1123
+ /**
1124
+ * Controls whether the agent will generate spans for in-process actions.
1125
+ * When disabled, this will only create spans for entry and exit spans.
1126
+ * entry spans - initial actions for web servers and messsage queue consumption.
1127
+ * exit spans - all outgoing calls to external services(external and database calls)
1128
+ */
1129
+ in_process_spans: {
1130
+ enabled: {
1131
+ formatter: boolean,
1132
+ default: true
1133
+ }
1134
+ },
1135
+
1123
1136
  sampler: {
1124
1137
  /**
1125
1138
  * When set to `always_on`, the sampled flag in the `traceparent` header
@@ -60,10 +60,72 @@ function addSpanKind({ segment, span }) {
60
60
  }
61
61
  }
62
62
 
63
+ /**
64
+ * Checks if the segment is an entry point span.
65
+ * An entry point span is defined as the base segment of a transaction.
66
+ * @param {object} params to function
67
+ * @param {Transaction} params.transaction active transaction
68
+ * @param {TraceSegment} params.segment segment that is creating span
69
+ * @returns {boolean} true if the segment is an entry point span
70
+ */
71
+ function isEntryPointSpan({ transaction, segment }) {
72
+ return transaction?.baseSegment === segment
73
+ }
74
+
75
+ /**
76
+ * Checks if the segment is an exit span.
77
+ * An exit span is defined as a segment that is an external call,
78
+ * datastore operation, or message broker operation.
79
+ * @param {TraceSegment} segment segment that is creating span
80
+ * @returns {boolean} true if the segment is an exit span
81
+ */
82
+ function isExitSpan(segment) {
83
+ return REGEXS.CLIENT.EXTERNAL.test(segment.name) || REGEXS.CLIENT.DATASTORE.test(segment.name) || REGEXS.PRODUCER.test(segment.name)
84
+ }
85
+
86
+ /**
87
+ * Determines if a span should be created based on the segment and transaction.
88
+ * If the segment is an entry point span or an exit span, a span should be created.
89
+ * @param {object} params to function
90
+ * @param {boolean} params.entryPoint true if the segment is an entry point span
91
+ * @param {TraceSegment} params.segment segment that is creating span
92
+ * @returns {boolean} true if a span should be created
93
+ */
94
+ function shouldCreateSpan({ entryPoint, segment }) {
95
+ return entryPoint ||
96
+ isExitSpan(segment)
97
+ }
98
+
99
+ /**
100
+ * Reparents a span based on the transaction and inProcessSpans.
101
+ * If inProcessSpans is true or the transaction has accepted distributed trace and it's the root segment,
102
+ * the parentId is not changed.
103
+ * If there is a parentId and the transaction has a base segment, the span is reparented to the base segment.
104
+ * If there is no parentId, the span is not reparented(indicates an entry root span for a DT trace).
105
+ * @param {object} params to function
106
+ * @param {boolean} params.inProcessSpans true if in-process spans are enabled
107
+ * @param {boolean} params.isRoot true if the segment is the root segment
108
+ * @param {string} params.parentId the parent id of the segment
109
+ * @param {Transaction} params.transaction active transaction
110
+ * @returns {string|null} the new parentId for the span
111
+ */
112
+ function reparentSpan({ inProcessSpans, isRoot, parentId, transaction }) {
113
+ if (inProcessSpans || (transaction.acceptedDistributedTrace && isRoot)) {
114
+ return parentId
115
+ } else if (parentId && transaction?.baseSegment?.id) {
116
+ return transaction.baseSegment.id
117
+ } else {
118
+ return null
119
+ }
120
+ }
121
+
63
122
  module.exports = {
64
123
  HTTP_LIBRARY,
65
124
  CATEGORIES,
66
125
  SPAN_KIND,
67
126
  REGEXS,
68
- addSpanKind
127
+ addSpanKind,
128
+ isEntryPointSpan,
129
+ reparentSpan,
130
+ shouldCreateSpan
69
131
  }
@@ -21,6 +21,7 @@ class SpanEventAggregator extends EventAggregator {
21
21
  opts.metricNames = opts.metricNames || NAMES.SPAN_EVENTS
22
22
 
23
23
  super(opts, agent)
24
+ this.inProcessSpans = agent.config.distributed_tracing.in_process_spans.enabled
24
25
  }
25
26
 
26
27
  _toPayloadSync() {
@@ -66,7 +67,6 @@ class SpanEventAggregator extends EventAggregator {
66
67
  * @param {Transaction} params.transaction active transaction
67
68
  * @param {string} [params.parentId] GUID of the parent span.
68
69
  * @param {boolean} params.isRoot if segment is root segment
69
- * @returns {boolean} True if the segment was added, or false if it was discarded.
70
70
  */
71
71
  addSegment({ segment, transaction, parentId, isRoot }) {
72
72
  // Check if the priority would be accepted before creating the event object.
@@ -77,8 +77,11 @@ class SpanEventAggregator extends EventAggregator {
77
77
 
78
78
  return false
79
79
  }
80
- const span = SpanEvent.fromSegment(segment, transaction, parentId || null, isRoot)
81
- return this.add(span, transaction.priority)
80
+ const span = SpanEvent.fromSegment({ segment, transaction, parentId, isRoot, inProcessSpans: this.inProcessSpans })
81
+
82
+ if (span) {
83
+ this.add(span, transaction.priority)
84
+ }
82
85
  }
83
86
 
84
87
  /**
@@ -9,7 +9,7 @@ const Config = require('../config')
9
9
  const { truncate } = require('../util/byte-limit')
10
10
 
11
11
  const { DESTINATIONS } = require('../config/attribute-filter')
12
- const { addSpanKind, HTTP_LIBRARY, REGEXS, SPAN_KIND, CATEGORIES } = require('./helpers')
12
+ const { addSpanKind, isEntryPointSpan, reparentSpan, shouldCreateSpan, HTTP_LIBRARY, REGEXS, SPAN_KIND, CATEGORIES } = require('./helpers')
13
13
  const EMPTY_USER_ATTRS = Object.freeze(Object.create(null))
14
14
  const SERVER_ADDRESS = 'server.address'
15
15
 
@@ -93,9 +93,15 @@ class SpanEvent {
93
93
  * @param {Transaction} transaction active transaction
94
94
  * @param {?string} [parentId] ID of the segment's parent.
95
95
  * @param {boolean} isRoot if segment is root segment
96
+ * @param {boolean} inProcessSpans if the segment is in-process, create span
96
97
  * @returns {SpanEvent} The constructed event.
97
98
  */
98
- static fromSegment(segment, transaction, parentId = null, isRoot = false) {
99
+ static fromSegment({ segment, transaction, parentId = null, isRoot = false, inProcessSpans }) {
100
+ const entryPoint = isEntryPointSpan({ segment, transaction })
101
+ if (!inProcessSpans && !shouldCreateSpan({ entryPoint, segment, transaction })) {
102
+ return null
103
+ }
104
+
99
105
  const spanContext = segment.getSpanContext()
100
106
 
101
107
  // Since segments already hold span agent attributes and we want to leverage
@@ -128,7 +134,7 @@ class SpanEvent {
128
134
 
129
135
  span.intrinsics.traceId = transaction.traceId
130
136
  span.intrinsics.guid = segment.id
131
- span.intrinsics.parentId = parentId
137
+ span.intrinsics.parentId = reparentSpan({ inProcessSpans, isRoot, segment, transaction, parentId })
132
138
  span.intrinsics.transactionId = transaction.id
133
139
  span.intrinsics.sampled = transaction.sampled
134
140
  span.intrinsics.priority = transaction.priority
@@ -142,7 +148,7 @@ class SpanEvent {
142
148
  }
143
149
 
144
150
  // Only set this if it will be `true`. Must be `null` otherwise.
145
- if (transaction.baseSegment === segment) {
151
+ if (entryPoint) {
146
152
  span.intrinsics['nr.entryPoint'] = true
147
153
  }
148
154
 
@@ -44,6 +44,7 @@ class StreamingSpanEventAggregator extends Aggregator {
44
44
  this.metrics = metrics
45
45
  this.started = false
46
46
  this.isStream = true
47
+ this.inProcessSpans = agent.config.distributed_tracing.in_process_spans.enabled
47
48
  }
48
49
 
49
50
  /**
@@ -112,8 +113,11 @@ class StreamingSpanEventAggregator extends Aggregator {
112
113
  return
113
114
  }
114
115
 
115
- const span = StreamingSpanEvent.fromSegment(segment, transaction, parentId, isRoot)
116
- this.stream.write(span)
116
+ const span = StreamingSpanEvent.fromSegment({ segment, transaction, parentId, isRoot, inProcessSpans: this.inProcessSpans })
117
+
118
+ if (span) {
119
+ this.stream.write(span)
120
+ }
117
121
  }
118
122
 
119
123
  reconfigure(config) {
@@ -10,7 +10,7 @@ const { truncate } = require('../util/byte-limit')
10
10
  const Config = require('../config')
11
11
 
12
12
  const { DESTINATIONS } = require('../config/attribute-filter')
13
- const { addSpanKind, HTTP_LIBRARY, REGEXS, SPAN_KIND, CATEGORIES } = require('./helpers')
13
+ const { addSpanKind, isEntryPointSpan, reparentSpan, shouldCreateSpan, HTTP_LIBRARY, REGEXS, SPAN_KIND, CATEGORIES } = require('./helpers')
14
14
 
15
15
  /**
16
16
  * Specialized span event class for use with infinite streaming.
@@ -108,7 +108,12 @@ class StreamingSpanEvent {
108
108
  }
109
109
  }
110
110
 
111
- static fromSegment(segment, transaction, parentId = null, isRoot = false) {
111
+ static fromSegment({ segment, transaction, parentId = null, isRoot = false, inProcessSpans }) {
112
+ const entryPoint = isEntryPointSpan({ segment, transaction })
113
+ if (!inProcessSpans && !shouldCreateSpan({ entryPoint, segment, transaction })) {
114
+ return null
115
+ }
116
+
112
117
  const spanContext = segment.getSpanContext()
113
118
 
114
119
  // Since segments already hold span agent attributes and we want to leverage
@@ -141,8 +146,9 @@ class StreamingSpanEvent {
141
146
  span.addIntrinsicAttribute(key, value)
142
147
  }
143
148
 
149
+ const newParentId = reparentSpan({ inProcessSpans, isRoot, segment, transaction, parentId })
144
150
  span.addIntrinsicAttribute('guid', segment.id)
145
- span.addIntrinsicAttribute('parentId', parentId)
151
+ span.addIntrinsicAttribute('parentId', newParentId)
146
152
  span.addIntrinsicAttribute('transactionId', transaction.id)
147
153
  span.addIntrinsicAttribute('sampled', transaction.sampled)
148
154
  span.addIntrinsicAttribute('priority', transaction.priority)
@@ -156,7 +162,7 @@ class StreamingSpanEvent {
156
162
  }
157
163
 
158
164
  // Only set this if it will be `true`. Must be `null` otherwise.
159
- if (transaction.baseSegment === segment) {
165
+ if (entryPoint) {
160
166
  span.addIntrinsicAttribute('nr.entryPoint', true)
161
167
  }
162
168
 
@@ -52,7 +52,7 @@ clmUtils.addCLMAttributes = function addCLMAttributes(fn, segment) {
52
52
  }
53
53
 
54
54
  try {
55
- const { funcInfo } = require('@contrast/fn-inspect')
55
+ const { funcInfo } = require('@newrelic/fn-inspect')
56
56
  const { lineNumber, method, file: filePath, column } = funcInfo(fn)
57
57
  const fnName = setFunctionName(method)
58
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newrelic",
3
- "version": "12.22.0",
3
+ "version": "12.23.0",
4
4
  "author": "New Relic Node.js agent team <nodejs@newrelic.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [
@@ -223,8 +223,8 @@
223
223
  "winston-transport": "^4.5.0"
224
224
  },
225
225
  "optionalDependencies": {
226
- "@contrast/fn-inspect": "^4.2.0",
227
- "@newrelic/native-metrics": "^11.0.0",
226
+ "@newrelic/fn-inspect": "^4.4.0",
227
+ "@newrelic/native-metrics": "^11.1.0",
228
228
  "@prisma/prisma-fmt-wasm": "^4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085"
229
229
  },
230
230
  "devDependencies": {