newrelic 11.9.0 → 11.10.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,26 @@
1
+ ### v11.10.0 (2024-01-22)
2
+
3
+ #### Features
4
+
5
+ * Added llm attribute to all transactions that contain llm spans for openai ([#1946](https://github.com/newrelic/node-newrelic/pull/1946)) ([6312629](https://github.com/newrelic/node-newrelic/commit/6312629326c02fe3de4db91ee293eb71b7ddd042))
6
+ * Added transaction ID to ErrorTrace event ([#1954](https://github.com/newrelic/node-newrelic/pull/1954)) ([5d0ebcd](https://github.com/newrelic/node-newrelic/commit/5d0ebcd216cb60b3a488b2b5f7c20f71f8d5bbeb))
7
+
8
+ #### Security improvements
9
+
10
+ * **remote_method:** Redacted ingest key in trace level logs ([#1948](https://github.com/newrelic/node-newrelic/pull/1948)) ([04fee88](https://github.com/newrelic/node-newrelic/commit/04fee886bd2f479e568830ea03217d7c9aba87d9))
11
+
12
+ #### Code refactoring
13
+
14
+ * Updated `lib/shim/shim.js` to remove cognitive complexity violations ([#1950](https://github.com/newrelic/node-newrelic/pull/1950)) ([60e57a1](https://github.com/newrelic/node-newrelic/commit/60e57a10f038c1e70dfef2937592983b2bfd5613))
15
+ * Updated message-shim to remove cognitive complexity violations. ([#1958](https://github.com/newrelic/node-newrelic/pull/1958)) ([3d1caaf](https://github.com/newrelic/node-newrelic/commit/3d1caaf7d7f7de1c2e139eb7943f557b896fc1e2))
16
+
17
+ #### Miscellaneous chores
18
+
19
+ * **deps:** Updated @newrelic/aws-sdk to latest and removed aws_bedrock_instrumentation feature flag ([#1956](https://github.com/newrelic/node-newrelic/pull/1956)) ([9eae28c](https://github.com/newrelic/node-newrelic/commit/9eae28c7ca06f9287fe09a2d51ad61534d9e8b1d))
20
+ * **deps:** Updated @newrelic/security-agent to v0.7.0 ([#1955](https://github.com/newrelic/node-newrelic/pull/1955)) ([e156539](https://github.com/newrelic/node-newrelic/commit/e15653916f4614e17501202f6831459a8104e7a7))
21
+ * Migrated non-sensitive CI values to vars ([#1957](https://github.com/newrelic/node-newrelic/pull/1957)) ([1739378](https://github.com/newrelic/node-newrelic/commit/17393782a278fd3c3289332c78a88a5d6819be7c))
22
+ * updated contributors list to include james and svetlana 🎉 ([#1947](https://github.com/newrelic/node-newrelic/pull/1947)) ([33a59fd](https://github.com/newrelic/node-newrelic/commit/33a59fdcdeb3e4074b4611d88e2fe79a65168fe3))
23
+
1
24
  ### v11.9.0 (2024-01-10)
2
25
 
3
26
  #### Features
@@ -509,7 +509,7 @@ This product includes source derived from [@grpc/proto-loader](https://github.co
509
509
 
510
510
  ### @newrelic/aws-sdk
511
511
 
512
- This product includes source derived from [@newrelic/aws-sdk](https://github.com/newrelic/node-newrelic-aws-sdk) ([v7.0.3](https://github.com/newrelic/node-newrelic-aws-sdk/tree/v7.0.3)), distributed under the [Apache-2.0 License](https://github.com/newrelic/node-newrelic-aws-sdk/blob/v7.0.3/LICENSE):
512
+ This product includes source derived from [@newrelic/aws-sdk](https://github.com/newrelic/node-newrelic-aws-sdk) ([v7.1.0](https://github.com/newrelic/node-newrelic-aws-sdk/tree/v7.1.0)), distributed under the [Apache-2.0 License](https://github.com/newrelic/node-newrelic-aws-sdk/blob/v7.1.0/LICENSE):
513
513
 
514
514
  ```
515
515
  Apache License
@@ -926,7 +926,7 @@ Apache License
926
926
 
927
927
  ### @newrelic/security-agent
928
928
 
929
- This product includes source derived from [@newrelic/security-agent](https://github.com/newrelic/csec-node-agent) ([v0.6.0](https://github.com/newrelic/csec-node-agent/tree/v0.6.0)), distributed under the [New Relic Pre-Release License](https://github.com/newrelic/csec-node-agent/blob/v0.6.0/LICENSE):
929
+ This product includes source derived from [@newrelic/security-agent](https://github.com/newrelic/csec-node-agent) ([v0.7.0](https://github.com/newrelic/csec-node-agent/tree/v0.7.0)), distributed under the [UNKNOWN License](https://github.com/newrelic/csec-node-agent/blob/v0.7.0/LICENSE):
930
930
 
931
931
  ```
932
932
  ## New Relic Pre-Release Software Notice
@@ -259,15 +259,19 @@ RemoteMethod.prototype._safeRequest = function _safeRequest(options) {
259
259
  level = 'info'
260
260
  }
261
261
 
262
- const logBody = Buffer.isBuffer(options.body) ? 'Buffer ' + options.body.length : options.body
263
- logger[level](
264
- { body: logBody },
265
- 'Posting to %s://%s:%s%s',
266
- protocol,
267
- options.host,
268
- options.port,
269
- options.path
270
- )
262
+ // check if trace is enabled or audit log(aka info in this case) before attemping
263
+ // to get the body length or parse the path with redacted key
264
+ if (logger.traceEnabled() || level === 'info') {
265
+ const logBody = Buffer.isBuffer(options.body) ? 'Buffer ' + options.body.length : options.body
266
+ logger[level](
267
+ { body: logBody },
268
+ 'Posting to %s://%s:%s%s',
269
+ protocol,
270
+ options.host,
271
+ options.port,
272
+ this._path({ redactLicenseKey: true })
273
+ )
274
+ }
271
275
 
272
276
  this._request(options)
273
277
  }
@@ -336,13 +340,14 @@ RemoteMethod.prototype._userAgent = function _userAgent() {
336
340
  /**
337
341
  * Generate a URL the collector understands.
338
342
  *
343
+ * @param {boolean} redactLicenseKey flag to redact license key in path
339
344
  * @returns {string} The URL path to be POSTed to.
340
345
  */
341
- RemoteMethod.prototype._path = function _path() {
346
+ RemoteMethod.prototype._path = function _path({ redactLicenseKey } = {}) {
342
347
  const query = {
343
348
  marshal_format: 'json',
344
349
  protocol_version: this._protocolVersion,
345
- license_key: this._config.license_key,
350
+ license_key: redactLicenseKey ? 'REDACTED' : this._config.license_key,
346
351
  method: this.name
347
352
  }
348
353
 
@@ -67,7 +67,8 @@ class Exception {
67
67
  * [1] -> name extracted from error info,
68
68
  * [2] -> extracted error message,
69
69
  * [3] -> extracted error type,
70
- * [4] -> attributes
70
+ * [4] -> attributes,
71
+ * [5] -> transaction id
71
72
  */
72
73
  function createError(transaction, exception, config) {
73
74
  const error = exception.error
@@ -111,7 +112,7 @@ function createError(transaction, exception, config) {
111
112
 
112
113
  maybeAddAgentAttributes(params, exception)
113
114
 
114
- return [0, name, message, type, params]
115
+ return [0, name, message, type, params, transaction?.id]
115
116
  }
116
117
 
117
118
  function isValidErrorGroupOutput(output) {
@@ -12,8 +12,7 @@ exports.prerelease = {
12
12
  reverse_naming_rules: false,
13
13
  undici_async_tracking: true,
14
14
  unresolved_promise_cleanup: true,
15
- legacy_context_manager: false,
16
- aws_bedrock_instrumentation: false
15
+ legacy_context_manager: false
17
16
  }
18
17
 
19
18
  // flags that are no longer used for released features
@@ -34,7 +33,8 @@ exports.released = [
34
33
  'await_support',
35
34
  'certificate_bundle',
36
35
  'async_local_context',
37
- 'undici_instrumentation'
36
+ 'undici_instrumentation',
37
+ 'aws_bedrock_instrumentation'
38
38
  ]
39
39
 
40
40
  // flags that are no longer used for unreleased features
@@ -19,6 +19,7 @@ const {
19
19
  AI: { OPENAI }
20
20
  } = require('../../lib/metrics/names')
21
21
  const semver = require('semver')
22
+ const { DESTINATIONS } = require('../config/attribute-filter')
22
23
 
23
24
  let TRACKING_METRIC = OPENAI.TRACKING_PREFIX
24
25
 
@@ -282,6 +283,8 @@ module.exports = function initialize(agent, openai, moduleName, shim) {
282
283
  err
283
284
  })
284
285
  }
286
+
287
+ segment.transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true)
285
288
  }
286
289
  }
287
290
  }
@@ -301,6 +304,8 @@ module.exports = function initialize(agent, openai, moduleName, shim) {
301
304
  promise: true,
302
305
  // eslint-disable-next-line max-params
303
306
  after(_shim, _fn, _name, err, response, segment) {
307
+ segment.transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true)
308
+
304
309
  if (!response) {
305
310
  // If we get an error, it is possible that `response = null`.
306
311
  // In that case, we define it to be an empty object.
@@ -0,0 +1,34 @@
1
+ /*
2
+ * Copyright 2024 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+ const common = module.exports
8
+
9
+ /**
10
+ * Constructs a message segment name from the given message descriptor.
11
+ *
12
+ * @private
13
+ * @param {MessageShim} shim The shim the segment will be constructed by.
14
+ * @param {object} msgDesc The message descriptor.
15
+ * @param {string} action Produce or consume?
16
+ * @returns {string} The generated name of the message segment.
17
+ */
18
+ common._nameMessageSegment = function _nameMessageSegment(shim, msgDesc, action) {
19
+ let name =
20
+ shim._metrics.PREFIX +
21
+ shim._metrics.LIBRARY +
22
+ '/' +
23
+ (msgDesc.destinationType || shim.EXCHANGE) +
24
+ '/' +
25
+ action
26
+
27
+ if (msgDesc.destinationName) {
28
+ name += shim._metrics.NAMED + msgDesc.destinationName
29
+ } else {
30
+ name += shim._metrics.TEMP
31
+ }
32
+
33
+ return name
34
+ }
@@ -0,0 +1,167 @@
1
+ /*
2
+ * Copyright 2024 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+ const TraceSegment = require('../../transaction/trace/segment')
8
+ const genericRecorder = require('../../metrics/recorders/generic')
9
+ const { _nameMessageSegment } = require('./common')
10
+ const specs = require('../specs')
11
+ module.exports = createRecorder
12
+
13
+ /**
14
+ * Generates the spec for the consumer
15
+ *
16
+ * @private
17
+ * @param {object} params to function
18
+ * @param {MessageShim} params.shim instance of shim
19
+ * @param {Function} params.fn consumer function
20
+ * @param {string} params.fnName name of function
21
+ * @param {Array} params.args arguments passed to original consume function
22
+ * @param {specs.MessageSpec} params.spec spec for the wrapped consume function
23
+ * @returns {specs.MessageSpec} new spec
24
+ */
25
+ function updateSpecFromArgs({ shim, fn, fnName, args, spec }) {
26
+ let msgDesc = null
27
+ if (shim.isFunction(spec)) {
28
+ msgDesc = spec.call(this, shim, fn, fnName, args)
29
+ msgDesc = new specs.MessageSpec(msgDesc)
30
+ } else {
31
+ msgDesc = new specs.MessageSpec(spec)
32
+ const destIdx = shim.normalizeIndex(args.length, spec.destinationName)
33
+ if (destIdx !== null) {
34
+ msgDesc.destinationName = args[destIdx]
35
+ }
36
+ }
37
+
38
+ return msgDesc
39
+ }
40
+
41
+ /**
42
+ * Binds the consumer callback to the active segment.
43
+ *
44
+ * @private
45
+ * @param {object} params to function
46
+ * @param {MessageShim} params.shim instance of shim
47
+ * @param {Array} params.args arguments passed to original consume function
48
+ * @param {specs.MessageSpec} params.msgDesc spec for the wrapped consume function
49
+ * @param {TraceSegment} params.segment active segment to bind callback
50
+ * @param {boolean} params.getParams flag to copy message parameters to segment
51
+ * @param {Function} params.resHandler function to handle response from callback to obtain the message parameters
52
+ */
53
+ function bindCallback({ shim, args, msgDesc, segment, getParams, resHandler }) {
54
+ const cbIdx = shim.normalizeIndex(args.length, msgDesc.callback)
55
+ if (cbIdx !== null) {
56
+ shim.bindCallbackSegment(args, cbIdx, segment)
57
+
58
+ // If we have a callback and a results handler, then wrap the callback so
59
+ // we can call the results handler and get the message properties.
60
+ if (resHandler) {
61
+ shim.wrap(args, cbIdx, function wrapCb(shim, cb, cbName) {
62
+ if (shim.isFunction(cb)) {
63
+ return function cbWrapper() {
64
+ const cbArgs = shim.argsToArray.apply(shim, arguments)
65
+ const msgProps = resHandler.call(this, shim, cb, cbName, cbArgs)
66
+ if (getParams && msgProps && msgProps.parameters) {
67
+ shim.copySegmentParameters(segment, msgProps.parameters)
68
+ }
69
+
70
+ return cb.apply(this, arguments)
71
+ }
72
+ }
73
+ })
74
+ }
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Binds the consumer function to the async context and checks return to possibly
80
+ * bind the promise
81
+ *
82
+ * @private
83
+ * @param {object} params to function
84
+ * @param {MessageShim} params.shim instance of shim
85
+ * @param {Function} params.fn consumer function
86
+ * @param {string} params.fnName name of function
87
+ * @param {Array} params.args arguments passed to original consume function
88
+ * @param {specs.MessageSpec} params.msgDesc spec for the wrapped consume function
89
+ * @param {TraceSegment} params.segment active segment to bind callback
90
+ * @param {boolean} params.getParams flag to copy message parameters to segment
91
+ * @param {Function} params.resHandler function to handle response from callback to obtain the message parameters
92
+ * @returns {Promise|*} response from consume function
93
+ */
94
+ function bindConsumer({ shim, fn, fnName, args, msgDesc, segment, getParams, resHandler }) {
95
+ // Call the method in the context of our segment.
96
+ let ret = shim.applySegment(fn, segment, true, this, args)
97
+
98
+ if (ret && msgDesc.promise && shim.isPromise(ret)) {
99
+ ret = shim.bindPromise(ret, segment)
100
+
101
+ // Intercept the promise to handle the result.
102
+ if (resHandler) {
103
+ ret = ret.then(function interceptValue(res) {
104
+ const msgProps = resHandler.call(this, shim, fn, fnName, res)
105
+ if (getParams && msgProps && msgProps.parameters) {
106
+ shim.copySegmentParameters(segment, msgProps.parameters)
107
+ }
108
+ return res
109
+ })
110
+ }
111
+ }
112
+
113
+ return ret
114
+ }
115
+
116
+ /**
117
+ *
118
+ * @private
119
+ * @param {object} params to function
120
+ * @param {MessageShim} params.shim instance of shim
121
+ * @param {Function} params.fn function that is being wrapped
122
+ * @param {string} params.fnName name of function
123
+ * @param {specs.MessageSpec} params.spec spec for the wrapped consume function
124
+ * @returns {Function} recorder for consume function
125
+ */
126
+ function createRecorder({ shim, fn, fnName, spec }) {
127
+ return function consumeRecorder() {
128
+ const parent = shim.getSegment()
129
+ if (!parent || !parent.transaction.isActive()) {
130
+ shim.logger.trace('Not recording consume, no active transaction.')
131
+ return fn.apply(this, arguments)
132
+ }
133
+
134
+ // Process the message args.
135
+ const args = shim.argsToArray.apply(shim, arguments)
136
+ const msgDesc = updateSpecFromArgs.call(this, { shim, fn, fnName, args, spec })
137
+
138
+ // Make the segment if we can.
139
+ if (!msgDesc) {
140
+ shim.logger.trace('Not recording consume, no message descriptor.')
141
+ return fn.apply(this, args)
142
+ }
143
+
144
+ const name = _nameMessageSegment(shim, msgDesc, shim._metrics.CONSUME)
145
+
146
+ // Adds details needed by createSegment when used with a spec
147
+ msgDesc.name = name
148
+ msgDesc.recorder = genericRecorder
149
+ msgDesc.parent = parent
150
+
151
+ const segment = shim.createSegment(msgDesc)
152
+ const getParams = shim.agent.config.message_tracer.segment_parameters.enabled
153
+ const resHandler = shim.isFunction(msgDesc.messageHandler) ? msgDesc.messageHandler : null
154
+
155
+ bindCallback({ shim, args, msgDesc, segment, getParams, resHandler })
156
+ return bindConsumer.call(this, {
157
+ shim,
158
+ fn,
159
+ fnName,
160
+ args,
161
+ msgDesc,
162
+ segment,
163
+ getParams,
164
+ resHandler
165
+ })
166
+ }
167
+ }