newrelic 12.17.0 → 12.18.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,32 +1,76 @@
1
- ### v12.17.0 (2025-04-02)
2
-
3
- #### Features
4
-
5
- * Azure Function utilization ([#3017](https://github.com/newrelic/node-newrelic/pull/3017)) ([80ab93e](https://github.com/newrelic/node-newrelic/commit/80ab93ee5599800a823e3828f9730d5421b460c2))
6
- * Support honoring W3C `traceparent` sampled flag ([#3009](https://github.com/newrelic/node-newrelic/pull/3009)) ([d903413](https://github.com/newrelic/node-newrelic/commit/d903413a6216dae2cde3e6b6366c3a390d4ed4cd))
7
- * By default the agent will not honor the `traceparent` sampled flag.
8
- * To control how sampling works with `traceparent` set the following in config:
9
- * `distributed_tracing.sampler.remote_parent_sampled`(when the traceparent sampled flag is `01`)
10
- * `always_on`: the agent will sample spans
11
- * `always_off`: the agent will not sample spans
12
- * `default`: the agent will rely on existing priority sampling to make its decisions
13
- * `distributed_tracing.sampler.remote_parent_not_sampled`(when the traceparent sampled flag is `00`)
14
- * `always_on`: the agent will sample spans
15
- * `always_off`: the agent will not sample spans
16
- * `default`: the agent will rely on existing priority sampling to make its decisions
17
-
18
- #### Code refactoring
19
-
20
- * Updated otel bridge to centralize mapping rules for a given span attribute to accomondate semantic convention spec updates ([#3010](https://github.com/newrelic/node-newrelic/pull/3010)) ([c20c36b](https://github.com/newrelic/node-newrelic/commit/c20c36bbe0f63ab18fbbe85ad4bfaa3b80f1475d))
21
-
22
- #### Documentation
23
-
24
- * Updated compatibility report ([#3013](https://github.com/newrelic/node-newrelic/pull/3013)) ([8fab715](https://github.com/newrelic/node-newrelic/commit/8fab715581ccc5da7118884a0080557891329daa))
25
-
26
- #### Tests
27
-
28
- * Pinned `openai@4.90.0` to work with `@langchain/openai` ([#3019](https://github.com/newrelic/node-newrelic/pull/3019)) ([eaa3db0](https://github.com/newrelic/node-newrelic/commit/eaa3db0a556a19c44a05926dc339ea7bcbc9cebd))
29
-
1
+ ### v12.18.1 (2025-04-29)
2
+
3
+ #### Bug fixes
4
+
5
+ * Only add `newrelic` header to outgoing headers if has a value ([#3052](https://github.com/newrelic/node-newrelic/pull/3052)) ([6ef0a90](https://github.com/newrelic/node-newrelic/commit/6ef0a904291fb4efc6d6f111b3ec93aac720c3ad))
6
+
7
+ #### Code refactoring
8
+
9
+ * Added defensive code when calculating exclusive time on a segment where it cannot locate the segment in the transaction trace tree ([#3051](https://github.com/newrelic/node-newrelic/pull/3051)) ([b4aa25e](https://github.com/newrelic/node-newrelic/commit/b4aa25e2df861dab732bb812bffe961c0cc3da36))
10
+ * Updated how otel bridge trace propagator assigns traceparent/tracestate headers ([#3046](https://github.com/newrelic/node-newrelic/pull/3046)) ([6e317b8](https://github.com/newrelic/node-newrelic/commit/6e317b889c68a2e0e69b1d4d2fa9fe2846a0e94f))
11
+
12
+ #### Documentation
13
+
14
+ * Updated compatibility report ([#3054](https://github.com/newrelic/node-newrelic/pull/3054)) ([#3050](https://github.com/newrelic/node-newrelic/pull/3050)) ([#3026](https://github.com/newrelic/node-newrelic/pull/3026))
15
+
16
+ #### Tests
17
+
18
+ * Skipped regressions in pg-native versioned tests ([#3053](https://github.com/newrelic/node-newrelic/pull/3053)) ([6ce3670](https://github.com/newrelic/node-newrelic/commit/6ce36706adc2e17a80b759b51c23807d617ad04f))
19
+ * Updated elasticsearch image for versioned tests ([#3038](https://github.com/newrelic/node-newrelic/pull/3038)) ([3d73f9f](https://github.com/newrelic/node-newrelic/commit/3d73f9faffd45d2d1465210d168822494439c577))
20
+
21
+ ### v12.18.0 (2025-04-14)
22
+
23
+ #### Features
24
+
25
+ * Support Azure functions background trigger types ([#3028](https://github.com/newrelic/node-newrelic/pull/3028)) ([3bf17b0](https://github.com/newrelic/node-newrelic/commit/3bf17b03abaa713c1f9ad544c54c24a6374359c2))
26
+ * Support Azure functions HTTP trigger type ([#3021](https://github.com/newrelic/node-newrelic/pull/3021)) ([90d894b](https://github.com/newrelic/node-newrelic/commit/90d894bf1eb4a48f4b7f314ae22eef2cc76c32b9))
27
+
28
+ #### Bug fixes
29
+
30
+ * Updated span streamer to properly retry failed batches and handle flushing batch queue every 5 seconds ([#3033](https://github.com/newrelic/node-newrelic/pull/3033)) ([7db0e7c](https://github.com/newrelic/node-newrelic/commit/7db0e7ca929d54858addd5255b46507f82333045))
31
+
32
+ #### Code refactoring
33
+
34
+ * Update otel bridge rules synthesizer to properly handle consumer kind rules ([#3003](https://github.com/newrelic/node-newrelic/pull/3003)) ([3295dd1](https://github.com/newrelic/node-newrelic/commit/3295dd12bbebb708f3eeca2ad32fbc157a892fec))
35
+
36
+ #### Documentation
37
+
38
+ * Updated compatibility report ([#3020](https://github.com/newrelic/node-newrelic/pull/3020)) ([ce32b67](https://github.com/newrelic/node-newrelic/commit/ce32b6754383b6b1fe95d1d37759810b4caaf34c))
39
+
40
+ #### Miscellaneous chores
41
+
42
+ * Fixed prisma CI issues ([#3031](https://github.com/newrelic/node-newrelic/pull/3031)) ([1349cae](https://github.com/newrelic/node-newrelic/commit/1349cae4d87941e4f55dffc2445c90e73d823bbc))
43
+ * Improved logging within utilization detection ([#3034](https://github.com/newrelic/node-newrelic/pull/3034)) ([b47bd46](https://github.com/newrelic/node-newrelic/commit/b47bd46ad144723ac62192eb9cc474ddd1fce98d))
44
+
45
+ ### v12.17.0 (2025-04-02)
46
+
47
+ #### Features
48
+
49
+ * Azure Function utilization ([#3017](https://github.com/newrelic/node-newrelic/pull/3017)) ([80ab93e](https://github.com/newrelic/node-newrelic/commit/80ab93ee5599800a823e3828f9730d5421b460c2))
50
+ * Support honoring W3C `traceparent` sampled flag ([#3009](https://github.com/newrelic/node-newrelic/pull/3009)) ([d903413](https://github.com/newrelic/node-newrelic/commit/d903413a6216dae2cde3e6b6366c3a390d4ed4cd))
51
+ * By default the agent will not honor the `traceparent` sampled flag.
52
+ * To control how sampling works with `traceparent` set the following in config:
53
+ * `distributed_tracing.sampler.remote_parent_sampled`(when the traceparent sampled flag is `01`)
54
+ * `always_on`: the agent will sample spans
55
+ * `always_off`: the agent will not sample spans
56
+ * `default`: the agent will rely on existing priority sampling to make its decisions
57
+ * `distributed_tracing.sampler.remote_parent_not_sampled`(when the traceparent sampled flag is `00`)
58
+ * `always_on`: the agent will sample spans
59
+ * `always_off`: the agent will not sample spans
60
+ * `default`: the agent will rely on existing priority sampling to make its decisions
61
+
62
+ #### Code refactoring
63
+
64
+ * Updated otel bridge to centralize mapping rules for a given span attribute to accomondate semantic convention spec updates ([#3010](https://github.com/newrelic/node-newrelic/pull/3010)) ([c20c36b](https://github.com/newrelic/node-newrelic/commit/c20c36bbe0f63ab18fbbe85ad4bfaa3b80f1475d))
65
+
66
+ #### Documentation
67
+
68
+ * Updated compatibility report ([#3013](https://github.com/newrelic/node-newrelic/pull/3013)) ([8fab715](https://github.com/newrelic/node-newrelic/commit/8fab715581ccc5da7118884a0080557891329daa))
69
+
70
+ #### Tests
71
+
72
+ * Pinned `openai@4.90.0` to work with `@langchain/openai` ([#3019](https://github.com/newrelic/node-newrelic/pull/3019)) ([eaa3db0](https://github.com/newrelic/node-newrelic/commit/eaa3db0a556a19c44a05926dc339ea7bcbc9cebd))
73
+
30
74
  ### v12.16.1 (2025-03-24)
31
75
 
32
76
  #### Features
@@ -91,7 +91,7 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic
91
91
 
92
92
  ### @grpc/grpc-js
93
93
 
94
- This product includes source derived from [@grpc/grpc-js](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js) ([v1.12.4](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/tree/v1.12.4)), distributed under the [Apache-2.0 License](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/blob/v1.12.4/LICENSE):
94
+ This product includes source derived from [@grpc/grpc-js](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js) ([v1.13.2](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/tree/v1.13.2)), distributed under the [Apache-2.0 License](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/blob/v1.13.2/LICENSE):
95
95
 
96
96
  ```
97
97
  Apache License
@@ -509,7 +509,7 @@ This product includes source derived from [@grpc/proto-loader](https://github.co
509
509
 
510
510
  ### @newrelic/security-agent
511
511
 
512
- This product includes source derived from [@newrelic/security-agent](https://github.com/newrelic/csec-node-agent) ([v2.3.1](https://github.com/newrelic/csec-node-agent/tree/v2.3.1)), distributed under the [UNKNOWN License](https://github.com/newrelic/csec-node-agent/blob/v2.3.1/LICENSE):
512
+ This product includes source derived from [@newrelic/security-agent](https://github.com/newrelic/csec-node-agent) ([v2.4.0](https://github.com/newrelic/csec-node-agent/tree/v2.4.0)), distributed under the [UNKNOWN License](https://github.com/newrelic/csec-node-agent/blob/v2.4.0/LICENSE):
513
513
 
514
514
  ```
515
515
  ## New Relic Software License v1.0
@@ -1271,7 +1271,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1271
1271
 
1272
1272
  ### import-in-the-middle
1273
1273
 
1274
- This product includes source derived from [import-in-the-middle](https://github.com/nodejs/import-in-the-middle) ([v1.13.0](https://github.com/nodejs/import-in-the-middle/tree/v1.13.0)), distributed under the [Apache-2.0 License](https://github.com/nodejs/import-in-the-middle/blob/v1.13.0/LICENSE):
1274
+ This product includes source derived from [import-in-the-middle](https://github.com/nodejs/import-in-the-middle) ([v1.13.1](https://github.com/nodejs/import-in-the-middle/tree/v1.13.1)), distributed under the [Apache-2.0 License](https://github.com/nodejs/import-in-the-middle/blob/v1.13.1/LICENSE):
1275
1275
 
1276
1276
  ```
1277
1277
  Apache License
@@ -1615,7 +1615,7 @@ IN THE SOFTWARE.
1615
1615
 
1616
1616
  ### require-in-the-middle
1617
1617
 
1618
- 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):
1618
+ This product includes source derived from [require-in-the-middle](https://github.com/elastic/require-in-the-middle) ([v7.5.0](https://github.com/elastic/require-in-the-middle/tree/v7.5.0)), distributed under the [MIT License](https://github.com/elastic/require-in-the-middle/blob/v7.5.0/LICENSE):
1619
1619
 
1620
1620
  ```
1621
1621
  The MIT License (MIT)
@@ -1701,7 +1701,7 @@ SOFTWARE.
1701
1701
 
1702
1702
  ### @aws-sdk/client-s3
1703
1703
 
1704
- This product includes source derived from [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3) ([v3.714.0](https://github.com/aws/aws-sdk-js-v3/tree/v3.714.0)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.714.0/LICENSE):
1704
+ This product includes source derived from [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3) ([v3.726.1](https://github.com/aws/aws-sdk-js-v3/tree/v3.726.1)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.726.1/LICENSE):
1705
1705
 
1706
1706
  ```
1707
1707
  Apache License
@@ -1910,7 +1910,7 @@ This product includes source derived from [@aws-sdk/client-s3](https://github.co
1910
1910
 
1911
1911
  ### @aws-sdk/s3-request-presigner
1912
1912
 
1913
- This product includes source derived from [@aws-sdk/s3-request-presigner](https://github.com/aws/aws-sdk-js-v3) ([v3.714.0](https://github.com/aws/aws-sdk-js-v3/tree/v3.714.0)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.714.0/LICENSE):
1913
+ This product includes source derived from [@aws-sdk/s3-request-presigner](https://github.com/aws/aws-sdk-js-v3) ([v3.726.1](https://github.com/aws/aws-sdk-js-v3/tree/v3.726.1)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.726.1/LICENSE):
1914
1914
 
1915
1915
  ```
1916
1916
  Apache License
@@ -3770,7 +3770,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3770
3770
 
3771
3771
  ### eslint
3772
3772
 
3773
- This product includes source derived from [eslint](https://github.com/eslint/eslint) ([v9.17.0](https://github.com/eslint/eslint/tree/v9.17.0)), distributed under the [MIT License](https://github.com/eslint/eslint/blob/v9.17.0/LICENSE):
3773
+ This product includes source derived from [eslint](https://github.com/eslint/eslint) ([v9.18.0](https://github.com/eslint/eslint/tree/v9.18.0)), distributed under the [MIT License](https://github.com/eslint/eslint/blob/v9.18.0/LICENSE):
3774
3774
 
3775
3775
  ```
3776
3776
  Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
@@ -1197,7 +1197,14 @@ defaultConfig.definition = () => ({
1197
1197
  port: {
1198
1198
  formatter: int,
1199
1199
  default: 443
1200
- }
1200
+ },
1201
+ /**
1202
+ * For testing only. This allows the connection to an insecure gRPC server.
1203
+ */
1204
+ insecure: {
1205
+ formatter: boolean,
1206
+ default: false
1207
+ },
1201
1208
  },
1202
1209
  span_events: {
1203
1210
  /**
@@ -1206,6 +1213,13 @@ defaultConfig.definition = () => ({
1206
1213
  queue_size: {
1207
1214
  formatter: int,
1208
1215
  default: 10000
1216
+ },
1217
+ /**
1218
+ * Size of batches to post to 8T server
1219
+ */
1220
+ batch_size: {
1221
+ formatter: int,
1222
+ default: 750
1209
1223
  }
1210
1224
  },
1211
1225
  batching: {
@@ -62,6 +62,7 @@ class GrpcConnection extends EventEmitter {
62
62
 
63
63
  const traceObserverConfig = infiniteTracingConfig.trace_observer
64
64
  this._endpoint = this.getTraceObserverEndpoint(traceObserverConfig)
65
+ this._insecureConnection = traceObserverConfig.insecure
65
66
 
66
67
  this._client = null
67
68
  this.stream = null
@@ -202,11 +203,13 @@ class GrpcConnection extends EventEmitter {
202
203
 
203
204
  _disconnect() {
204
205
  logger.trace('Disconnecting from gRPC endpoint.')
206
+ this._setState(connectionStates.disconnected)
205
207
 
206
208
  if (this.stream) {
207
209
  this.stream.removeAllListeners()
208
210
 
209
211
  const oldStream = this.stream
212
+
210
213
  this.stream.on('status', function endStreamStatusHandler(grpcStatus) {
211
214
  logger.trace('End stream status received [%s]: %s', grpcStatus.code, grpcStatus.details)
212
215
 
@@ -228,8 +231,6 @@ class GrpcConnection extends EventEmitter {
228
231
 
229
232
  this.stream = null
230
233
  }
231
-
232
- this._setState(connectionStates.disconnected)
233
234
  }
234
235
 
235
236
  /**
@@ -374,12 +375,10 @@ class GrpcConnection extends EventEmitter {
374
375
 
375
376
  const traceApi = protoDescriptor.com.newrelic.trace.v1
376
377
 
377
- const credentials = this._generateCredentials(grpc)
378
-
379
- // If you want to use mock server use insecure creds
380
- // const credentials = grpc.credentials.createInsecure()
378
+ const credentials = this._insecureConnection ? grpc.credentials.createInsecure() : this._generateCredentials(grpc)
381
379
 
382
380
  const opts = {}
381
+
383
382
  if (this._compression) {
384
383
  // 2 = gzip compression
385
384
  // see: https://github.com/grpc/grpc-node/blob/master/packages/grpc-js/src/compression-algorithms.ts#L21
@@ -393,6 +392,27 @@ class GrpcConnection extends EventEmitter {
393
392
  .incrementCallCount()
394
393
  }
395
394
 
395
+ // this defines retries of writes to stream if it fails with `UNAVAILABLE` or `INTERNAL`
396
+ const serviceConfig = {
397
+ methodConfig: [
398
+ {
399
+ name: [
400
+ {
401
+ service: 'com.newrelic.trace.v1.IngestService'
402
+ }
403
+ ],
404
+ retryPolicy: {
405
+ maxAttempts: 10,
406
+ initialBackoff: '0.1s',
407
+ maxBackoff: '1s',
408
+ backoffMultiplier: 2,
409
+ retryableStatusCodes: ['UNAVAILABLE', 'INTERNAL']
410
+ }
411
+ }
412
+ ]
413
+ }
414
+ opts['grpc.service_config'] = JSON.stringify(serviceConfig)
415
+
396
416
  return new traceApi.IngestService(endpoint, credentials, opts)
397
417
  }
398
418
  }
@@ -0,0 +1,311 @@
1
+ /*
2
+ * Copyright 2025 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+
8
+ const defaultLogger = require('../../logger').child({ component: 'azure-functions' })
9
+ const urltils = require('../../util/urltils')
10
+ const headerProcessing = require('../../header-processing')
11
+ const synthetics = require('../../synthetics')
12
+
13
+ const backgroundRecorder = require('../../metrics/recorders/other.js')
14
+ const recordWeb = require('../../metrics/recorders/http')
15
+
16
+ const {
17
+ DESTINATIONS: DESTS,
18
+ TYPES
19
+ } = require('../../transaction/index.js')
20
+
21
+ const {
22
+ WEBSITE_OWNER_NAME,
23
+ WEBSITE_RESOURCE_GROUP,
24
+ WEBSITE_SITE_NAME
25
+ } = process.env
26
+ const SUBSCRIPTION_ID = WEBSITE_OWNER_NAME?.split('+').shift()
27
+ const RESOURCE_GROUP_NAME = WEBSITE_RESOURCE_GROUP ?? WEBSITE_OWNER_NAME?.split('+').pop().split('-Linux').shift()
28
+ const AZURE_FUNCTION_APP_NAME = WEBSITE_SITE_NAME
29
+
30
+ let coldStart = true
31
+ let _agent
32
+ let _logger
33
+
34
+ module.exports = function initialize(agent, azureFunctions, _moduleName, shim, { logger = defaultLogger } = {}) {
35
+ _agent = agent
36
+ _logger = logger
37
+ if (!SUBSCRIPTION_ID || !RESOURCE_GROUP_NAME || !AZURE_FUNCTION_APP_NAME) {
38
+ logger.warn(
39
+ {
40
+ data: {
41
+ expectedVars: ['WEBSITE_OWNER_NAME', 'WEBSITE_RESOURCE_GROUP', 'WEBSITE_SITE_NAME'],
42
+ found: { WEBSITE_OWNER_NAME, WEBSITE_RESOURCE_GROUP, WEBSITE_SITE_NAME }
43
+ },
44
+ },
45
+ 'could not initialize azure functions instrumentation due to missing environment variables'
46
+ )
47
+ return
48
+ }
49
+
50
+ const httpMethods = ['http', 'get', 'put', 'post', 'patch', 'deleteRequest']
51
+ shim.wrap(azureFunctions.app, httpMethods, wrapAzureHttpMethods)
52
+
53
+ const backgroundMethods = [
54
+ 'cosmosDB',
55
+ 'eventGrid',
56
+ 'eventHub',
57
+ 'mySql',
58
+ 'serviceBusQueue',
59
+ 'serviceBusTopic',
60
+ 'sql',
61
+ 'storageBlob',
62
+ 'storageQueue',
63
+ 'timer',
64
+ 'warmup',
65
+ 'webPubSub'
66
+ ]
67
+ shim.wrap(azureFunctions.app, backgroundMethods, wrapAzureBackgroundMethods)
68
+ }
69
+
70
+ function wrapAzureHttpMethods(shim, appMethod) {
71
+ return async function wrappedAzureHttpMethod(...args) {
72
+ // If the app doesn't need an options object, the user can pass the
73
+ // handler function as the second argument
74
+ // (e.g. `app.get('name', handler)`).
75
+ // See https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node?tabs=javascript%2Cwindows%2Cazure-cli&pivots=nodejs-model-v4#registering-a-function
76
+ let handler
77
+ if (typeof args[1] === 'function') {
78
+ handler = args[1]
79
+ args[1] = { handler }
80
+ } else {
81
+ handler = args[1].handler
82
+ }
83
+
84
+ const tracer = shim.tracer
85
+ args[1].handler = tracer.transactionProxy(async function wrappedHandler(...args) {
86
+ const [request, context] = args
87
+ const ctx = tracer.getContext()
88
+ const tx = tracer.getTransaction()
89
+
90
+ // Set the transaction name according to our spec (category + function name).
91
+ tx.setPartialName(`AzureFunction/${context.functionName}`)
92
+
93
+ const url = new URL(request.url)
94
+ const segment = tracer.createSegment({
95
+ name: request.url,
96
+ recorder: recordWeb,
97
+ parent: ctx.segment,
98
+ transaction: tx
99
+ })
100
+ segment.start()
101
+
102
+ const transport = url.protocol === 'https:' ? 'HTTPS' : 'HTTP'
103
+ tx.type = TYPES.WEB
104
+ tx.baseSegment = segment
105
+ tx.parsedUrl = url
106
+ tx.url = urltils.obfuscatePath(_agent.config, url.pathname)
107
+ tx.verb = request.method
108
+ if (url.port === '') {
109
+ tx.port = transport === 'HTTPS' ? '443' : '80'
110
+ } else {
111
+ tx.port = url.port
112
+ }
113
+
114
+ tx.trace.attributes.addAttribute(
115
+ DESTS.TRANS_EVENT | DESTS.ERROR_EVENT,
116
+ 'request.uri',
117
+ tx.url
118
+ )
119
+ segment.addSpanAttribute('request.uri', tx.url)
120
+ if (request.method != null) {
121
+ segment.addSpanAttribute('request.method', request.method)
122
+ }
123
+ addAttributes({ transaction: tx, functionContext: context })
124
+
125
+ const queueTimeStamp = headerProcessing.getQueueTime(_logger, request.headers)
126
+ if (queueTimeStamp) {
127
+ tx.queueTime = Date.now() - queueTimeStamp
128
+ }
129
+
130
+ synthetics.assignHeadersToTransaction(_agent.config, tx, request.headers)
131
+ if (_agent.config.distributed_tracing.enabled === true) {
132
+ tx.acceptDistributedTraceHeaders(transport, request.headers)
133
+ }
134
+
135
+ const newContext = ctx.enterSegment({ segment })
136
+ const boundHandler = tracer.bindFunction(handler, newContext)
137
+
138
+ const result = await boundHandler(...args)
139
+ // Responses should have a shape as described at:
140
+ // https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node?tabs=javascript%2Cwindows%2Cazure-cli&pivots=nodejs-model-v4#http-response
141
+ if (result.status) {
142
+ tx.trace.attributes.addAttribute(
143
+ DESTS.TRANS_COMMON,
144
+ 'http.statusCode',
145
+ result.status
146
+ )
147
+ }
148
+
149
+ if (coldStart === true) {
150
+ tx.trace.attributes.addAttribute(DESTS.TRANS_COMMON, 'faas.coldStart', true)
151
+ coldStart = false
152
+ }
153
+
154
+ tx.end()
155
+ return result
156
+ })
157
+
158
+ await appMethod(...args)
159
+ }
160
+ }
161
+
162
+ function wrapAzureBackgroundMethods(shim, appMethod) {
163
+ return async function wrappedAzureBackgroundMethod(...args) {
164
+ let handler
165
+ if (typeof args[1] === 'function') {
166
+ handler = args[1]
167
+ args[1] = { handler }
168
+ } else {
169
+ handler = args[1].handler
170
+ }
171
+
172
+ const tracer = shim.tracer
173
+ args[1].handler = tracer.transactionProxy(async function wrappedHandler(...args) {
174
+ const [, context] = args
175
+ const ctx = tracer.getContext()
176
+ const tx = tracer.getTransaction()
177
+
178
+ tx.setPartialName(`AzureFunction/${context.functionName}`)
179
+
180
+ const segment = tracer.createSegment({
181
+ name: `${appMethod}-trigger`,
182
+ recorder: backgroundRecorder,
183
+ parent: ctx.segment,
184
+ transaction: tx
185
+ })
186
+ segment.start()
187
+
188
+ tx.type = TYPES.BG
189
+ tx.baseSegment = segment
190
+ addAttributes({ transaction: tx, functionContext: context })
191
+
192
+ const newContext = ctx.enterSegment({ segment })
193
+ const boundHandler = tracer.bindFunction(handler, newContext)
194
+
195
+ const result = await boundHandler(...args)
196
+ if (coldStart === true) {
197
+ tx.trace.attributes.addAttribute(DESTS.TRANS_COMMON, 'faas.coldStart', true)
198
+ coldStart = false
199
+ }
200
+
201
+ tx.end()
202
+ return result
203
+ })
204
+
205
+ await appMethod(...args)
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Add required New Relic attributes to the transaction.
211
+ *
212
+ * @param {object} params Function parameters.
213
+ * @param {Transaction} params.transaction The transaction to update.
214
+ * @param {object} params.functionContext The function invocation context
215
+ * provided by the Azure functions runtime.
216
+ */
217
+ function addAttributes({ transaction, functionContext }) {
218
+ transaction.trace.attributes.addAttribute(
219
+ DESTS.TRANS_COMMON,
220
+ 'faas.invocation_id',
221
+ functionContext.invocationId ?? 'unknown'
222
+ )
223
+ transaction.trace.attributes.addAttribute(
224
+ DESTS.TRANS_COMMON,
225
+ 'faas.name',
226
+ functionContext.functionName ?? 'unknown'
227
+ )
228
+ transaction.trace.attributes.addAttribute(
229
+ DESTS.TRANS_COMMON,
230
+ 'faas.trigger',
231
+ mapTriggerType({ functionContext })
232
+ )
233
+ transaction.trace.attributes.addAttribute(
234
+ DESTS.TRANS_COMMON,
235
+ 'cloud.resource_id',
236
+ buildCloudResourceId({ functionContext })
237
+ )
238
+ }
239
+
240
+ /**
241
+ * Inspects the provided function invocation context and returns a recognized
242
+ * trigger type suitable for sending to the collector as `faas.trigger`.
243
+ *
244
+ * @param {object} params Function parameters
245
+ * @param {object} params.functionContext The function context as provided by
246
+ * the Azure functions runtime.
247
+ *
248
+ * @returns {string} A string appropriate for New Relic.
249
+ */
250
+ function mapTriggerType({ functionContext }) {
251
+ const input = functionContext.options?.trigger?.type
252
+
253
+ // Input types are found at:
254
+ // https://github.com/Azure/azure-functions-nodejs-library/blob/138c021/src/trigger.ts
255
+ // https://learn.microsoft.com/en-us/azure/azure-functions/functions-triggers-bindings?tabs=isolated-process%2Cnode-v4%2Cpython-v2&pivots=programming-language-javascript#supported-bindings
256
+ switch (input) {
257
+ case 'httpTrigger': {
258
+ return 'http'
259
+ }
260
+
261
+ case 'timerTrigger': {
262
+ return 'timer'
263
+ }
264
+
265
+ case 'blobTrigger':
266
+ case 'cosmosDBTrigger':
267
+ case 'daprBindingTrigger':
268
+ case 'mysqlTrigger':
269
+ case 'queueTrigger':
270
+ case 'sqlTrigger': {
271
+ return 'datasource'
272
+ }
273
+
274
+ case 'daprTopicTrigger':
275
+ case 'eventGridTrigger':
276
+ case 'eventHubTrigger':
277
+ case 'kafkaTrigger':
278
+ case 'rabbitMQTrigger':
279
+ case 'redisListTrigger':
280
+ case 'redisPubSubTrigger':
281
+ case 'redisStreamTrigger':
282
+ case 'serviceBusTrigger':
283
+ case 'signalRTrigger':
284
+ case 'webPubSubTrigger': {
285
+ return 'pubsub'
286
+ }
287
+
288
+ default: {
289
+ return 'other'
290
+ }
291
+ }
292
+ }
293
+
294
+ function buildCloudResourceId({ functionContext }) {
295
+ return [
296
+ '/subscriptions/',
297
+ SUBSCRIPTION_ID,
298
+ '/resourceGroups/',
299
+ RESOURCE_GROUP_NAME,
300
+ '/providers/Microsoft.Web/sites/',
301
+ AZURE_FUNCTION_APP_NAME,
302
+ '/functions/',
303
+ functionContext.functionName
304
+ ].join('')
305
+ }
306
+
307
+ module.exports.internals = {
308
+ addAttributes,
309
+ mapTriggerType,
310
+ buildCloudResourceId
311
+ }
@@ -10,6 +10,7 @@ const InstrumentationDescriptor = require('./instrumentation-descriptor')
10
10
  // Return a new copy of this array every time we're called
11
11
  module.exports = function instrumentations() {
12
12
  return {
13
+ '@azure/functions': { type: InstrumentationDescriptor.TYPE_GENERIC },
13
14
  '@elastic/elasticsearch': { type: InstrumentationDescriptor.TYPE_DATASTORE },
14
15
  '@opensearch-project/opensearch': { type: InstrumentationDescriptor.TYPE_DATASTORE },
15
16
  '@grpc/grpc-js': { module: './instrumentation/grpc-js' },