newrelic 12.1.1 → 12.3.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,53 @@
1
+ ### v12.3.0 (2024-08-27)
2
+
3
+ #### Features
4
+
5
+ * Added new API method `withLlmCustomAttributes` to run a function in a LLM context ([#2437](https://github.com/newrelic/node-newrelic/pull/2437)) ([57e6be9](https://github.com/newrelic/node-newrelic/commit/57e6be9f4717fde3caada0e3ca3680959180f928))
6
+ * The context will be used to assign custom attributes to every LLM event produced within the function
7
+
8
+ #### Bug fixes
9
+
10
+ * Improved AWS Lambda event detection ([#2498](https://github.com/newrelic/node-newrelic/pull/2498)) ([5e8b260](https://github.com/newrelic/node-newrelic/commit/5e8b2608d9914e2a4282f7c9c42ff17dfa9f793e))
11
+
12
+ #### Documentation
13
+
14
+ * Updated compatibility report ([#2493](https://github.com/newrelic/node-newrelic/pull/2493)) ([0448927](https://github.com/newrelic/node-newrelic/commit/0448927a49254b5b3c7ed9ff072cec24449fc558))
15
+
16
+ #### Miscellaneous chores
17
+ * Fixed linting scripts ([#2497](https://github.com/newrelic/node-newrelic/pull/2497)) ([c395779](https://github.com/newrelic/node-newrelic/commit/c395779f499cca0ec7f915342c23b2d2381b0163))
18
+ * Removed examples/shim ([#2484](https://github.com/newrelic/node-newrelic/pull/2484)) ([40d1f5c](https://github.com/newrelic/node-newrelic/commit/40d1f5ccc50d49805fc68946806fc9f74179673b))
19
+ * Updated test-utils dependency and added matrix-count only ([#2494](https://github.com/newrelic/node-newrelic/pull/2494)) ([5e04c76](https://github.com/newrelic/node-newrelic/commit/5e04c76600b8e6b7bfe331c2bec1b6cfa05ab922))
20
+
21
+ #### Tests
22
+
23
+ * Converted the api unit tests to `node:test` ([#2516](https://github.com/newrelic/node-newrelic/pull/2516)) ([ab91576](https://github.com/newrelic/node-newrelic/commit/ab91576fa949161f902b1604752a7fc38e7f2a74))
24
+ * Converted context-manager unit tests to `node:test` ([#2508](https://github.com/newrelic/node-newrelic/pull/2508)) ([9363eb0](https://github.com/newrelic/node-newrelic/commit/9363eb08ce8a13e67f94e5378ca95f32a562d504))
25
+
26
+ #### Continuous integration
27
+
28
+ * Updated codecov action sha to post coverage from forks. Added flag to fail ci if it fails to upload report ([#2490](https://github.com/newrelic/node-newrelic/pull/2490)) ([12fbe56](https://github.com/newrelic/node-newrelic/commit/12fbe56ca2581b3dd5cc5e2c1eceade46a8d191d))
29
+
30
+ ### v12.2.0 (2024-08-19)
31
+
32
+ #### Features
33
+
34
+ * Added instrumentation support for Express 5 beta ([#2476](https://github.com/newrelic/node-newrelic/pull/2476)) ([06a4c2f](https://github.com/newrelic/node-newrelic/commit/06a4c2f9d62f7313fd246b4eed7f9f04f8b6345b))
35
+ * This will be experimental until `express@5.0.0` is generally available
36
+
37
+ #### Bug fixes
38
+
39
+ * Updated `koa` instrumentation to properly get the matched route name and to handle changes in `@koa/router@13.0.0` ([#2486](https://github.com/newrelic/node-newrelic/pull/2486)) ([0c2ee2f](https://github.com/newrelic/node-newrelic/commit/0c2ee2fd1698972de35a0ad2685e626a074125ed))
40
+
41
+ #### Documentation
42
+
43
+ * Removed reference to `@newrelic/next` in README ([#2479](https://github.com/newrelic/node-newrelic/pull/2479)) ([8740539](https://github.com/newrelic/node-newrelic/commit/8740539c4004e421e5f26d0c92216bcffb93c9cc))
44
+ * Updated compatibility report ([#2487](https://github.com/newrelic/node-newrelic/pull/2487)) ([c0a5e64](https://github.com/newrelic/node-newrelic/commit/c0a5e646773c365897a908daf034881703dbd1df))
45
+
46
+ #### Miscellaneous chores
47
+
48
+ * Reverted to upstream `require-in-the-middle` ([#2473](https://github.com/newrelic/node-newrelic/pull/2473)) ([9bbc41c](https://github.com/newrelic/node-newrelic/commit/9bbc41c5be479af56d5aa0c87291d2fec607e9e4))
49
+ * Updated aggregators unit tests to node:test ([#2481](https://github.com/newrelic/node-newrelic/pull/2481)) ([fd2d76f](https://github.com/newrelic/node-newrelic/commit/fd2d76fb2f6e8debc165700f932d57a02c3d3956))
50
+
1
51
  ### v12.1.1 (2024-08-15)
2
52
 
3
53
  #### Bug fixes
package/README.md CHANGED
@@ -152,7 +152,6 @@ For more information on getting started, [check the Node.js docs](https://docs.n
152
152
  There are modules that can be installed and configured to accompany the Node.js agent:
153
153
 
154
154
  * [@newrelic/apollo-server-plugin](https://github.com/newrelic/newrelic-node-apollo-server-plugin): New Relic's official Apollo Server plugin for use with the Node.js agent.
155
- * [@newrelic/next](https://github.com/newrelic/newrelic-node-nextjs): Provides instrumentation for the [Next.js](https://github.com/vercel/next.js/) npm package.
156
155
 
157
156
  There are modules included within the Node.js agent to add more instrumentation for 3rd party modules:
158
157
 
@@ -16,7 +16,6 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic
16
16
 
17
17
  * [@grpc/grpc-js](#grpcgrpc-js)
18
18
  * [@grpc/proto-loader](#grpcproto-loader)
19
- * [@newrelic/ritm](#newrelicritm)
20
19
  * [@newrelic/security-agent](#newrelicsecurity-agent)
21
20
  * [@tyriar/fibonacci-heap](#tyriarfibonacci-heap)
22
21
  * [concat-stream](#concat-stream)
@@ -26,6 +25,7 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic
26
25
  * [json-stringify-safe](#json-stringify-safe)
27
26
  * [module-details-from-path](#module-details-from-path)
28
27
  * [readable-stream](#readable-stream)
28
+ * [require-in-the-middle](#require-in-the-middle)
29
29
  * [semver](#semver)
30
30
  * [winston-transport](#winston-transport)
31
31
 
@@ -509,39 +509,9 @@ This product includes source derived from [@grpc/proto-loader](https://github.co
509
509
 
510
510
  ```
511
511
 
512
- ### @newrelic/ritm
513
-
514
- This product includes source derived from [@newrelic/ritm](https://github.com/newrelic-forks/require-in-the-middle) ([v7.2.0](https://github.com/newrelic-forks/require-in-the-middle/tree/v7.2.0)), distributed under the [MIT License](https://github.com/newrelic-forks/require-in-the-middle/blob/v7.2.0/LICENSE):
515
-
516
- ```
517
- The MIT License (MIT)
518
-
519
- Copyright (c) 2016-2019, Thomas Watson Steen
520
- Copyright (c) 2019-2023, Elasticsearch B.V.
521
-
522
- Permission is hereby granted, free of charge, to any person obtaining a copy
523
- of this software and associated documentation files (the "Software"), to deal
524
- in the Software without restriction, including without limitation the rights
525
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
526
- copies of the Software, and to permit persons to whom the Software is
527
- furnished to do so, subject to the following conditions:
528
-
529
- The above copyright notice and this permission notice shall be included in all
530
- copies or substantial portions of the Software.
531
-
532
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
533
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
534
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
535
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
536
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
537
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
538
- SOFTWARE.
539
-
540
- ```
541
-
542
512
  ### @newrelic/security-agent
543
513
 
544
- This product includes source derived from [@newrelic/security-agent](https://github.com/newrelic/csec-node-agent) ([v1.4.0](https://github.com/newrelic/csec-node-agent/tree/v1.4.0)), distributed under the [UNKNOWN License](https://github.com/newrelic/csec-node-agent/blob/v1.4.0/LICENSE):
514
+ This product includes source derived from [@newrelic/security-agent](https://github.com/newrelic/csec-node-agent) ([v1.5.0](https://github.com/newrelic/csec-node-agent/tree/v1.5.0)), distributed under the [UNKNOWN License](https://github.com/newrelic/csec-node-agent/blob/v1.5.0/LICENSE):
545
515
 
546
516
  ```
547
517
  ## New Relic Software License v1.0
@@ -676,7 +646,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
676
646
 
677
647
  ### import-in-the-middle
678
648
 
679
- This product includes source derived from [import-in-the-middle](https://github.com/nodejs/import-in-the-middle) ([v1.9.1](https://github.com/nodejs/import-in-the-middle/tree/v1.9.1)), distributed under the [Apache-2.0 License](https://github.com/nodejs/import-in-the-middle/blob/v1.9.1/LICENSE):
649
+ This product includes source derived from [import-in-the-middle](https://github.com/nodejs/import-in-the-middle) ([v1.11.0](https://github.com/nodejs/import-in-the-middle/tree/v1.11.0)), distributed under the [Apache-2.0 License](https://github.com/nodejs/import-in-the-middle/blob/v1.11.0/LICENSE):
680
650
 
681
651
  ```
682
652
  Apache License
@@ -1018,6 +988,36 @@ IN THE SOFTWARE.
1018
988
 
1019
989
  ```
1020
990
 
991
+ ### require-in-the-middle
992
+
993
+ This product includes source derived from [require-in-the-middle](https://github.com/elastic/require-in-the-middle) ([v7.4.0](https://github.com/elastic/require-in-the-middle/tree/v7.4.0)), distributed under the [MIT License](https://github.com/elastic/require-in-the-middle/blob/v7.4.0/LICENSE):
994
+
995
+ ```
996
+ The MIT License (MIT)
997
+
998
+ Copyright (c) 2016-2019, Thomas Watson Steen
999
+ Copyright (c) 2019-2023, Elasticsearch B.V.
1000
+
1001
+ Permission is hereby granted, free of charge, to any person obtaining a copy
1002
+ of this software and associated documentation files (the "Software"), to deal
1003
+ in the Software without restriction, including without limitation the rights
1004
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1005
+ copies of the Software, and to permit persons to whom the Software is
1006
+ furnished to do so, subject to the following conditions:
1007
+
1008
+ The above copyright notice and this permission notice shall be included in all
1009
+ copies or substantial portions of the Software.
1010
+
1011
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1012
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1013
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1014
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1015
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1016
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1017
+ SOFTWARE.
1018
+
1019
+ ```
1020
+
1021
1021
  ### semver
1022
1022
 
1023
1023
  This product includes source derived from [semver](https://github.com/npm/node-semver) ([v7.6.3](https://github.com/npm/node-semver/tree/v7.6.3)), distributed under the [ISC License](https://github.com/npm/node-semver/blob/v7.6.3/LICENSE):
@@ -1076,7 +1076,7 @@ SOFTWARE.
1076
1076
 
1077
1077
  ### @aws-sdk/client-s3
1078
1078
 
1079
- This product includes source derived from [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3) ([v3.616.0](https://github.com/aws/aws-sdk-js-v3/tree/v3.616.0)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.616.0/LICENSE):
1079
+ This product includes source derived from [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3) ([v3.632.0](https://github.com/aws/aws-sdk-js-v3/tree/v3.632.0)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.632.0/LICENSE):
1080
1080
 
1081
1081
  ```
1082
1082
  Apache License
@@ -1285,7 +1285,7 @@ This product includes source derived from [@aws-sdk/client-s3](https://github.co
1285
1285
 
1286
1286
  ### @aws-sdk/s3-request-presigner
1287
1287
 
1288
- This product includes source derived from [@aws-sdk/s3-request-presigner](https://github.com/aws/aws-sdk-js-v3) ([v3.616.0](https://github.com/aws/aws-sdk-js-v3/tree/v3.616.0)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.616.0/LICENSE):
1288
+ This product includes source derived from [@aws-sdk/s3-request-presigner](https://github.com/aws/aws-sdk-js-v3) ([v3.632.0](https://github.com/aws/aws-sdk-js-v3/tree/v3.632.0)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.632.0/LICENSE):
1289
1289
 
1290
1290
  ```
1291
1291
  Apache License
@@ -1940,7 +1940,7 @@ This product includes source derived from [@newrelic/newrelic-oss-cli](https://g
1940
1940
 
1941
1941
  ### @newrelic/test-utilities
1942
1942
 
1943
- This product includes source derived from [@newrelic/test-utilities](https://github.com/newrelic/node-test-utilities) ([v8.7.0](https://github.com/newrelic/node-test-utilities/tree/v8.7.0)), distributed under the [Apache-2.0 License](https://github.com/newrelic/node-test-utilities/blob/v8.7.0/LICENSE):
1943
+ This product includes source derived from [@newrelic/test-utilities](https://github.com/newrelic/node-test-utilities) ([v9.1.0](https://github.com/newrelic/node-test-utilities/tree/v9.1.0)), distributed under the [Apache-2.0 License](https://github.com/newrelic/node-test-utilities/blob/v9.1.0/LICENSE):
1944
1944
 
1945
1945
  ```
1946
1946
  Apache License
@@ -2179,7 +2179,7 @@ THE SOFTWARE.
2179
2179
 
2180
2180
  ### @slack/bolt
2181
2181
 
2182
- This product includes source derived from [@slack/bolt](https://github.com/slackapi/bolt) ([v3.19.0](https://github.com/slackapi/bolt/tree/v3.19.0)), distributed under the [MIT License](https://github.com/slackapi/bolt/blob/v3.19.0/LICENSE):
2182
+ This product includes source derived from [@slack/bolt](https://github.com/slackapi/bolt) ([v3.21.1](https://github.com/slackapi/bolt/tree/v3.21.1)), distributed under the [MIT License](https://github.com/slackapi/bolt/blob/v3.21.1/LICENSE):
2183
2183
 
2184
2184
  ```
2185
2185
  The MIT License (MIT)
@@ -2683,7 +2683,7 @@ THE SOFTWARE.
2683
2683
 
2684
2684
  ### aws-sdk
2685
2685
 
2686
- This product includes source derived from [aws-sdk](https://github.com/aws/aws-sdk-js) ([v2.1659.0](https://github.com/aws/aws-sdk-js/tree/v2.1659.0)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js/blob/v2.1659.0/LICENSE.txt):
2686
+ This product includes source derived from [aws-sdk](https://github.com/aws/aws-sdk-js) ([v2.1677.0](https://github.com/aws/aws-sdk-js/tree/v2.1677.0)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js/blob/v2.1677.0/LICENSE.txt):
2687
2687
 
2688
2688
  ```
2689
2689
 
@@ -3111,7 +3111,7 @@ THE SOFTWARE.
3111
3111
 
3112
3112
  ### eslint-plugin-jsdoc
3113
3113
 
3114
- This product includes source derived from [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) ([v48.8.0](https://github.com/gajus/eslint-plugin-jsdoc/tree/v48.8.0)), distributed under the [BSD-3-Clause License](https://github.com/gajus/eslint-plugin-jsdoc/blob/v48.8.0/LICENSE):
3114
+ This product includes source derived from [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) ([v48.11.0](https://github.com/gajus/eslint-plugin-jsdoc/tree/v48.11.0)), distributed under the [BSD-3-Clause License](https://github.com/gajus/eslint-plugin-jsdoc/blob/v48.11.0/LICENSE):
3115
3115
 
3116
3116
  ```
3117
3117
  Copyright (c) 2018, Gajus Kuizinas (http://gajus.com/)
@@ -4026,7 +4026,7 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
4026
4026
 
4027
4027
  ### self-cert
4028
4028
 
4029
- This product includes source derived from [self-cert](https://github.com/jsumners/self-cert) ([v2.0.0](https://github.com/jsumners/self-cert/tree/v2.0.0)), distributed under the [MIT License](https://github.com/jsumners/self-cert/blob/v2.0.0/Readme.md):
4029
+ This product includes source derived from [self-cert](https://github.com/jsumners/self-cert) ([v2.0.1](https://github.com/jsumners/self-cert/tree/v2.0.1)), distributed under the [MIT License](https://github.com/jsumners/self-cert/blob/v2.0.1/Readme.md):
4030
4030
 
4031
4031
  ```
4032
4032
  MIT License
@@ -4189,7 +4189,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
4189
4189
 
4190
4190
  ### @contrast/fn-inspect
4191
4191
 
4192
- This product includes source derived from [@contrast/fn-inspect](https://github.com/Contrast-Security-Inc/node-fn-inspect) ([v4.2.0](https://github.com/Contrast-Security-Inc/node-fn-inspect/tree/v4.2.0)), distributed under the [MIT License](https://github.com/Contrast-Security-Inc/node-fn-inspect/blob/v4.2.0/LICENSE):
4192
+ This product includes source derived from [@contrast/fn-inspect](https://github.com/Contrast-Security-Inc/node-fn-inspect) ([v4.3.0](https://github.com/Contrast-Security-Inc/node-fn-inspect/tree/v4.3.0)), distributed under the [MIT License](https://github.com/Contrast-Security-Inc/node-fn-inspect/blob/v4.3.0/LICENSE):
4193
4193
 
4194
4194
  ```
4195
4195
  Copyright 2022 Contrast Security, Inc
@@ -4215,7 +4215,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
4215
4215
 
4216
4216
  ### @newrelic/native-metrics
4217
4217
 
4218
- This product includes source derived from [@newrelic/native-metrics](https://github.com/newrelic/node-native-metrics) ([v10.2.0](https://github.com/newrelic/node-native-metrics/tree/v10.2.0)), distributed under the [Apache-2.0 License](https://github.com/newrelic/node-native-metrics/blob/v10.2.0/LICENSE):
4218
+ This product includes source derived from [@newrelic/native-metrics](https://github.com/newrelic/node-native-metrics) ([v11.0.0](https://github.com/newrelic/node-native-metrics/tree/v11.0.0)), distributed under the [Apache-2.0 License](https://github.com/newrelic/node-native-metrics/blob/v11.0.0/LICENSE):
4219
4219
 
4220
4220
  ```
4221
4221
  Apache License
package/api.js CHANGED
@@ -32,6 +32,7 @@ const obfuscate = require('./lib/util/sql/obfuscate')
32
32
  const { DESTINATIONS } = require('./lib/config/attribute-filter')
33
33
  const parse = require('module-details-from-path')
34
34
  const { isSimpleObject } = require('./lib/util/objects')
35
+ const { AsyncLocalStorage } = require('async_hooks')
35
36
 
36
37
  /*
37
38
  *
@@ -1902,4 +1903,52 @@ API.prototype.ignoreApdex = function ignoreApdex() {
1902
1903
  transaction.ignoreApdex = true
1903
1904
  }
1904
1905
 
1906
+ /**
1907
+ * Run a function with the passed in LLM context as the active context and return its return value.
1908
+ *
1909
+ * An example of setting a custom attribute:
1910
+ *
1911
+ * newrelic.withLlmCustomAttributes({'llm.someAttribute': 'someValue'}, () => {
1912
+ * return;
1913
+ * })
1914
+ * @param {Object} context LLM custom attributes context
1915
+ * @param {Function} callback The function to execute in context.
1916
+ */
1917
+ API.prototype.withLlmCustomAttributes = function withLlmCustomAttributes(context, callback) {
1918
+ context = context || {}
1919
+ const metric = this.agent.metrics.getOrCreateMetric(
1920
+ NAMES.SUPPORTABILITY.API + '/withLlmCustomAttributes'
1921
+ )
1922
+ metric.incrementCallCount()
1923
+
1924
+ const transaction = this.agent.tracer.getTransaction()
1925
+
1926
+ if (!callback || typeof callback !== 'function') {
1927
+ logger.warn('withLlmCustomAttributes must be used with a valid callback')
1928
+ return
1929
+ }
1930
+
1931
+ if (!transaction) {
1932
+ logger.warn('withLlmCustomAttributes must be called within the scope of a transaction.')
1933
+ return callback()
1934
+ }
1935
+
1936
+ for (const [key, value] of Object.entries(context)) {
1937
+ if (typeof value === 'object' || typeof value === 'function') {
1938
+ logger.warn(`Invalid attribute type for ${key}. Skipped.`)
1939
+ delete context[key]
1940
+ } else if (key.indexOf('llm.') !== 0) {
1941
+ logger.warn(`Invalid attribute name ${key}. Renamed to "llm.${key}".`)
1942
+ delete context[key]
1943
+ context[`llm.${key}`] = value
1944
+ }
1945
+ }
1946
+
1947
+ transaction._llmContextManager = transaction._llmContextManager || new AsyncLocalStorage()
1948
+ const parentContext = transaction._llmContextManager.getStore() || {}
1949
+
1950
+ const fullContext = Object.assign({}, parentContext, context)
1951
+ return transaction._llmContextManager.run(fullContext, callback)
1952
+ }
1953
+
1905
1954
  module.exports = API
@@ -18,6 +18,7 @@ const { DESTINATIONS } = require('../../../config/attribute-filter')
18
18
  const { AI } = require('../../../metrics/names')
19
19
  const { RecorderSpec } = require('../../../shim/specs')
20
20
  const InstrumentationDescriptor = require('../../../instrumentation-descriptor')
21
+ const { extractLlmContext } = require('../../../util/llm-utils')
21
22
 
22
23
  let TRACKING_METRIC
23
24
 
@@ -55,7 +56,12 @@ function isStreamingEnabled({ commandName, config }) {
55
56
  */
56
57
  function recordEvent({ agent, type, msg }) {
57
58
  msg.serialize()
58
- agent.customEventAggregator.add([{ type, timestamp: Date.now() }, msg])
59
+ const llmContext = extractLlmContext(agent)
60
+
61
+ agent.customEventAggregator.add([
62
+ { type, timestamp: Date.now() },
63
+ Object.assign({}, msg, llmContext)
64
+ ])
59
65
  }
60
66
 
61
67
  /**
@@ -6,7 +6,6 @@
6
6
  'use strict'
7
7
 
8
8
  const { MiddlewareSpec, MiddlewareMounterSpec, RenderSpec } = require('../../lib/shim/specs')
9
- const { MIDDLEWARE_TYPE_NAMES } = require('../../lib/shim/webframework-shim/common')
10
9
 
11
10
  /**
12
11
  * Express middleware generates traces where middleware are considered siblings
@@ -25,18 +24,8 @@ module.exports = function initialize(agent, express, moduleName, shim) {
25
24
  return err !== 'route' && err !== 'router'
26
25
  })
27
26
 
28
- if (express.Router.use) {
29
- wrapExpress4(shim, express)
30
- } else {
31
- wrapExpress3(shim, express)
32
- }
33
- }
34
-
35
- function wrapExpress4(shim, express) {
36
- // Wrap `use` and `route` which are hung off `Router` directly, not on a
37
- // prototype.
38
27
  shim.wrapMiddlewareMounter(
39
- express.Router,
28
+ express.application,
40
29
  'use',
41
30
  new MiddlewareMounterSpec({
42
31
  route: shim.FIRST,
@@ -44,8 +33,13 @@ function wrapExpress4(shim, express) {
44
33
  })
45
34
  )
46
35
 
36
+ wrapExpressRouter(shim, express.Router.use ? express.Router : express.Router.prototype)
37
+ wrapResponse(shim, express.response)
38
+ }
39
+
40
+ function wrapExpressRouter(shim, router) {
47
41
  shim.wrapMiddlewareMounter(
48
- express.application,
42
+ router,
49
43
  'use',
50
44
  new MiddlewareMounterSpec({
51
45
  route: shim.FIRST,
@@ -53,7 +47,7 @@ function wrapExpress4(shim, express) {
53
47
  })
54
48
  )
55
49
 
56
- shim.wrap(express.Router, 'route', function wrapRoute(shim, fn) {
50
+ shim.wrap(router, 'route', function wrapRoute(shim, fn) {
57
51
  if (!shim.isFunction(fn)) {
58
52
  return fn
59
53
  }
@@ -89,7 +83,7 @@ function wrapExpress4(shim, express) {
89
83
  })
90
84
 
91
85
  shim.wrapMiddlewareMounter(
92
- express.Router,
86
+ router,
93
87
  'param',
94
88
  new MiddlewareMounterSpec({
95
89
  route: shim.FIRST,
@@ -105,56 +99,6 @@ function wrapExpress4(shim, express) {
105
99
  }
106
100
  })
107
101
  )
108
-
109
- wrapResponse(shim, express.response)
110
- }
111
-
112
- function wrapExpress3(shim, express) {
113
- // In Express 3 the app returned from `express()` is actually a `connect` app
114
- // which we have no access to before creation. We can not easily wrap the app
115
- // because there are a lot of methods dangling on it that act on the app itself.
116
- // Really we just care about apps being used as `request` event listeners on
117
- // `http.Server` instances so we'll wrap that instead.
118
-
119
- shim.wrapMiddlewareMounter(
120
- express.Router.prototype,
121
- 'param',
122
- new MiddlewareMounterSpec({
123
- route: shim.FIRST,
124
- wrapper: function wrapParamware(shim, middleware, fnName, route) {
125
- return shim.recordParamware(
126
- middleware,
127
- new MiddlewareSpec({
128
- name: route,
129
- req: shim.FIRST,
130
- next: shim.THIRD,
131
- type: MIDDLEWARE_TYPE_NAMES.PARAMWARE
132
- })
133
- )
134
- }
135
- })
136
- )
137
- shim.wrapMiddlewareMounter(
138
- express.Router.prototype,
139
- 'use',
140
- new MiddlewareMounterSpec({
141
- route: shim.FIRST,
142
- wrapper: wrapMiddleware
143
- })
144
- )
145
- shim.wrapMiddlewareMounter(
146
- express.application,
147
- 'use',
148
- new MiddlewareMounterSpec({
149
- route: shim.FIRST,
150
- wrapper: wrapMiddleware
151
- })
152
- )
153
-
154
- // NOTE: Do not wrap application route methods in Express 3, they all just
155
- // forward their arguments to the router.
156
- wrapRouteMethods(shim, express.Router.prototype, shim.FIRST)
157
- wrapResponse(shim, express.response)
158
102
  }
159
103
 
160
104
  function wrapRouteMethods(shim, route, path) {
@@ -115,10 +115,8 @@ function wrapMatchedRoute(shim, context) {
115
115
  Object.defineProperty(context, '_matchedRoute', {
116
116
  get: () => context[symbols.koaMatchedRoute],
117
117
  set: (val) => {
118
- const match = getLayerForTransactionName(context)
119
-
120
118
  // match should never be undefined given _matchedRoute was set
121
- if (match) {
119
+ if (val) {
122
120
  const currentSegment = shim.getActiveSegment()
123
121
 
124
122
  // Segment/Transaction may be null, see:
@@ -131,7 +129,7 @@ function wrapMatchedRoute(shim, context) {
131
129
  transaction.nameState.popPath()
132
130
  }
133
131
 
134
- transaction.nameState.appendPath(match.path)
132
+ transaction.nameState.appendPath(val)
135
133
  transaction.nameState.markPath()
136
134
  }
137
135
  }
@@ -169,22 +167,6 @@ function wrapResponseStatus(shim, context) {
169
167
  })
170
168
  }
171
169
 
172
- function getLayerForTransactionName(context) {
173
- // Context.matched might be null
174
- // See https://github.com/newrelic/node-newrelic-koa/pull/29
175
- if (!context.matched) {
176
- return null
177
- }
178
- for (let i = context.matched.length - 1; i >= 0; i--) {
179
- const layer = context.matched[i]
180
- if (layer.opts.end) {
181
- return layer
182
- }
183
- }
184
-
185
- return null
186
- }
187
-
188
170
  function getInheritedPropertyDescriptor(obj, property) {
189
171
  let proto = obj
190
172
  let descriptor = null
@@ -7,6 +7,7 @@
7
7
  const {
8
8
  AI: { LANGCHAIN }
9
9
  } = require('../../metrics/names')
10
+ const { extractLlmContext } = require('../../util/llm-utils')
10
11
 
11
12
  const common = module.exports
12
13
 
@@ -49,7 +50,12 @@ common.mergeMetadata = function mergeMetadata(localMeta = {}, paramsMeta = {}) {
49
50
  */
50
51
  common.recordEvent = function recordEvent({ agent, type, msg, pkgVersion }) {
51
52
  agent.metrics.getOrCreateMetric(`${LANGCHAIN.TRACKING_PREFIX}/${pkgVersion}`).incrementCallCount()
52
- agent.customEventAggregator.add([{ type, timestamp: Date.now() }, msg])
53
+ const llmContext = extractLlmContext(agent)
54
+
55
+ agent.customEventAggregator.add([
56
+ { type, timestamp: Date.now() },
57
+ Object.assign({}, msg, llmContext)
58
+ ])
53
59
  }
54
60
 
55
61
  /**
@@ -22,8 +22,16 @@ module.exports = [
22
22
  onRequire: cbManagerInstrumentation
23
23
  },
24
24
  {
25
+ // This block is for catching langchain internal imports
26
+ // of the callback manager. See:
27
+ // https://github.com/elastic/require-in-the-middle/pull/88#issuecomment-2124940546
25
28
  type: InstrumentationDescriptor.TYPE_GENERIC,
26
- moduleName: '@langchain/core/dist/runnables/base',
29
+ moduleName: '@langchain/core/dist/callbacks/manager.cjs',
30
+ onRequire: cbManagerInstrumentation
31
+ },
32
+ {
33
+ type: InstrumentationDescriptor.TYPE_GENERIC,
34
+ moduleName: '@langchain/core/runnables',
27
35
  onRequire: runnableInstrumentation
28
36
  },
29
37
  {
@@ -12,6 +12,7 @@ const {
12
12
  LlmErrorMessage
13
13
  } = require('../../lib/llm-events/openai')
14
14
  const { RecorderSpec } = require('../../lib/shim/specs')
15
+ const { extractLlmContext } = require('../util/llm-utils')
15
16
 
16
17
  const MIN_VERSION = '4.0.0'
17
18
  const MIN_STREAM_VERSION = '4.12.2'
@@ -75,7 +76,12 @@ function decorateSegment({ shim, result, apiKey }) {
75
76
  * @param {object} params.msg LLM event
76
77
  */
77
78
  function recordEvent({ agent, type, msg }) {
78
- agent.customEventAggregator.add([{ type, timestamp: Date.now() }, msg])
79
+ const llmContext = extractLlmContext(agent)
80
+
81
+ agent.customEventAggregator.add([
82
+ { type, timestamp: Date.now() },
83
+ Object.assign({}, msg, llmContext)
84
+ ])
79
85
  }
80
86
 
81
87
  /**
@@ -106,38 +106,45 @@ function isLambdaProxyEvent(event) {
106
106
  return isGatewayV1Event(event) || isGatewayV2Event(event)
107
107
  }
108
108
 
109
- function isGatewayV1Event(event) {
110
- let result = false
111
-
112
- if (event?.version === '1.0') {
113
- result = true
114
- } else if (
115
- typeof event?.path === 'string' &&
116
- (event.headers ?? event.multiValueHeaders) &&
117
- typeof event?.httpMethod === 'string'
118
- // eslint-disable-next-line sonarjs/no-duplicated-branches
119
- ) {
120
- result = true
121
- }
109
+ const v1Keys = [
110
+ 'body',
111
+ 'headers',
112
+ 'httpMethod',
113
+ 'isBase64Encoded',
114
+ 'multiValueHeaders',
115
+ 'multiValueQueryStringParameters',
116
+ 'path',
117
+ 'pathParameters',
118
+ 'queryStringParameters',
119
+ 'requestContext',
120
+ 'resource',
121
+ 'stageVariables',
122
+ 'version'
123
+ ].join(',')
122
124
 
123
- return result
125
+ function isGatewayV1Event(event) {
126
+ const keys = Object.keys(event).sort().join(',')
127
+ return keys === v1Keys && event?.version === '1.0'
124
128
  }
125
129
 
126
- function isGatewayV2Event(event) {
127
- let result = false
128
-
129
- if (event?.version === '2.0') {
130
- result = true
131
- } else if (
132
- typeof event?.requestContext?.http?.path === 'string' &&
133
- Object.prototype.toString.call(event.headers) === '[object Object]' &&
134
- typeof event?.requestContext?.http?.method === 'string'
135
- // eslint-disable-next-line sonarjs/no-duplicated-branches
136
- ) {
137
- result = true
138
- }
130
+ const v2Keys = [
131
+ 'body',
132
+ 'cookies',
133
+ 'headers',
134
+ 'isBase64Encoded',
135
+ 'pathParameters',
136
+ 'queryStringParameters',
137
+ 'rawPath',
138
+ 'rawQueryString',
139
+ 'requestContext',
140
+ 'routeKey',
141
+ 'stageVariables',
142
+ 'version'
143
+ ].join(',')
139
144
 
140
- return result
145
+ function isGatewayV2Event(event) {
146
+ const keys = Object.keys(event).sort().join(',')
147
+ return keys === v2Keys && event?.version === '2.0'
141
148
  }
142
149
 
143
150
  /**
@@ -155,5 +162,7 @@ module.exports = {
155
162
  LambdaProxyWebRequest,
156
163
  LambdaProxyWebResponse,
157
164
  isLambdaProxyEvent,
158
- isValidLambdaProxyResponse
165
+ isValidLambdaProxyResponse,
166
+ isGatewayV1Event,
167
+ isGatewayV2Event
159
168
  }
package/lib/shimmer.js CHANGED
@@ -11,7 +11,7 @@ const fs = require('./util/unwrapped-core').fs
11
11
  const logger = require('./logger').child({ component: 'shimmer' })
12
12
  const INSTRUMENTATIONS = require('./instrumentations')()
13
13
  const shims = require('./shim')
14
- const { Hook } = require('@newrelic/ritm')
14
+ const { Hook } = require('require-in-the-middle')
15
15
  const IitmHook = require('import-in-the-middle')
16
16
  const { nrEsmProxy } = require('./symbols')
17
17
  const isAbsolutePath = require('./util/is-absolute-path')
@@ -0,0 +1,34 @@
1
+ /*
2
+ * Copyright 2020 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+
8
+ exports = module.exports = { extractLlmContext, extractLlmAttributes }
9
+
10
+ /**
11
+ * Extract LLM attributes from the LLM context
12
+ *
13
+ * @param {Object} context LLM context object
14
+ * @returns {Object} LLM custom attributes
15
+ */
16
+ function extractLlmAttributes(context) {
17
+ return Object.keys(context).reduce((result, key) => {
18
+ if (key.indexOf('llm.') === 0) {
19
+ result[key] = context[key]
20
+ }
21
+ return result
22
+ }, {})
23
+ }
24
+
25
+ /**
26
+ * Extract LLM context from the active transaction
27
+ *
28
+ * @param {Agent} agent NR agent instance
29
+ * @returns {Object} LLM context object
30
+ */
31
+ function extractLlmContext(agent) {
32
+ const context = agent.tracer.getTransaction()?._llmContextManager?.getStore() || {}
33
+ return extractLlmAttributes(context)
34
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newrelic",
3
- "version": "12.1.1",
3
+ "version": "12.3.0",
4
4
  "author": "New Relic Node.js agent team <nodejs@newrelic.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [
@@ -164,9 +164,9 @@
164
164
  "integration": "npm run prepare-test && npm run sub-install && time c8 -o ./coverage/integration borp --timeout 600000 --reporter ./test/lib/test-reporter.mjs 'test/integration/**/*.tap.js'",
165
165
  "integration:esm": "NODE_OPTIONS='--loader=./esm-loader.mjs' time c8 -o ./coverage/integration-esm borp --reporter ./test/lib/test-reporter.mjs 'test/integration/**/*.tap.mjs'",
166
166
  "prepare-test": "npm run ssl && npm run docker-env",
167
- "lint": "eslint ./*.{js,mjs} lib test bin examples",
168
- "lint:fix": "eslint --fix, ./*.{js,mjs} lib test bin examples",
169
- "public-docs": "jsdoc -c ./jsdoc-conf.jsonc && cp examples/shim/*.png out/",
167
+ "lint": "eslint ./*.{js,mjs} lib test bin",
168
+ "lint:fix": "eslint --fix, ./*.{js,mjs} lib test bin",
169
+ "public-docs": "jsdoc -c ./jsdoc-conf.jsonc",
170
170
  "publish-docs": "./bin/publish-docs.sh",
171
171
  "services": "DOCKER_PLATFORM=linux/$(uname -m) docker compose up -d --wait",
172
172
  "services:stop": "docker compose down",
@@ -197,7 +197,6 @@
197
197
  "dependencies": {
198
198
  "@grpc/grpc-js": "^1.9.4",
199
199
  "@grpc/proto-loader": "^0.7.5",
200
- "@newrelic/ritm": "^7.2.0",
201
200
  "@newrelic/security-agent": "^1.3.0",
202
201
  "@tyriar/fibonacci-heap": "^2.0.7",
203
202
  "concat-stream": "^2.0.0",
@@ -207,6 +206,7 @@
207
206
  "json-stringify-safe": "^5.0.0",
208
207
  "module-details-from-path": "^1.0.3",
209
208
  "readable-stream": "^3.6.1",
209
+ "require-in-the-middle": "^7.4.0",
210
210
  "semver": "^7.5.2",
211
211
  "winston-transport": "^4.5.0"
212
212
  },
@@ -221,7 +221,7 @@
221
221
  "@koa/router": "^12.0.1",
222
222
  "@newrelic/eslint-config": "^0.3.0",
223
223
  "@newrelic/newrelic-oss-cli": "^0.1.2",
224
- "@newrelic/test-utilities": "^8.5.0",
224
+ "@newrelic/test-utilities": "^9.1.0",
225
225
  "@octokit/rest": "^18.0.15",
226
226
  "@slack/bolt": "^3.7.0",
227
227
  "@smithy/eventstream-codec": "^2.2.0",