newrelic 13.6.3 → 13.6.5

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,26 @@
1
+ ### v13.6.5 (2025-11-17)
2
+
3
+ #### Bug fixes
4
+
5
+ * Updated express instrumentation to ignore error when the `next` handler passes `route` or `router` ([#3513](https://github.com/newrelic/node-newrelic/pull/3513)) ([724b218](https://github.com/newrelic/node-newrelic/commit/724b218174bddcf2ecf8cffc317bbfad1f0012eb))
6
+ * Updated MessageConsumerSubscriber to wait for consumer callback to end when it is a promise ([#3510](https://github.com/newrelic/node-newrelic/pull/3510)) ([45667a0](https://github.com/newrelic/node-newrelic/commit/45667a0ce2aaaf57a5e7a8acc8f0feea31e6152f))
7
+
8
+ #### Documentation
9
+
10
+ * Updated compatibility report ([#3498](https://github.com/newrelic/node-newrelic/pull/3498)) ([ef41b38](https://github.com/newrelic/node-newrelic/commit/ef41b3851b2096035d5b7b655d5243a9aeb699c8))
11
+
12
+ #### Tests
13
+
14
+ * Updated aws sdk v3 s3 test to no longer test DeleteBucketCommand ([#3512](https://github.com/newrelic/node-newrelic/pull/3512)) ([df27448](https://github.com/newrelic/node-newrelic/commit/df274484e0e657fd9c38452e500450878fc24ee4))
15
+
16
+ ### v13.6.4 (2025-11-13)
17
+
18
+ #### Bug fixes
19
+
20
+ * Updated `MessageConsumerSubscriber` to end transaction created for a given message consumption ([#3503](https://github.com/newrelic/node-newrelic/pull/3503)) ([ba30818](https://github.com/newrelic/node-newrelic/commit/ba308184e5cb4f916aec53963cce0caa9149efbe))
21
+ * Updated `MessageProducerSubscriber` to properly set the sampled flag on `traceparent` ([#3505](https://github.com/newrelic/node-newrelic/pull/3505)) ([0b1a9a8](https://github.com/newrelic/node-newrelic/commit/0b1a9a80b81e0f4388d421f17176eab386d04cbc))
22
+ * Updated the bedrock middleware to be registered with a `high` priority to ensure payload is properly deserialized ([#3500](https://github.com/newrelic/node-newrelic/pull/3500)) ([63e531b](https://github.com/newrelic/node-newrelic/commit/63e531bf50d600d92556b3785725dd38145f3491))
23
+
1
24
  ### v13.6.3 (2025-11-11)
2
25
 
3
26
  #### Bug fixes
@@ -8040,3 +8063,4 @@ Special thanks to Ryan Copley (@RyanCopley) for the contribution.
8040
8063
 
8041
8064
 
8042
8065
 
8066
+
@@ -392,6 +392,11 @@ module.exports.bedrockMiddlewareConfig = {
392
392
  config: {
393
393
  name: 'NewRelicBedrockMiddleware',
394
394
  step: 'deserialize',
395
+ // force high priority to ensure we wrap before any other
396
+ // middleware and then our `after` hook will be the result of
397
+ // any other middleware registered on the deserialize step
398
+ // See: https://github.com/aws/aws-sdk-js-v3/issues/7490
399
+ priority: 'high',
395
400
  override: true
396
401
  }
397
402
  }
@@ -9,8 +9,11 @@ const isString = require('#agentlib/util/is-string.js')
9
9
 
10
10
  /**
11
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
12
+ * 1. Capture the connection arguments and assign to symbol on connection to be
13
+ * used for all other amqplib subscribers.
14
+ * 2. Create a segment for the connect. Note this will attempt to create a segment
15
+ * but the base subscriber will decide if it is created or not, otherwise it'll
16
+ * return the current context.
14
17
  */
15
18
  class ConnectSubscriber extends Subscriber {
16
19
  constructor({ agent, logger }) {
@@ -114,7 +114,9 @@ class Subscriber {
114
114
  asyncStart.runStores(context, () => {
115
115
  try {
116
116
  if (callback) {
117
- return Reflect.apply(callback, this, arguments)
117
+ const cbResult = Reflect.apply(callback, this, arguments)
118
+ context.cbResult = cbResult
119
+ return cbResult
118
120
  }
119
121
  } finally {
120
122
  asyncEnd.publish(context)
@@ -363,7 +365,7 @@ class Subscriber {
363
365
  if (distributedTracingEnabled) {
364
366
  // we have to pass in traceId, segment id, and hard code traceFlags to 1
365
367
  // because we're inserting headers right before the original function is bound.
366
- const traceFlags = tx.sampled === true ? 1 : 0
368
+ const traceFlags = tx.isSampled() === true ? 1 : 0
367
369
  tx.insertDistributedTraceHeaders(headers, null, { traceId: tx.traceId, spanId: ctx?.segment?.id, traceFlags })
368
370
  } else {
369
371
  cat.addCatHeaders(this.config, tx, headers, useMqNames)
@@ -0,0 +1,26 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ const MiddlewareSubscriber = require('../middleware')
7
+
8
+ /**
9
+ * Special error handler to ignore if the error passed to next handler
10
+ * is 'route' or 'router', which are used by Express internally to
11
+ * skip out of a route or router.
12
+ *
13
+ * @param {*} err - The error passed to the next handler
14
+ * @returns {boolean} - returns true if error exists and not equal to a string or `route` or `router`
15
+ */
16
+ function errorHandler(err) {
17
+ return err && err !== 'route' && err !== 'router'
18
+ }
19
+
20
+ class ExpressSubscriber extends MiddlewareSubscriber {
21
+ constructor({ agent, logger, packageName = 'express', channelName }) {
22
+ super({ agent, logger, packageName, channelName, system: 'Expressjs', errorHandler })
23
+ }
24
+ }
25
+
26
+ module.exports = ExpressSubscriber
@@ -5,11 +5,11 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- const MiddlewareSubscriber = require('../middleware')
8
+ const ExpressSubscriber = require('./base')
9
9
 
10
- class ExpressParamSubscriber extends MiddlewareSubscriber {
11
- constructor({ agent, logger, packageName = 'express' }) {
12
- super({ agent, logger, packageName, channelName: 'nr_param', system: 'Expressjs' })
10
+ class ExpressParamSubscriber extends ExpressSubscriber {
11
+ constructor({ agent, logger, packageName }) {
12
+ super({ agent, logger, packageName, channelName: 'nr_param' })
13
13
  }
14
14
 
15
15
  handler(data) {
@@ -5,12 +5,12 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- const MiddlewareSubscriber = require('../middleware')
9
8
  const methods = ['all', 'delete', 'get', 'head', 'post', 'put', 'patch']
9
+ const ExpressSubscriber = require('./base')
10
10
 
11
- class ExpressRouteSubscriber extends MiddlewareSubscriber {
12
- constructor({ agent, logger, packageName = 'express' }) {
13
- super({ agent, logger, packageName, channelName: 'nr_route', system: 'Expressjs' })
11
+ class ExpressRouteSubscriber extends ExpressSubscriber {
12
+ constructor({ agent, logger, packageName }) {
13
+ super({ agent, logger, packageName, channelName: 'nr_route' })
14
14
  this.events = ['end']
15
15
  }
16
16
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- const MiddlewareSubscriber = require('../middleware')
8
+ const ExpressSubscriber = require('./base')
9
9
 
10
- class ExpressUseSubscriber extends MiddlewareSubscriber {
11
- constructor({ agent, logger, packageName = 'express' }) {
12
- super({ agent, logger, packageName, channelName: 'nr_use', system: 'Expressjs' })
10
+ class ExpressUseSubscriber extends ExpressSubscriber {
11
+ constructor({ agent, logger, packageName }) {
12
+ super({ agent, logger, packageName, channelName: 'nr_use' })
13
13
  }
14
14
 
15
15
  handler(data) {
@@ -65,6 +65,25 @@ class MessageConsumerSubscriber extends Subscriber {
65
65
  }
66
66
  }
67
67
 
68
+ /**
69
+ * Checks the result of the message handler.
70
+ * If it's a promise, waits for it to resolve before ending transaction.
71
+ * This ensures that the transaction stays active until the promise resolves.
72
+ * @param {object} data the data associated with the `asyncEnd` event
73
+ */
74
+ asyncEnd(data) {
75
+ const ctx = this.agent.tracer.getContext()
76
+ const result = data.cbResult
77
+ if (typeof result?.then === 'function') {
78
+ const prom = Promise.resolve(result)
79
+ prom.finally(() => {
80
+ ctx?.transaction?.end()
81
+ })
82
+ } else {
83
+ ctx?.transaction?.end()
84
+ }
85
+ }
86
+
68
87
  enable() {
69
88
  super.enable()
70
89
  this.channel.asyncStart.bindStore(this.store, (data) => {
@@ -83,9 +102,8 @@ class MessageConsumerSubscriber extends Subscriber {
83
102
  /**
84
103
  * Used to create a transaction for every consumption callback.
85
104
  *
86
- * @param {object} data event passed to asyncStart handler
87
105
  */
88
- asyncStart(data) {
106
+ asyncStart() {
89
107
  const ctx = this.agent.tracer.getContext()
90
108
  const tx = ctx?.transaction
91
109
  tx.setPartialName(this.name)
@@ -19,6 +19,17 @@ function getFunctionName(handler) {
19
19
  return handler.name === '' ? '<anonymous>' : handler.name
20
20
  }
21
21
 
22
+ /**
23
+ * Default error handler for determining if error should stored with transaction.
24
+ * Note: Based on previous shim instrumentation only Express, Restify, and Hapi have
25
+ * a different handler.
26
+ * @param {Error} err error passed to done handler
27
+ * @returns {boolean} returns true if error exists
28
+ */
29
+ function defaultErrorHandler(err) {
30
+ return err
31
+ }
32
+
22
33
  /**
23
34
  * The baseline parameters available to the middleware wrapper
24
35
  *
@@ -26,6 +37,7 @@ function getFunctionName(handler) {
26
37
  * @property {object} agent A New Relic Node.js agent instance.
27
38
  * @property {object} logger An agent logger instance.
28
39
  * @property {string} system handling the instrumentation(i.e - Fastify, Expressjs, Hapi, etc)
40
+ * @property {Function} [errorHandler] optional function to determine if error should be recorded
29
41
  */
30
42
 
31
43
  /**
@@ -36,13 +48,14 @@ function getFunctionName(handler) {
36
48
  * @property {string} prefix formatted prefix to name segments/timeslice metrics
37
49
  */
38
50
  class MiddlewareWrapper {
39
- constructor({ agent, logger, system }) {
51
+ constructor({ agent, logger, system, errorHandler }) {
40
52
  this.agent = agent
41
53
  this.logger = logger
42
54
  this.system = system
43
55
  this.config = agent.config
44
56
  this.prefix = `Nodejs/Middleware/${this.system}`
45
57
  this.agent.environment.setFramework(system)
58
+ this.isError = errorHandler ?? defaultErrorHandler
46
59
  }
47
60
 
48
61
  /**
@@ -180,7 +193,7 @@ class MiddlewareWrapper {
180
193
  if (isSync) {
181
194
  function wrappedDone(...doneArgs) {
182
195
  const [err] = doneArgs
183
- if (err) {
196
+ if (self.isError(err)) {
184
197
  self.storeError(txInfo, err)
185
198
  // route has been completed, pop from path
186
199
  // to allow other handlers to name it more accurately
@@ -8,12 +8,12 @@ const Subscriber = require('./base')
8
8
  const MiddlewareWrapper = require('./middleware-wrapper')
9
9
 
10
10
  class MiddlewareSubscriber extends Subscriber {
11
- constructor({ agent, logger, packageName, channelName, system }) {
11
+ constructor({ agent, logger, packageName, channelName, system, errorHandler }) {
12
12
  super({ agent, logger, packageName, channelName })
13
13
  // this is because the handler simply wraps a function
14
14
  // that is executed later when a request is made
15
15
  this.requireActiveTx = false
16
- this.wrapper = new MiddlewareWrapper({ agent, logger, system })
16
+ this.wrapper = new MiddlewareWrapper({ agent, logger, system, errorHandler })
17
17
  }
18
18
  }
19
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newrelic",
3
- "version": "13.6.3",
3
+ "version": "13.6.5",
4
4
  "author": "New Relic Node.js agent team <nodejs@newrelic.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [