newrelic 12.1.0 → 12.1.1

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,23 @@
1
+ ### v12.1.1 (2024-08-15)
2
+
3
+ #### Bug fixes
4
+
5
+ * Updated `amqplib` instrumentation to properly parse host/port from connect ([#2461](https://github.com/newrelic/node-newrelic/pull/2461)) ([91636a8](https://github.com/newrelic/node-newrelic/commit/91636a8e9702ba4ad1bf9b3941432ea65a3920fe))
6
+ * Updated `redis` instrumentation to parse host/port when a url is not provided ([#2463](https://github.com/newrelic/node-newrelic/pull/2463)) ([2b67623](https://github.com/newrelic/node-newrelic/commit/2b67623afef5fb132105c7f5b1d72e23b6d56dc1))
7
+ * Updated the `kafkajs` node metrics to remove `/Named` from the name ([#2458](https://github.com/newrelic/node-newrelic/pull/2458)) ([37ce113](https://github.com/newrelic/node-newrelic/commit/37ce1137a91c2efa85541cf6ec252a759e5f48ea))
8
+
9
+ #### Code refactoring
10
+
11
+ * Updated pino instrumentation to separate the wrapping of asJson into its own function ([#2464](https://github.com/newrelic/node-newrelic/pull/2464)) ([81fdde1](https://github.com/newrelic/node-newrelic/commit/81fdde1e35b5643ff141db1309ca58d7f6176cd5))
12
+
13
+ #### Documentation
14
+
15
+ * Updated compatibility report ([#2460](https://github.com/newrelic/node-newrelic/pull/2460)) ([a4570e9](https://github.com/newrelic/node-newrelic/commit/a4570e93298d10f4464570b75867634b95a61e89))
16
+
17
+ #### Miscellaneous chores
18
+
19
+ * Removed limit on superagent versioned testing ([#2456](https://github.com/newrelic/node-newrelic/pull/2456)) ([b4b6a6b](https://github.com/newrelic/node-newrelic/commit/b4b6a6b2eca8bd47d17f8b265344b4596c8226b3))
20
+
1
21
  ### v12.1.0 (2024-08-12)
2
22
 
3
23
  #### Bug fixes
@@ -104,8 +104,9 @@ function getRedisParams(clientOpts) {
104
104
  }
105
105
 
106
106
  return new DatastoreParameters({
107
- host: clientOpts?.socket?.host || 'localhost',
108
- port_path_or_id: clientOpts?.socket?.path || clientOpts?.socket?.port || '6379',
107
+ host: clientOpts?.host || clientOpts?.socket?.host || 'localhost',
108
+ port_path_or_id:
109
+ clientOpts?.port || clientOpts?.socket?.path || clientOpts?.socket?.port || '6379',
109
110
  database_name: clientOpts?.database || 0
110
111
  })
111
112
  }
@@ -9,10 +9,10 @@ const {
9
9
  OperationSpec,
10
10
  params: { DatastoreParameters }
11
11
  } = require('../../shim/specs')
12
- const url = require('url')
13
12
  const wrapModel = require('./channel-model')
14
- const { setCallback } = require('./utils')
13
+ const { setCallback, parseConnectionArgs } = require('./utils')
15
14
  const wrapChannel = require('./channel')
15
+ const { amqpConnection } = require('../../symbols')
16
16
 
17
17
  module.exports.instrumentPromiseAPI = instrumentChannelAPI
18
18
  module.exports.instrumentCallbackAPI = instrumentCallbackAPI
@@ -73,31 +73,52 @@ function instrumentAMQP(shim, amqp, promiseMode) {
73
73
  /**
74
74
  *
75
75
  * Instruments the connect method
76
+ * We have to both wrap and record because
77
+ * we need the host/port for all subsequent calls on the model/channel
78
+ * but record only completes in an active transaction
76
79
  *
77
80
  * @param {Shim} shim instance of shim
78
81
  * @param {object} amqp amqplib object
79
82
  * @param {boolean} promiseMode is this promise based?
80
83
  */
81
84
  function wrapConnect(shim, amqp, promiseMode) {
82
- shim.record(amqp, 'connect', function recordConnect(shim, connect, name, args) {
83
- let [connArgs] = args
84
- const params = new DatastoreParameters()
85
+ shim.wrap(amqp, 'connect', function wrapConnect(shim, connect) {
86
+ return function wrappedConnect() {
87
+ const args = shim.argsToArray.apply(shim, arguments)
88
+ const [connArgs] = args
89
+ const params = parseConnectionArgs({ shim, connArgs })
90
+ const cb = args[args.length - 1]
91
+ if (!promiseMode) {
92
+ args[args.length - 1] = function wrappedCallback() {
93
+ const cbArgs = shim.argsToArray.apply(shim, arguments)
94
+ const [, c] = cbArgs
95
+ c.connection[amqpConnection] = params
96
+ return cb.apply(this, cbArgs)
97
+ }
98
+ }
85
99
 
86
- if (shim.isString(connArgs)) {
87
- connArgs = url.parse(connArgs)
88
- params.host = connArgs.hostname
89
- if (connArgs.port) {
90
- params.port = connArgs.port
100
+ const result = connect.apply(this, args)
101
+ if (promiseMode) {
102
+ return result.then((c) => {
103
+ c.connection[amqpConnection] = params
104
+ return c
105
+ })
91
106
  }
107
+ return result
92
108
  }
109
+ })
93
110
 
111
+ shim.record(amqp, 'connect', function recordConnect(shim, connect, name, args) {
112
+ const [connArgs] = args
113
+ const params = parseConnectionArgs({ shim, connArgs })
94
114
  return new OperationSpec({
95
115
  name: 'amqplib.connect',
96
116
  callback: setCallback(shim, promiseMode),
97
117
  promise: promiseMode,
98
- parameters: params,
99
- stream: null,
100
- recorder: null
118
+ parameters: new DatastoreParameters({
119
+ host: params.host,
120
+ port_path_or_id: params.port
121
+ })
101
122
  })
102
123
  })
103
124
  }
@@ -5,6 +5,7 @@
5
5
 
6
6
  'use strict'
7
7
  const { MessageSpec, MessageSubscribeSpec, RecorderSpec } = require('../../shim/specs')
8
+ const { amqpConnection } = require('../../symbols')
8
9
  const CHANNEL_METHODS = [
9
10
  'close',
10
11
  'open',
@@ -22,13 +23,7 @@ const CHANNEL_METHODS = [
22
23
  'prefetch',
23
24
  'recover'
24
25
  ]
25
- const {
26
- describeMessage,
27
- setCallback,
28
- parseConnect,
29
- getParametersFromMessage,
30
- TEMP_RE
31
- } = require('./utils')
26
+ const { describeMessage, setCallback, getParametersFromMessage, TEMP_RE } = require('./utils')
32
27
 
33
28
  /**
34
29
  *
@@ -89,7 +84,7 @@ function recordPurge({ shim, proto, promiseMode }) {
89
84
 
90
85
  function recordGet({ shim, proto, promiseMode }) {
91
86
  shim.recordConsume(proto, 'get', function wrapGet() {
92
- const { host, port } = parseConnect(this?.connection?.stream)
87
+ const { host, port } = this?.connection?.[amqpConnection] || {}
93
88
  return new MessageSpec({
94
89
  destinationName: shim.FIRST,
95
90
  callback: setCallback(shim, promiseMode),
@@ -115,7 +110,7 @@ function recordGet({ shim, proto, promiseMode }) {
115
110
 
116
111
  function recordConsume({ shim, proto, promiseMode }) {
117
112
  shim.recordSubscribedConsume(proto, 'consume', function consume() {
118
- const { host, port } = parseConnect(this?.connection?.stream)
113
+ const { host, port } = this?.connection?.[amqpConnection] || {}
119
114
  return new MessageSubscribeSpec({
120
115
  name: 'amqplib.Channel#consume',
121
116
  queue: shim.FIRST,
@@ -5,7 +5,8 @@
5
5
 
6
6
  'use strict'
7
7
  const { MessageSpec } = require('../../shim/specs')
8
- const { parseConnect, getParameters, TEMP_RE } = require('./utils')
8
+ const { amqpConnection } = require('../../symbols')
9
+ const { getParameters, TEMP_RE } = require('./utils')
9
10
 
10
11
  /**
11
12
  *
@@ -47,24 +48,26 @@ module.exports = function wrapChannel(shim) {
47
48
  }
48
49
  })
49
50
 
50
- shim.recordProduce(proto, 'sendMessage', function recordSendMessage(shim, fn, n, args) {
51
- const fields = args[0]
52
- if (!fields) {
53
- return null
54
- }
55
- const isDefault = fields.exchange === ''
56
- let exchange = 'Default'
57
- if (!isDefault) {
58
- exchange = TEMP_RE.test(fields.exchange) ? null : fields.exchange
59
- }
60
- const { host, port } = parseConnect(this?.connection?.stream)
51
+ shim.recordProduce(proto, 'sendMessage', recordSendMessage)
52
+ }
53
+
54
+ function recordSendMessage(shim, fn, n, args) {
55
+ const fields = args[0]
56
+ if (!fields) {
57
+ return null
58
+ }
59
+ const isDefault = fields.exchange === ''
60
+ let exchange = 'Default'
61
+ if (!isDefault) {
62
+ exchange = TEMP_RE.test(fields.exchange) ? null : fields.exchange
63
+ }
64
+ const { host, port } = this?.connection?.[amqpConnection] || {}
61
65
 
62
- return new MessageSpec({
63
- destinationName: exchange,
64
- destinationType: shim.EXCHANGE,
65
- routingKey: fields.routingKey,
66
- headers: fields.headers,
67
- parameters: getParameters({ parameters: Object.create(null), fields, host, port })
68
- })
66
+ return new MessageSpec({
67
+ destinationName: exchange,
68
+ destinationType: shim.EXCHANGE,
69
+ routingKey: fields.routingKey,
70
+ headers: fields.headers,
71
+ parameters: getParameters({ parameters: Object.create(null), fields, host, port })
69
72
  })
70
73
  }
@@ -8,7 +8,6 @@ const {
8
8
  MessageSpec,
9
9
  params: { QueueMessageParameters }
10
10
  } = require('../../shim/specs')
11
- const { amqpConnection } = require('../../symbols')
12
11
  const TEMP_RE = /^amq\./
13
12
 
14
13
  /**
@@ -100,25 +99,6 @@ function getParametersFromMessage({ message, host, port }) {
100
99
  return parameters
101
100
  }
102
101
 
103
- /**
104
- * Extracts the host/port from the amqp socket connection.
105
- * Stores on connection as symbol to only parse once.
106
- *
107
- * @param {Socket} socket amqp connection
108
- * @returns {object} {host, port } of connection
109
- */
110
- function parseConnect(socket) {
111
- if (socket[amqpConnection]) {
112
- return socket[amqpConnection]
113
- }
114
- const host = ['127.0.0.1', '::1', '[::1]'].includes(socket?.remoteAddress)
115
- ? 'localhost'
116
- : socket?.remoteAddress
117
- const port = socket?.remotePort
118
- socket[amqpConnection] = { host, port }
119
- return { host, port }
120
- }
121
-
122
102
  /**
123
103
  * Helper to set the appropriate value of the callback property
124
104
  * in the spec. If it's a promise set to null otherwise set it to `shim.LAST`
@@ -131,11 +111,33 @@ function setCallback(shim, promiseMode) {
131
111
  return promiseMode ? null : shim.LAST
132
112
  }
133
113
 
114
+ /**
115
+ * Parses the connection args to return host/port
116
+ *
117
+ * @param {string|object} connArgs connection arguments
118
+ * @returns {object} {host, port }
119
+ */
120
+ function parseConnectionArgs({ shim, connArgs }) {
121
+ const params = {}
122
+ if (shim.isString(connArgs)) {
123
+ connArgs = new URL(connArgs)
124
+ params.host = connArgs.hostname
125
+ if (connArgs.port) {
126
+ params.port = parseInt(connArgs.port, 10)
127
+ }
128
+ } else {
129
+ params.port = connArgs.port || (connArgs.protocol === 'amqp' ? 5672 : 5671)
130
+ params.host = connArgs.hostname
131
+ }
132
+
133
+ return params
134
+ }
135
+
134
136
  module.exports = {
135
137
  describeMessage,
136
138
  getParameters,
137
139
  getParametersFromMessage,
138
- parseConnect,
140
+ parseConnectionArgs,
139
141
  setCallback,
140
142
  TEMP_RE
141
143
  }
@@ -11,7 +11,7 @@ function recordLinkingMetrics({ agent, brokers, topic, producer = true }) {
11
11
  const kind = producer === true ? 'Produce' : 'Consume'
12
12
  for (const broker of brokers) {
13
13
  agent.metrics
14
- .getOrCreateMetric(`MessageBroker/Kafka/Nodes/${broker}/${kind}/Named/${topic}`)
14
+ .getOrCreateMetric(`MessageBroker/Kafka/Nodes/${broker}/${kind}/${topic}`)
15
15
  .incrementCallCount()
16
16
  }
17
17
  }
@@ -15,7 +15,6 @@ const {
15
15
  } = require('../../util/application-logging')
16
16
  const semver = require('semver')
17
17
 
18
- // eslint-disable-next-line sonarjs/cognitive-complexity
19
18
  module.exports = function instrument(shim, tools) {
20
19
  const pinoVersion = shim.pkgVersion
21
20
 
@@ -35,7 +34,20 @@ module.exports = function instrument(shim, tools) {
35
34
  const metrics = agent.metrics
36
35
  createModuleUsageMetric('pino', metrics)
37
36
 
37
+ wrapAsJson({ shim, tools })
38
+ }
39
+
40
+ /**
41
+ * Wraps `asJson` to properly decorate and forward logs
42
+ *
43
+ * @param {object} params to function
44
+ * @param {Shim} params.shim instance of shim
45
+ * @param {object} params.tools exported `pino/lib/tools`
46
+ */
47
+ function wrapAsJson({ shim, tools }) {
38
48
  const symbols = shim.require('./lib/symbols')
49
+ const { agent } = shim
50
+ const { config, metrics } = agent
39
51
 
40
52
  shim.wrap(tools, 'asJson', function wrapJson(shim, asJson) {
41
53
  /**
@@ -98,16 +110,14 @@ module.exports = function instrument(shim, tools) {
98
110
  * reformats error and assigns NR context data
99
111
  * to log line
100
112
  *
101
- * @param logLine.logLine
102
- * @param {object} logLine log line
103
- * @param {object} metadata NR context data
104
- * @param {string} chindings serialized string of all common log line data
105
- * @param logLine.args
106
- * @param logLine.agent
107
- * @param logLine.chindings
108
- * @param logLine.msg
109
- * @param logLine.level
110
- * @param logLine.logger
113
+ * @param {object} params to function
114
+ * @param {object} params.logLine log line
115
+ * @param {string} params.msg message of log line
116
+ * @param {object} params.agent instance of agent
117
+ * @param {string} params.chindings serialized string of all common log line data
118
+ * @param {string} params.level log level
119
+ * @param {object} params.logger instance of agent logger
120
+ * @returns {function} wrapped log formatter function
111
121
  */
112
122
  function reformatLogLine({ logLine, msg, agent, chindings = '', level, logger }) {
113
123
  const metadata = agent.getLinkingMetadata()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newrelic",
3
- "version": "12.1.0",
3
+ "version": "12.1.1",
4
4
  "author": "New Relic Node.js agent team <nodejs@newrelic.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [