newrelic 13.2.0 → 13.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.
Files changed (59) hide show
  1. package/NEWS.md +44 -0
  2. package/THIRD_PARTY_NOTICES.md +219 -8
  3. package/api.js +1 -1
  4. package/esm-loader.mjs +6 -1
  5. package/lib/instrumentation/@azure/functions.js +8 -46
  6. package/lib/instrumentation/core/http-outbound.js +26 -10
  7. package/lib/instrumentation/core/http.js +20 -118
  8. package/lib/instrumentations.js +0 -14
  9. package/lib/otel/logs/index.js +80 -0
  10. package/lib/otel/metrics/index.js +119 -0
  11. package/lib/otel/setup-signal.js +37 -0
  12. package/lib/otel/setup.js +15 -25
  13. package/lib/otel/{attr-reconciler.js → traces/attr-reconciler.js} +2 -2
  14. package/lib/otel/{exception-mapping.js → traces/exception-mapping.js} +1 -1
  15. package/lib/otel/traces/index.js +40 -0
  16. package/lib/otel/{rules.js → traces/rules.js} +1 -1
  17. package/lib/otel/{segment-synthesis.js → traces/segment-synthesis.js} +5 -4
  18. package/lib/otel/{segments → traces/segments}/consumer.js +4 -4
  19. package/lib/otel/{segments → traces/segments}/database.js +6 -6
  20. package/lib/otel/{segments → traces/segments}/http-external.js +11 -8
  21. package/lib/otel/traces/segments/index.js +22 -0
  22. package/lib/otel/{segments → traces/segments}/internal.js +1 -1
  23. package/lib/otel/{segments → traces/segments}/producer.js +2 -2
  24. package/lib/otel/{segments → traces/segments}/server.js +4 -4
  25. package/lib/otel/{segments → traces/segments}/utils.js +1 -1
  26. package/lib/otel/{span-processor.js → traces/span-processor.js} +13 -19
  27. package/lib/otel/{transformation-rules.json → traces/transformation-rules.json} +3 -2
  28. package/lib/otel/{utils.js → traces/utils.js} +2 -1
  29. package/lib/serverless/aws-lambda.js +7 -68
  30. package/lib/shim/webframework-shim/middleware.js +1 -1
  31. package/lib/shimmer.js +2 -2
  32. package/lib/subscriber-configs.js +2 -1
  33. package/lib/subscribers/base.js +30 -0
  34. package/lib/subscribers/elasticsearch/config.js +1 -0
  35. package/lib/subscribers/mcp-sdk/client-request.js +66 -0
  36. package/lib/subscribers/mcp-sdk/config.js +13 -75
  37. package/lib/subscribers/openai/base.js +21 -0
  38. package/lib/subscribers/openai/chat-responses.js +15 -0
  39. package/lib/subscribers/openai/chat.js +85 -0
  40. package/lib/subscribers/openai/client.js +31 -0
  41. package/lib/subscribers/openai/config.js +85 -0
  42. package/lib/subscribers/openai/embeddings.js +54 -0
  43. package/lib/subscribers/openai/utils.js +349 -0
  44. package/lib/transaction/index.js +157 -56
  45. package/lib/transaction/trace/segment.js +5 -2
  46. package/lib/util/urltils.js +158 -192
  47. package/lib/utilization/common.js +0 -6
  48. package/package.json +3 -4
  49. package/lib/instrumentation/openai.js +0 -482
  50. package/lib/otel/logs/bootstrap-logs.js +0 -84
  51. package/lib/otel/metrics/bootstrap-metrics.js +0 -117
  52. package/lib/otel/segments/index.js +0 -22
  53. package/lib/patch-module.js +0 -70
  54. package/lib/subscribers/mcp-sdk/client-prompt.js +0 -23
  55. package/lib/subscribers/mcp-sdk/client-resource.js +0 -24
  56. package/lib/subscribers/mcp-sdk/client-tool.js +0 -23
  57. package/lib/subscribers/mcp-sdk/client.js +0 -29
  58. /package/lib/otel/{constants.js → traces/constants.js} +0 -0
  59. /package/lib/otel/{sampler.js → traces/sampler.js} +0 -0
@@ -16,8 +16,11 @@ const Timer = require('../timer')
16
16
  const Trace = require('./trace')
17
17
  const synthetics = require('../synthetics')
18
18
  const urltils = require('../util/urltils')
19
+ const cat = require('../util/cat')
19
20
  const TraceContext = require('./tracecontext').TraceContext
20
21
  const Logs = require('./logs')
22
+ const headerAttributes = require('../header-attributes')
23
+ const headerProcessing = require('../header-processing')
21
24
  const DT_ACCEPT_PAYLOAD_EXCEPTION_METRIC = 'DistributedTrace/AcceptPayload/Exception'
22
25
  const DT_ACCEPT_PAYLOAD_PARSE_EXCEPTION_METRIC = 'DistributedTrace/AcceptPayload/ParseException'
23
26
  const REQUEST_PARAMS_PATH = 'request.parameters.'
@@ -119,7 +122,6 @@ function Transaction(agent, traceId) {
119
122
  this.syntheticsData = null
120
123
  this.syntheticsInfoData = null
121
124
  this.url = null
122
- this.parsedUrl = null
123
125
  this.verb = null
124
126
  this.baseSegment = null
125
127
  this.type = TYPES.WEB
@@ -317,8 +319,9 @@ Transaction.prototype.setPartialName = function setPartialName(name) {
317
319
  * boolean flag in `ignore`.
318
320
  */
319
321
  Transaction.prototype._partialNameFromUri = _partialNameFromUri
320
- function _partialNameFromUri(requestUrl, status) {
321
- const scrubbedUrl = urltils.scrub(requestUrl)
322
+ function _partialNameFromUri() {
323
+ const scrubbedUrl = this.url
324
+ const status = this.statusCode
322
325
 
323
326
  // 0. If there is a name in the name-state stack, use it.
324
327
  let partialName = this._partialName
@@ -386,21 +389,69 @@ Transaction.prototype.isIgnored = function getIgnore() {
386
389
  }
387
390
 
388
391
  /**
389
- * Derives the transaction's name from the given URL and status code.
392
+ * Finalize a web transaction. This includes naming the transaction, collecting
393
+ * response headers, and ending the transaction and its base segment.
394
+ *
395
+ * @param {object} params The parameters for finalizing the transaction.
396
+ * @param {number} params.statusCode The response status code.
397
+ * @param {string} [params.statusMessage] The response status message.
398
+ * @param {object} [params.headers] The response headers.
399
+ * @param {Boolean} params.end Whether to end the transaction and baseSegment.
400
+ */
401
+ Transaction.prototype.finalizeWeb = function finalizeWeb({ statusCode, statusMessage, headers, end }) {
402
+ // Naming must happen before the segment and transaction are ended
403
+ // because metrics recording depends on naming's side effects.
404
+ this.finalizeNameFromWeb(statusCode)
405
+ if (statusCode != null) {
406
+ const responseCode = String(statusCode)
407
+
408
+ if (/^\d+$/.test(responseCode)) {
409
+ this.trace.attributes.addAttribute(
410
+ DESTS.TRANS_COMMON,
411
+ 'http.statusCode',
412
+ responseCode
413
+ )
414
+
415
+ this.baseSegment.addSpanAttribute('http.statusCode', responseCode)
416
+ }
417
+ }
418
+
419
+ if (statusMessage !== undefined) {
420
+ this.trace.attributes.addAttribute(
421
+ DESTS.TRANS_COMMON,
422
+ 'http.statusText',
423
+ statusMessage
424
+ )
425
+
426
+ this.baseSegment.addSpanAttribute('http.statusText', statusMessage)
427
+ }
428
+
429
+ if (headers) {
430
+ headerAttributes.collectResponseHeaders(headers, this)
431
+ }
432
+
433
+ if (end) {
434
+ // And we are done! End the segment and transaction.
435
+ this.baseSegment.end()
436
+ this.end()
437
+ }
438
+ }
439
+
440
+ /**
441
+ * Derives the transaction's name from `transaction.url` and status code.
390
442
  *
391
443
  * The transaction's name will be set after this as well as its ignored status
392
444
  * based on the derived name.
393
445
  *
394
- * @param {string} requestURL - The URL to derive the request's name and status from.
395
446
  * @param {number} statusCode - The response status code.
396
447
  */
397
- Transaction.prototype.finalizeNameFromUri = finalizeNameFromUri
448
+ Transaction.prototype.finalizeNameFromWeb = finalizeNameFromWeb
398
449
 
399
- function finalizeNameFromUri(requestURL, statusCode) {
450
+ function finalizeNameFromWeb(statusCode) {
400
451
  if (logger.traceEnabled()) {
401
452
  logger.trace(
402
453
  {
403
- requestURL,
454
+ requestURL: this.url,
404
455
  statusCode,
405
456
  transactionId: this.id,
406
457
  transactionName: this.name
@@ -409,10 +460,6 @@ function finalizeNameFromUri(requestURL, statusCode) {
409
460
  )
410
461
  }
411
462
 
412
- if (!this.agent.config.url_obfuscation.enabled) {
413
- this.url = urltils.scrub(requestURL)
414
- }
415
-
416
463
  this.statusCode = statusCode
417
464
  this.name = this.getFullName()
418
465
 
@@ -426,7 +473,16 @@ function finalizeNameFromUri(requestURL, statusCode) {
426
473
  this.ignore = this.forceIgnore
427
474
  }
428
475
 
429
- this.baseSegment && this._markAsWeb(requestURL)
476
+ const obfuscatedUrl = urltils.obfuscatePath(this.agent.config, this.url)
477
+ this.url = obfuscatedUrl
478
+ // URL is sent as an agent attribute with transaction events
479
+ this.trace.attributes.addAttribute(
480
+ DESTS.TRANS_EVENT | DESTS.ERROR_EVENT,
481
+ 'request.uri',
482
+ obfuscatedUrl
483
+ )
484
+
485
+ this?.baseSegment?.markAsWeb(this, obfuscatedUrl)
430
486
 
431
487
  this._copyNameToActiveSpan(this.name)
432
488
 
@@ -471,37 +527,6 @@ Transaction.prototype._copyNameToActiveSpan = function _copyNameToActiveSpan(nam
471
527
  spanContext.addIntrinsicAttribute('transaction.name', name)
472
528
  }
473
529
 
474
- /**
475
- * Copies final base segment parameters to trace attributes before reapplying
476
- * them to the segment.
477
- *
478
- * Handles adding query parameters to `request.parameter.*` attributes
479
- *
480
- * @param {string} rawURL The URL, as it came in, for parameter extraction.
481
- */
482
- Transaction.prototype._markAsWeb = function _markAsWeb(rawURL) {
483
- // Because we are assured we have the URL here, lets grab query params.
484
- const params = urltils.parseParameters(rawURL)
485
- for (const key in params) {
486
- if (props.hasOwn(params, key)) {
487
- this.trace.attributes.addAttribute(DESTS.NONE, REQUEST_PARAMS_PATH + key, params[key])
488
-
489
- const segment = this.agent.tracer.getSegment()
490
-
491
- if (!segment) {
492
- logger.trace(
493
- 'Active segment not available, not adding request.parameters span attribute for %s',
494
- key
495
- )
496
- } else {
497
- segment.attributes.addAttribute(DESTS.NONE, REQUEST_PARAMS_PATH + key, params[key])
498
- }
499
- }
500
- }
501
-
502
- this.baseSegment.markAsWeb(this)
503
- }
504
-
505
530
  /**
506
531
  * Sets the transaction's name and determines if it will be ignored.
507
532
  *
@@ -514,7 +539,7 @@ Transaction.prototype.finalizeName = function finalizeName(name) {
514
539
  // If no name is given, and this is a web transaction with a url, then
515
540
  // finalize the name using the stored url.
516
541
  if (name == null && this.isWeb() && this.url) {
517
- return this.finalizeNameFromUri(this.url, this.statusCode)
542
+ return this.finalizeNameFromWeb(this.statusCode)
518
543
  }
519
544
 
520
545
  // this may seem out of place but certain API methods
@@ -566,7 +591,7 @@ Transaction.prototype.finalizeName = function finalizeName(name) {
566
591
  */
567
592
  Transaction.prototype.getName = function getName() {
568
593
  if (this.isWeb() && this.url) {
569
- const finalName = this._partialNameFromUri(this.url, this.statusCode)
594
+ const finalName = this._partialNameFromUri()
570
595
  if (finalName.ignore) {
571
596
  this.ignore = true
572
597
  }
@@ -1427,18 +1452,16 @@ Transaction.prototype.addRequestParameters = addRequestParameters
1427
1452
  * @param {Object<string, string>} requestParameters of the request object
1428
1453
  */
1429
1454
  function addRequestParameters(requestParameters) {
1430
- for (const key in requestParameters) {
1431
- if (props.hasOwn(requestParameters, key)) {
1432
- this.trace.attributes.addAttribute(
1433
- DESTS.NONE,
1434
- REQUEST_PARAMS_PATH + key,
1435
- requestParameters[key]
1436
- )
1455
+ for (const [key, value] of Object.entries(requestParameters)) {
1456
+ this.trace.attributes.addAttribute(
1457
+ DESTS.NONE,
1458
+ REQUEST_PARAMS_PATH + key,
1459
+ value
1460
+ )
1437
1461
 
1438
- const segment = this.baseSegment
1462
+ const segment = this.baseSegment
1439
1463
 
1440
- segment.attributes.addAttribute(DESTS.NONE, REQUEST_PARAMS_PATH + key, requestParameters[key])
1441
- }
1464
+ segment.attributes.addAttribute(DESTS.NONE, REQUEST_PARAMS_PATH + key, value)
1442
1465
  }
1443
1466
  }
1444
1467
 
@@ -1453,4 +1476,82 @@ Transaction.prototype.incrementCounters = function incrementCounters() {
1453
1476
  this.agent.incrementCounters()
1454
1477
  }
1455
1478
 
1479
+ /**
1480
+ * Initializes a web transaction with request information:
1481
+ * Assigns type(web), verb, url, port attributes on transaction.
1482
+ * Collects request headers as attributes on the transaction.
1483
+ * Applies user naming rules to the transaction.
1484
+ * Calculates queue time if possible.
1485
+ * Assigns synthetics information if present.
1486
+ * Adds distributed trace or cat headers if present.
1487
+ *
1488
+ * @param {object} params - Parameters to initialize the web transaction.
1489
+ * @param {string} params.absoluteUrl - Full URL of the request.
1490
+ * @param {string} params.method - HTTP method of the request.
1491
+ * @param {number} params.port - Port of the request.
1492
+ * @param {object} [params.headers] - HTTP headers of the request.
1493
+ * @param {string} [params.transport] - Transport type that delivered the request.
1494
+ */
1495
+ Transaction.prototype.initializeWeb = function initializeWeb({ absoluteUrl, method, port, headers = {}, transport }) {
1496
+ this.type = TYPES.WEB
1497
+ headerAttributes.collectRequestHeaders(headers, this)
1498
+
1499
+ if (method != null) {
1500
+ this.trace.attributes.addAttribute(DESTS.TRANS_COMMON, 'request.method', method)
1501
+ this.baseSegment.addSpanAttribute('request.method', method)
1502
+ this.verb = method
1503
+ }
1504
+
1505
+ this.port = port
1506
+
1507
+ // the error tracer needs a URL for tracing, even though naming overwrites
1508
+ try {
1509
+ const parsedUrl = new URL(absoluteUrl)
1510
+ const data = urltils.scrubAndParseParameters(parsedUrl)
1511
+ this.url = data.path
1512
+ this.addRequestParameters(data.parameters)
1513
+ } catch (err) {
1514
+ logger.debug('Could not parse URL %s: %s', absoluteUrl, err.message)
1515
+ this.url = '/unknown'
1516
+ }
1517
+
1518
+ // need to set any config-driven names early for RUM
1519
+ logger.trace({ url: this.url, transaction: this.id },
1520
+ 'Appplying user naming rules for RUM.')
1521
+
1522
+ this.applyUserNamingRules(this.url)
1523
+
1524
+ const queueTimeStamp = headerProcessing.getQueueTime(logger, headers)
1525
+ if (queueTimeStamp) {
1526
+ this.queueTime = Date.now() - queueTimeStamp
1527
+ }
1528
+
1529
+ synthetics.assignHeadersToTransaction(this.agent.config, this, headers)
1530
+ this.addDtCatHeaders({ headers, transport })
1531
+ }
1532
+
1533
+ /**
1534
+ * Handles accepting upstream DT headers(traceparent/tracestate or newrelic) if present and DT is enabled.
1535
+ * If CAT is enabled, handles accepting CAT headers and assigns to transaction.
1536
+ *
1537
+ * @param {object} params - Parameters for adding DT or CAT headers.
1538
+ * @param {object} params.headers - HTTP headers of the request(must be lower-cased, by default http instrumentation has headers lower case, other libraries must pass this in lowercased).
1539
+ * @param {string} [params.transport] - Transport type that delivered the request.
1540
+ */
1541
+ Transaction.prototype.addDtCatHeaders = function addDtCatHeaders({ headers, transport }) {
1542
+ if (this.agent.config.distributed_tracing.enabled) {
1543
+ // Node http request headers are automatically lowercase
1544
+ // need to pass in lower case for other instrumentation
1545
+ this.acceptDistributedTraceHeaders(transport, headers)
1546
+ } else if (this.agent.config.cross_application_tracer.enabled) {
1547
+ const { id, transactionId } = cat.extractCatHeaders(headers)
1548
+ const { externalId, externalTransaction } = cat.parseCatData(
1549
+ id,
1550
+ transactionId,
1551
+ this.agent.config.encoding_key
1552
+ )
1553
+ cat.assignCatToTransaction(externalId, externalTransaction, this)
1554
+ }
1555
+ }
1556
+
1456
1557
  module.exports = Transaction
@@ -133,13 +133,14 @@ TraceSegment.prototype.setNameFromTransaction = function setNameFromTransaction(
133
133
  /**
134
134
  * Once a transaction is named, the web segment also needs to be updated to
135
135
  * match it (which implies this method must be called subsequent to
136
- * transaction.finalizeNameFromUri). To properly name apdex metrics during metric
136
+ * transaction.finalizeNameFromWeb). To properly name apdex metrics during metric
137
137
  * recording, it's also necessary to copy the transaction's partial name. And
138
138
  * finally, marking the trace segment as being a web segment copies the
139
139
  * segment's parameters onto the transaction.
140
140
  * @param {Transaction} transaction The transaction to which this segment will be bound.
141
+ * @param obfuscatedUrl
141
142
  */
142
- TraceSegment.prototype.markAsWeb = function markAsWeb(transaction) {
143
+ TraceSegment.prototype.markAsWeb = function markAsWeb(transaction, obfuscatedUrl) {
143
144
  this.setNameFromTransaction(transaction)
144
145
 
145
146
  const traceAttrs = transaction.trace.attributes.get(DESTINATIONS.TRANS_TRACE)
@@ -148,6 +149,8 @@ TraceSegment.prototype.markAsWeb = function markAsWeb(transaction) {
148
149
  this.addAttribute(key, traceAttrs[key])
149
150
  }
150
151
  })
152
+
153
+ this.addSpanAttribute('request.uri', obfuscatedUrl)
151
154
  }
152
155
 
153
156
  /**