@pingops/otel 0.2.6 → 0.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/dist/index.cjs CHANGED
@@ -134,6 +134,7 @@ var PingopsSpanProcessor = class {
134
134
  });
135
135
  this.config = {
136
136
  debug: config.debug ?? false,
137
+ sdkVersion: config.sdkVersion,
137
138
  headersAllowList: config.headersAllowList,
138
139
  headersDenyList: config.headersDenyList,
139
140
  domainAllowList: config.domainAllowList,
@@ -171,6 +172,7 @@ var PingopsSpanProcessor = class {
171
172
  spanId: spanContext.spanId,
172
173
  traceId: spanContext.traceId
173
174
  });
175
+ if (this.config.sdkVersion) span.setAttribute("pingops.sdk.version", this.config.sdkVersion);
174
176
  const propagatedAttributes = (0, _pingops_core.getPropagatedAttributesFromContext)(parentContext);
175
177
  if (Object.keys(propagatedAttributes).length > 0) {
176
178
  for (const [key, value] of Object.entries(propagatedAttributes)) if (typeof value === "string" || Array.isArray(value)) span.setAttribute(key, value);
@@ -423,6 +425,7 @@ function shouldIgnoreOutboundInstrumentation(requestUrl) {
423
425
  */
424
426
  function resolveOutboundSpanParentContext(activeContext, requestUrl) {
425
427
  if (!(0, _opentelemetry_core.isTracingSuppressed)(activeContext)) return activeContext;
428
+ if (activeContext.getValue(_pingops_core.PINGOPS_INTENTIONAL_SUPPRESSION) === true) return activeContext;
426
429
  if (isExporterRequestUrl(requestUrl)) return activeContext;
427
430
  if (!hasLoggedSuppressionLeakWarning) {
428
431
  logger.warn("Detected suppressed context for outbound user request; running instrumentation on ROOT_CONTEXT to prevent Noop spans from suppression leakage");
@@ -431,18 +434,74 @@ function resolveOutboundSpanParentContext(activeContext, requestUrl) {
431
434
  return _opentelemetry_api.ROOT_CONTEXT;
432
435
  }
433
436
 
437
+ //#endregion
438
+ //#region src/instrumentations/body-utils.ts
439
+ const HTTP_REQUEST_BODY = "http.request.body";
440
+ const HTTP_RESPONSE_BODY = "http.response.body";
441
+ const HTTP_REQUEST_BODY_SIZE = "http.request.body.size";
442
+ const HTTP_RESPONSE_BODY_SIZE = "http.response.body.size";
443
+ const DEFAULT_MAX_REQUEST_BODY_SIZE = 10 * 1024;
444
+ const DEFAULT_MAX_RESPONSE_BODY_SIZE = 10 * 1024;
445
+ /**
446
+ * Gets domain rule configuration for a given URL.
447
+ */
448
+ function getDomainRule(url$1, domainAllowList) {
449
+ if (!domainAllowList) return;
450
+ const domain = (0, _pingops_core.extractDomainFromUrl)(url$1);
451
+ for (const rule of domainAllowList) if (domain === rule.domain || domain.endsWith(`.${rule.domain}`) || domain === rule.domain.slice(1)) return rule;
452
+ }
453
+ /**
454
+ * Determines if request body should be captured based on priority:
455
+ * context > domain rule > global config > default (false).
456
+ */
457
+ function shouldCaptureRequestBody(url$1) {
458
+ const contextValue = _opentelemetry_api.context.active().getValue(_pingops_core.PINGOPS_CAPTURE_REQUEST_BODY);
459
+ if (contextValue !== void 0) return contextValue;
460
+ if (url$1) {
461
+ const domainRule = getDomainRule(url$1, getGlobalConfig()?.domainAllowList);
462
+ if (domainRule?.captureRequestBody !== void 0) return domainRule.captureRequestBody;
463
+ }
464
+ const globalConfig$1 = getGlobalConfig();
465
+ if (globalConfig$1?.captureRequestBody !== void 0) return globalConfig$1.captureRequestBody;
466
+ return false;
467
+ }
468
+ /**
469
+ * Determines if response body should be captured based on priority:
470
+ * context > domain rule > global config > default (false).
471
+ */
472
+ function shouldCaptureResponseBody(url$1) {
473
+ const contextValue = _opentelemetry_api.context.active().getValue(_pingops_core.PINGOPS_CAPTURE_RESPONSE_BODY);
474
+ if (contextValue !== void 0) return contextValue;
475
+ if (url$1) {
476
+ const domainRule = getDomainRule(url$1, getGlobalConfig()?.domainAllowList);
477
+ if (domainRule?.captureResponseBody !== void 0) return domainRule.captureResponseBody;
478
+ }
479
+ const globalConfig$1 = getGlobalConfig();
480
+ if (globalConfig$1?.captureResponseBody !== void 0) return globalConfig$1.captureResponseBody;
481
+ return false;
482
+ }
483
+ /**
484
+ * Normalizes supported HTTP chunk types into a Buffer.
485
+ */
486
+ function toBufferChunk(data) {
487
+ if (typeof data === "string") return Buffer.from(data);
488
+ if (Buffer.isBuffer(data)) return data;
489
+ if (data instanceof Uint8Array) return Buffer.from(data);
490
+ return null;
491
+ }
492
+
434
493
  //#endregion
435
494
  //#region src/instrumentations/http/pingops-http.ts
436
495
  /**
437
496
  * Pingops HTTP instrumentation that extends HttpInstrumentation
438
497
  * with request/response body capture
439
498
  */
440
- const DEFAULT_MAX_REQUEST_BODY_SIZE$1 = 4 * 1024;
441
- const DEFAULT_MAX_RESPONSE_BODY_SIZE$1 = 4 * 1024;
442
499
  const LEGACY_ATTR_HTTP_URL = "http.url";
443
500
  const PingopsSemanticAttributes = {
444
- HTTP_REQUEST_BODY: "http.request.body",
445
- HTTP_RESPONSE_BODY: "http.response.body"
501
+ HTTP_REQUEST_BODY,
502
+ HTTP_RESPONSE_BODY,
503
+ HTTP_REQUEST_BODY_SIZE,
504
+ HTTP_RESPONSE_BODY_SIZE
446
505
  };
447
506
  /**
448
507
  * Manually flattens a nested object into dot-notation keys
@@ -455,7 +514,7 @@ function isPrimitiveArray(value) {
455
514
  }
456
515
  function flatten(obj, prefix = "") {
457
516
  const result = {};
458
- for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) {
517
+ for (const key in obj) if (Object.hasOwn(obj, key)) {
459
518
  const newKey = prefix ? `${prefix}.${key}` : key;
460
519
  const value = obj[key];
461
520
  if (isPlainObject(value)) Object.assign(result, flatten(value, newKey));
@@ -474,61 +533,12 @@ function setAttributeValue(span, attrName, attrValue) {
474
533
  } else if (isPlainObject(attrValue)) span.setAttributes(flatten({ [attrName]: attrValue }));
475
534
  }
476
535
  /**
477
- * Extracts domain from URL
478
- */
479
- function extractDomainFromUrl$1(url$1) {
480
- try {
481
- return new URL(url$1).hostname;
482
- } catch {
483
- const match = url$1.match(/^(?:https?:\/\/)?([^/]+)/);
484
- return match ? match[1] : "";
485
- }
486
- }
487
- /**
488
- * Gets domain rule configuration for a given URL
489
- */
490
- function getDomainRule$1(url$1, domainAllowList) {
491
- if (!domainAllowList) return;
492
- const domain = extractDomainFromUrl$1(url$1);
493
- for (const rule of domainAllowList) if (domain === rule.domain || domain.endsWith(`.${rule.domain}`) || domain === rule.domain.slice(1)) return rule;
494
- }
495
- /**
496
- * Determines if request body should be captured based on priority:
497
- * context > domain rule > global config > default (false)
498
- */
499
- function shouldCaptureRequestBody$1(url$1) {
500
- const contextValue = _opentelemetry_api.context.active().getValue(_pingops_core.PINGOPS_CAPTURE_REQUEST_BODY);
501
- if (contextValue !== void 0) return contextValue;
502
- if (url$1) {
503
- const domainRule = getDomainRule$1(url$1, getGlobalConfig()?.domainAllowList);
504
- if (domainRule?.captureRequestBody !== void 0) return domainRule.captureRequestBody;
505
- }
506
- const globalConfig$1 = getGlobalConfig();
507
- if (globalConfig$1?.captureRequestBody !== void 0) return globalConfig$1.captureRequestBody;
508
- return false;
509
- }
510
- /**
511
- * Determines if response body should be captured based on priority:
512
- * context > domain rule > global config > default (false)
513
- */
514
- function shouldCaptureResponseBody$1(url$1) {
515
- const contextValue = _opentelemetry_api.context.active().getValue(_pingops_core.PINGOPS_CAPTURE_RESPONSE_BODY);
516
- if (contextValue !== void 0) return contextValue;
517
- if (url$1) {
518
- const domainRule = getDomainRule$1(url$1, getGlobalConfig()?.domainAllowList);
519
- if (domainRule?.captureResponseBody !== void 0) return domainRule.captureResponseBody;
520
- }
521
- const globalConfig$1 = getGlobalConfig();
522
- if (globalConfig$1?.captureResponseBody !== void 0) return globalConfig$1.captureResponseBody;
523
- return false;
524
- }
525
- /**
526
- * Captures request body from string or Buffer data
536
+ * Captures request body from a chunk buffer.
527
537
  */
528
538
  function captureRequestBody(span, data, maxSize, semanticAttr, url$1) {
529
- if (!shouldCaptureRequestBody$1(url$1)) return;
539
+ if (!shouldCaptureRequestBody(url$1)) return;
530
540
  if (data.length && data.length <= maxSize) try {
531
- const requestBody = typeof data === "string" ? data : data.toString("utf-8");
541
+ const requestBody = data.toString("utf-8");
532
542
  if (requestBody) setAttributeValue(span, semanticAttr, requestBody);
533
543
  } catch (e) {
534
544
  console.error("Error occurred while capturing request body:", e);
@@ -538,12 +548,12 @@ function captureRequestBody(span, data, maxSize, semanticAttr, url$1) {
538
548
  * Captures response body from chunks
539
549
  */
540
550
  function captureResponseBody(span, chunks, semanticAttr, responseHeaders, url$1, maxSize) {
541
- if (!shouldCaptureResponseBody$1(url$1)) return;
551
+ if (!shouldCaptureResponseBody(url$1)) return;
542
552
  if (chunks === null) {
543
553
  const contentEncoding = responseHeaders?.["content-encoding"];
544
554
  const contentType = responseHeaders?.["content-type"];
545
555
  const toHeaderString = (value) => typeof value === "string" ? value : Array.isArray(value) ? value.join(", ") : "unknown";
546
- setAttributeValue(span, semanticAttr, `[truncated response body; exceeded maxResponseBodySize=${maxSize ?? DEFAULT_MAX_RESPONSE_BODY_SIZE$1}; content-type=${toHeaderString(contentType)}; content-encoding=${toHeaderString(contentEncoding)}]`);
556
+ setAttributeValue(span, semanticAttr, `[truncated response body; exceeded maxResponseBodySize=${maxSize ?? DEFAULT_MAX_RESPONSE_BODY_SIZE}; content-type=${toHeaderString(contentType)}; content-encoding=${toHeaderString(contentEncoding)}]`);
547
557
  return;
548
558
  }
549
559
  if (chunks.length) try {
@@ -662,7 +672,9 @@ var PingopsHttpInstrumentation = class extends _opentelemetry_instrumentation_ht
662
672
  const headers = extractRequestHeaders(request);
663
673
  if (headers) captureRequestHeaders(span, headers);
664
674
  if (request instanceof http.ClientRequest) {
665
- const maxRequestBodySize = config?.maxRequestBodySize || DEFAULT_MAX_REQUEST_BODY_SIZE$1;
675
+ const maxRequestBodySize = config?.maxRequestBodySize || DEFAULT_MAX_REQUEST_BODY_SIZE;
676
+ let requestBodySize = 0;
677
+ span.setAttribute(PingopsSemanticAttributes.HTTP_REQUEST_BODY_SIZE, requestBodySize);
666
678
  const hostHeader = request.getHeader("host");
667
679
  const host = typeof hostHeader === "string" ? hostHeader : Array.isArray(hostHeader) ? hostHeader.join(",") : typeof hostHeader === "number" ? String(hostHeader) : void 0;
668
680
  const url$1 = request.path && host ? `${request.protocol || "http:"}//${host}${request.path}` : void 0;
@@ -675,11 +687,21 @@ var PingopsHttpInstrumentation = class extends _opentelemetry_instrumentation_ht
675
687
  const originalWrite = request.write.bind(request);
676
688
  const originalEnd = request.end.bind(request);
677
689
  request.write = ((data, ...rest) => {
678
- if (typeof data === "string" || data instanceof Buffer) captureRequestBody(span, data, maxRequestBodySize, PingopsSemanticAttributes.HTTP_REQUEST_BODY, url$1);
690
+ const chunkBuffer = toBufferChunk(data);
691
+ if (chunkBuffer) {
692
+ requestBodySize += chunkBuffer.length;
693
+ span.setAttribute(PingopsSemanticAttributes.HTTP_REQUEST_BODY_SIZE, requestBodySize);
694
+ captureRequestBody(span, chunkBuffer, maxRequestBodySize, PingopsSemanticAttributes.HTTP_REQUEST_BODY, url$1);
695
+ }
679
696
  return originalWrite(data, ...rest);
680
697
  });
681
698
  request.end = ((data, ...rest) => {
682
- if (typeof data === "string" || data instanceof Buffer) captureRequestBody(span, data, maxRequestBodySize, PingopsSemanticAttributes.HTTP_REQUEST_BODY, url$1);
699
+ const chunkBuffer = toBufferChunk(data);
700
+ if (chunkBuffer) {
701
+ requestBodySize += chunkBuffer.length;
702
+ span.setAttribute(PingopsSemanticAttributes.HTTP_REQUEST_BODY_SIZE, requestBodySize);
703
+ captureRequestBody(span, chunkBuffer, maxRequestBodySize, PingopsSemanticAttributes.HTTP_REQUEST_BODY, url$1);
704
+ }
683
705
  return originalEnd(data, ...rest);
684
706
  });
685
707
  }
@@ -697,19 +719,19 @@ var PingopsHttpInstrumentation = class extends _opentelemetry_instrumentation_ht
697
719
  span.setAttribute(_opentelemetry_semantic_conventions.ATTR_URL_PATH, path);
698
720
  if (query) span.setAttribute(_opentelemetry_semantic_conventions.ATTR_URL_QUERY, query);
699
721
  }
700
- const maxResponseBodySize = config?.maxResponseBodySize || DEFAULT_MAX_RESPONSE_BODY_SIZE$1;
722
+ const maxResponseBodySize = config?.maxResponseBodySize || DEFAULT_MAX_RESPONSE_BODY_SIZE;
701
723
  const url$1 = response.url || void 0;
702
724
  let chunks = [];
703
725
  let totalSize = 0;
704
- const shouldCapture = shouldCaptureResponseBody$1(url$1);
726
+ span.setAttribute(PingopsSemanticAttributes.HTTP_RESPONSE_BODY_SIZE, 0);
727
+ const shouldCapture = shouldCaptureResponseBody(url$1);
705
728
  response.prependListener("data", (chunk) => {
706
- if (!chunk || !shouldCapture) return;
707
- let chunkBuffer = null;
708
- if (typeof chunk === "string") chunkBuffer = Buffer.from(chunk);
709
- else if (Buffer.isBuffer(chunk)) chunkBuffer = chunk;
710
- else if (chunk instanceof Uint8Array) chunkBuffer = Buffer.from(chunk);
729
+ if (!chunk) return;
730
+ const chunkBuffer = toBufferChunk(chunk);
711
731
  if (!chunkBuffer) return;
712
732
  totalSize += chunkBuffer.length;
733
+ span.setAttribute(PingopsSemanticAttributes.HTTP_RESPONSE_BODY_SIZE, totalSize);
734
+ if (!shouldCapture) return;
713
735
  if (chunks && totalSize <= maxResponseBodySize) chunks.push(chunkBuffer);
714
736
  else chunks = null;
715
737
  });
@@ -717,6 +739,7 @@ var PingopsHttpInstrumentation = class extends _opentelemetry_instrumentation_ht
717
739
  const finalizeCapture = () => {
718
740
  if (finalized) return;
719
741
  finalized = true;
742
+ span.setAttribute(PingopsSemanticAttributes.HTTP_RESPONSE_BODY_SIZE, totalSize);
720
743
  captureResponseBody(span, chunks, PingopsSemanticAttributes.HTTP_RESPONSE_BODY, headers, url$1, maxResponseBodySize);
721
744
  };
722
745
  response.prependOnceListener("end", finalizeCapture);
@@ -765,59 +788,6 @@ function createHttpInstrumentation(config) {
765
788
 
766
789
  //#endregion
767
790
  //#region src/instrumentations/undici/pingops-undici.ts
768
- const DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024;
769
- const DEFAULT_MAX_RESPONSE_BODY_SIZE = 4 * 1024;
770
- const HTTP_REQUEST_BODY = "http.request.body";
771
- const HTTP_RESPONSE_BODY = "http.response.body";
772
- /**
773
- * Extracts domain from URL
774
- */
775
- function extractDomainFromUrl(url$1) {
776
- try {
777
- return new url.URL(url$1).hostname;
778
- } catch {
779
- const match = url$1.match(/^(?:https?:\/\/)?([^/]+)/);
780
- return match ? match[1] : "";
781
- }
782
- }
783
- /**
784
- * Gets domain rule configuration for a given URL
785
- */
786
- function getDomainRule(url$1, domainAllowList) {
787
- if (!domainAllowList) return;
788
- const domain = extractDomainFromUrl(url$1);
789
- for (const rule of domainAllowList) if (domain === rule.domain || domain.endsWith(`.${rule.domain}`) || domain === rule.domain.slice(1)) return rule;
790
- }
791
- /**
792
- * Determines if request body should be captured based on priority:
793
- * context > domain rule > global config > default (false)
794
- */
795
- function shouldCaptureRequestBody(url$1) {
796
- const contextValue = _opentelemetry_api.context.active().getValue(_pingops_core.PINGOPS_CAPTURE_REQUEST_BODY);
797
- if (contextValue !== void 0) return contextValue;
798
- if (url$1) {
799
- const domainRule = getDomainRule(url$1, getGlobalConfig()?.domainAllowList);
800
- if (domainRule?.captureRequestBody !== void 0) return domainRule.captureRequestBody;
801
- }
802
- const globalConfig$1 = getGlobalConfig();
803
- if (globalConfig$1?.captureRequestBody !== void 0) return globalConfig$1.captureRequestBody;
804
- return false;
805
- }
806
- /**
807
- * Determines if response body should be captured based on priority:
808
- * context > domain rule > global config > default (false)
809
- */
810
- function shouldCaptureResponseBody(url$1) {
811
- const contextValue = _opentelemetry_api.context.active().getValue(_pingops_core.PINGOPS_CAPTURE_RESPONSE_BODY);
812
- if (contextValue !== void 0) return contextValue;
813
- if (url$1) {
814
- const domainRule = getDomainRule(url$1, getGlobalConfig()?.domainAllowList);
815
- if (domainRule?.captureResponseBody !== void 0) return domainRule.captureResponseBody;
816
- }
817
- const globalConfig$1 = getGlobalConfig();
818
- if (globalConfig$1?.captureResponseBody !== void 0) return globalConfig$1.captureResponseBody;
819
- return false;
820
- }
821
791
  var UndiciInstrumentation = class extends _opentelemetry_instrumentation.InstrumentationBase {
822
792
  _recordFromReq = /* @__PURE__ */ new WeakMap();
823
793
  constructor(config = {}) {
@@ -925,7 +895,9 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
925
895
  [_opentelemetry_semantic_conventions.ATTR_URL_FULL]: requestUrl.toString(),
926
896
  [_opentelemetry_semantic_conventions.ATTR_URL_PATH]: requestUrl.pathname,
927
897
  [_opentelemetry_semantic_conventions.ATTR_URL_QUERY]: requestUrl.search,
928
- [_opentelemetry_semantic_conventions.ATTR_URL_SCHEME]: urlScheme
898
+ [_opentelemetry_semantic_conventions.ATTR_URL_SCHEME]: urlScheme,
899
+ [HTTP_REQUEST_BODY_SIZE]: 0,
900
+ [HTTP_RESPONSE_BODY_SIZE]: 0
929
901
  };
930
902
  const schemePorts = {
931
903
  https: "443",
@@ -968,6 +940,10 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
968
940
  responseBodyChunks: [],
969
941
  requestBodySize: 0,
970
942
  responseBodySize: 0,
943
+ requestBodyCaptureSize: 0,
944
+ responseBodyCaptureSize: 0,
945
+ requestBodyCaptureExceeded: false,
946
+ responseBodyCaptureExceeded: false,
971
947
  url: requestUrl.toString()
972
948
  });
973
949
  }
@@ -1014,11 +990,13 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
1014
990
  const record = this._recordFromReq.get(request);
1015
991
  if (!record) return;
1016
992
  const { span, attributes, startTime } = record;
993
+ span.setAttribute(HTTP_REQUEST_BODY_SIZE, record.requestBodySize);
994
+ span.setAttribute(HTTP_RESPONSE_BODY_SIZE, record.responseBodySize);
1017
995
  if (shouldCaptureResponseBody(record.url)) {
1018
996
  const maxResponseBodySize = this.getConfig().maxResponseBodySize ?? DEFAULT_MAX_RESPONSE_BODY_SIZE;
1019
997
  const contentEncoding = record.attributes?.["http.response.header.content-encoding"] ?? void 0;
1020
998
  const contentType = record.attributes?.["http.response.header.content-type"] ?? void 0;
1021
- if (record.responseBodySize === Infinity) span.setAttribute(HTTP_RESPONSE_BODY, `[truncated response body; exceeded maxResponseBodySize=${maxResponseBodySize}; content-type=${contentType ?? "unknown"}; content-encoding=${contentEncoding ?? "identity"}]`);
999
+ if (record.responseBodyCaptureExceeded) span.setAttribute(HTTP_RESPONSE_BODY, `[truncated response body; exceeded maxResponseBodySize=${maxResponseBodySize}; content-type=${contentType ?? "unknown"}; content-encoding=${contentEncoding ?? "identity"}]`);
1022
1000
  else if (record.responseBodyChunks.length > 0) try {
1023
1001
  const responseBodyBuffer = Buffer.concat(record.responseBodyChunks);
1024
1002
  if ((0, _pingops_core.isCompressedContentEncoding)(contentEncoding)) {
@@ -1040,8 +1018,10 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
1040
1018
  const record = this._recordFromReq.get(request);
1041
1019
  if (!record) return;
1042
1020
  const { span, attributes, startTime } = record;
1021
+ span.setAttribute(HTTP_REQUEST_BODY_SIZE, record.requestBodySize);
1022
+ span.setAttribute(HTTP_RESPONSE_BODY_SIZE, record.responseBodySize);
1043
1023
  if (shouldCaptureRequestBody(record.url)) {
1044
- if (record.requestBodyChunks.length > 0 && record.requestBodySize !== Infinity) try {
1024
+ if (record.requestBodyChunks.length > 0 && !record.requestBodyCaptureExceeded) try {
1045
1025
  const requestBody = Buffer.concat(record.requestBodyChunks).toString("utf-8");
1046
1026
  if (requestBody) span.setAttribute(HTTP_REQUEST_BODY, requestBody);
1047
1027
  } catch (e) {
@@ -1062,24 +1042,29 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
1062
1042
  onBodyChunkSent({ request, chunk }) {
1063
1043
  const record = this._recordFromReq.get(request);
1064
1044
  if (!record) return;
1045
+ record.requestBodySize += chunk.length;
1065
1046
  if (!shouldCaptureRequestBody(record.url)) return;
1066
1047
  const maxRequestBodySize = this.getConfig().maxRequestBodySize ?? DEFAULT_MAX_REQUEST_BODY_SIZE;
1067
- if (record.requestBodySize + chunk.length <= maxRequestBodySize) {
1048
+ if (!record.requestBodyCaptureExceeded && record.requestBodyCaptureSize + chunk.length <= maxRequestBodySize) {
1068
1049
  record.requestBodyChunks.push(chunk);
1069
- record.requestBodySize += chunk.length;
1050
+ record.requestBodyCaptureSize += chunk.length;
1070
1051
  } else {
1071
- record.requestBodySize = Infinity;
1052
+ record.requestBodyCaptureExceeded = true;
1072
1053
  record.requestBodyChunks = [];
1054
+ record.requestBodyCaptureSize = 0;
1073
1055
  }
1074
1056
  }
1075
1057
  onBodySent({ request }) {
1076
1058
  const record = this._recordFromReq.get(request);
1077
1059
  if (!record) return;
1060
+ record.span.setAttribute(HTTP_REQUEST_BODY_SIZE, record.requestBodySize);
1078
1061
  if (!shouldCaptureRequestBody(record.url)) {
1079
1062
  record.requestBodyChunks = [];
1063
+ record.requestBodyCaptureSize = 0;
1064
+ record.requestBodyCaptureExceeded = false;
1080
1065
  return;
1081
1066
  }
1082
- if (record.requestBodySize === Infinity) {
1067
+ if (record.requestBodyCaptureExceeded) {
1083
1068
  const maxRequestBodySize = this.getConfig().maxRequestBodySize ?? DEFAULT_MAX_REQUEST_BODY_SIZE;
1084
1069
  record.span.setAttribute(HTTP_REQUEST_BODY, `[truncated request body; exceeded maxRequestBodySize=${maxRequestBodySize}]`);
1085
1070
  } else if (record.requestBodyChunks.length > 0) try {
@@ -1089,18 +1074,22 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
1089
1074
  this._diag.error("Error occurred while capturing request body:", e);
1090
1075
  }
1091
1076
  record.requestBodyChunks = [];
1077
+ record.requestBodyCaptureSize = 0;
1078
+ record.requestBodyCaptureExceeded = false;
1092
1079
  }
1093
1080
  onBodyChunkReceived({ request, chunk }) {
1094
1081
  const record = this._recordFromReq.get(request);
1095
1082
  if (!record) return;
1083
+ record.responseBodySize += chunk.length;
1096
1084
  if (!shouldCaptureResponseBody(record.url)) return;
1097
1085
  const maxResponseBodySize = this.getConfig().maxResponseBodySize ?? DEFAULT_MAX_RESPONSE_BODY_SIZE;
1098
- if (record.responseBodySize + chunk.length <= maxResponseBodySize) {
1086
+ if (!record.responseBodyCaptureExceeded && record.responseBodyCaptureSize + chunk.length <= maxResponseBodySize) {
1099
1087
  record.responseBodyChunks.push(chunk);
1100
- record.responseBodySize += chunk.length;
1088
+ record.responseBodyCaptureSize += chunk.length;
1101
1089
  } else {
1102
- record.responseBodySize = Infinity;
1090
+ record.responseBodyCaptureExceeded = true;
1103
1091
  record.responseBodyChunks = [];
1092
+ record.responseBodyCaptureSize = 0;
1104
1093
  }
1105
1094
  }
1106
1095
  recordRequestDuration(attributes, startTime) {