@pingops/otel 0.2.5 → 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 +148 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -22
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +23 -22
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +149 -133
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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
|
|
445
|
-
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.
|
|
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
|
-
*
|
|
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
|
|
539
|
+
if (!shouldCaptureRequestBody(url$1)) return;
|
|
530
540
|
if (data.length && data.length <= maxSize) try {
|
|
531
|
-
const requestBody =
|
|
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
|
|
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
|
|
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 {
|
|
@@ -615,6 +625,21 @@ function extractRequestUrlFromSpanOptions(options) {
|
|
|
615
625
|
if (!host) return;
|
|
616
626
|
return `${scheme}://${host}${typeof attrs[_opentelemetry_semantic_conventions.ATTR_SERVER_PORT] === "number" ? `:${attrs[_opentelemetry_semantic_conventions.ATTR_SERVER_PORT]}` : ""}${typeof attrs[_opentelemetry_semantic_conventions.ATTR_URL_PATH] === "string" ? attrs[_opentelemetry_semantic_conventions.ATTR_URL_PATH] : "/"}${typeof attrs[_opentelemetry_semantic_conventions.ATTR_URL_QUERY] === "string" && attrs[_opentelemetry_semantic_conventions.ATTR_URL_QUERY].length > 0 ? `?${attrs[_opentelemetry_semantic_conventions.ATTR_URL_QUERY]}` : ""}`;
|
|
617
627
|
}
|
|
628
|
+
function parseRequestPathAndQuery(pathWithQuery) {
|
|
629
|
+
const queryIndex = pathWithQuery.indexOf("?");
|
|
630
|
+
if (queryIndex < 0) return { path: pathWithQuery || "/" };
|
|
631
|
+
const path = pathWithQuery.slice(0, queryIndex) || "/";
|
|
632
|
+
const queryPart = pathWithQuery.slice(queryIndex);
|
|
633
|
+
return {
|
|
634
|
+
path,
|
|
635
|
+
query: queryPart.length > 0 ? queryPart : void 0
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
function extractClientRequestPath(request) {
|
|
639
|
+
if (!request || typeof request !== "object" || !("path" in request)) return;
|
|
640
|
+
const path = request.path;
|
|
641
|
+
return typeof path === "string" && path.length > 0 ? path : void 0;
|
|
642
|
+
}
|
|
618
643
|
const PingopsHttpSemanticAttributes = PingopsSemanticAttributes;
|
|
619
644
|
var PingopsHttpInstrumentation = class extends _opentelemetry_instrumentation_http.HttpInstrumentation {
|
|
620
645
|
constructor(config) {
|
|
@@ -647,18 +672,36 @@ var PingopsHttpInstrumentation = class extends _opentelemetry_instrumentation_ht
|
|
|
647
672
|
const headers = extractRequestHeaders(request);
|
|
648
673
|
if (headers) captureRequestHeaders(span, headers);
|
|
649
674
|
if (request instanceof http.ClientRequest) {
|
|
650
|
-
const maxRequestBodySize = config?.maxRequestBodySize || DEFAULT_MAX_REQUEST_BODY_SIZE
|
|
675
|
+
const maxRequestBodySize = config?.maxRequestBodySize || DEFAULT_MAX_REQUEST_BODY_SIZE;
|
|
676
|
+
let requestBodySize = 0;
|
|
677
|
+
span.setAttribute(PingopsSemanticAttributes.HTTP_REQUEST_BODY_SIZE, requestBodySize);
|
|
651
678
|
const hostHeader = request.getHeader("host");
|
|
652
679
|
const host = typeof hostHeader === "string" ? hostHeader : Array.isArray(hostHeader) ? hostHeader.join(",") : typeof hostHeader === "number" ? String(hostHeader) : void 0;
|
|
653
680
|
const url$1 = request.path && host ? `${request.protocol || "http:"}//${host}${request.path}` : void 0;
|
|
681
|
+
if (typeof request.path === "string" && request.path.length > 0) {
|
|
682
|
+
const { path, query } = parseRequestPathAndQuery(request.path);
|
|
683
|
+
span.setAttribute(_opentelemetry_semantic_conventions.ATTR_URL_PATH, path);
|
|
684
|
+
if (query) span.setAttribute(_opentelemetry_semantic_conventions.ATTR_URL_QUERY, query);
|
|
685
|
+
}
|
|
686
|
+
if (url$1) span.setAttribute(_opentelemetry_semantic_conventions.ATTR_URL_FULL, url$1);
|
|
654
687
|
const originalWrite = request.write.bind(request);
|
|
655
688
|
const originalEnd = request.end.bind(request);
|
|
656
689
|
request.write = ((data, ...rest) => {
|
|
657
|
-
|
|
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
|
+
}
|
|
658
696
|
return originalWrite(data, ...rest);
|
|
659
697
|
});
|
|
660
698
|
request.end = ((data, ...rest) => {
|
|
661
|
-
|
|
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
|
+
}
|
|
662
705
|
return originalEnd(data, ...rest);
|
|
663
706
|
});
|
|
664
707
|
}
|
|
@@ -670,19 +713,25 @@ var PingopsHttpInstrumentation = class extends _opentelemetry_instrumentation_ht
|
|
|
670
713
|
const headers = extractResponseHeaders(response);
|
|
671
714
|
if (headers) captureResponseHeaders(span, headers);
|
|
672
715
|
if (response instanceof http.IncomingMessage) {
|
|
673
|
-
const
|
|
716
|
+
const requestPath = response.req instanceof http.ClientRequest ? extractClientRequestPath(response.req) : void 0;
|
|
717
|
+
if (requestPath) {
|
|
718
|
+
const { path, query } = parseRequestPathAndQuery(requestPath);
|
|
719
|
+
span.setAttribute(_opentelemetry_semantic_conventions.ATTR_URL_PATH, path);
|
|
720
|
+
if (query) span.setAttribute(_opentelemetry_semantic_conventions.ATTR_URL_QUERY, query);
|
|
721
|
+
}
|
|
722
|
+
const maxResponseBodySize = config?.maxResponseBodySize || DEFAULT_MAX_RESPONSE_BODY_SIZE;
|
|
674
723
|
const url$1 = response.url || void 0;
|
|
675
724
|
let chunks = [];
|
|
676
725
|
let totalSize = 0;
|
|
677
|
-
|
|
726
|
+
span.setAttribute(PingopsSemanticAttributes.HTTP_RESPONSE_BODY_SIZE, 0);
|
|
727
|
+
const shouldCapture = shouldCaptureResponseBody(url$1);
|
|
678
728
|
response.prependListener("data", (chunk) => {
|
|
679
|
-
if (!chunk
|
|
680
|
-
|
|
681
|
-
if (typeof chunk === "string") chunkBuffer = Buffer.from(chunk);
|
|
682
|
-
else if (Buffer.isBuffer(chunk)) chunkBuffer = chunk;
|
|
683
|
-
else if (chunk instanceof Uint8Array) chunkBuffer = Buffer.from(chunk);
|
|
729
|
+
if (!chunk) return;
|
|
730
|
+
const chunkBuffer = toBufferChunk(chunk);
|
|
684
731
|
if (!chunkBuffer) return;
|
|
685
732
|
totalSize += chunkBuffer.length;
|
|
733
|
+
span.setAttribute(PingopsSemanticAttributes.HTTP_RESPONSE_BODY_SIZE, totalSize);
|
|
734
|
+
if (!shouldCapture) return;
|
|
686
735
|
if (chunks && totalSize <= maxResponseBodySize) chunks.push(chunkBuffer);
|
|
687
736
|
else chunks = null;
|
|
688
737
|
});
|
|
@@ -690,6 +739,7 @@ var PingopsHttpInstrumentation = class extends _opentelemetry_instrumentation_ht
|
|
|
690
739
|
const finalizeCapture = () => {
|
|
691
740
|
if (finalized) return;
|
|
692
741
|
finalized = true;
|
|
742
|
+
span.setAttribute(PingopsSemanticAttributes.HTTP_RESPONSE_BODY_SIZE, totalSize);
|
|
693
743
|
captureResponseBody(span, chunks, PingopsSemanticAttributes.HTTP_RESPONSE_BODY, headers, url$1, maxResponseBodySize);
|
|
694
744
|
};
|
|
695
745
|
response.prependOnceListener("end", finalizeCapture);
|
|
@@ -738,59 +788,6 @@ function createHttpInstrumentation(config) {
|
|
|
738
788
|
|
|
739
789
|
//#endregion
|
|
740
790
|
//#region src/instrumentations/undici/pingops-undici.ts
|
|
741
|
-
const DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024;
|
|
742
|
-
const DEFAULT_MAX_RESPONSE_BODY_SIZE = 4 * 1024;
|
|
743
|
-
const HTTP_REQUEST_BODY = "http.request.body";
|
|
744
|
-
const HTTP_RESPONSE_BODY = "http.response.body";
|
|
745
|
-
/**
|
|
746
|
-
* Extracts domain from URL
|
|
747
|
-
*/
|
|
748
|
-
function extractDomainFromUrl(url$1) {
|
|
749
|
-
try {
|
|
750
|
-
return new url.URL(url$1).hostname;
|
|
751
|
-
} catch {
|
|
752
|
-
const match = url$1.match(/^(?:https?:\/\/)?([^/]+)/);
|
|
753
|
-
return match ? match[1] : "";
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
/**
|
|
757
|
-
* Gets domain rule configuration for a given URL
|
|
758
|
-
*/
|
|
759
|
-
function getDomainRule(url$1, domainAllowList) {
|
|
760
|
-
if (!domainAllowList) return;
|
|
761
|
-
const domain = extractDomainFromUrl(url$1);
|
|
762
|
-
for (const rule of domainAllowList) if (domain === rule.domain || domain.endsWith(`.${rule.domain}`) || domain === rule.domain.slice(1)) return rule;
|
|
763
|
-
}
|
|
764
|
-
/**
|
|
765
|
-
* Determines if request body should be captured based on priority:
|
|
766
|
-
* context > domain rule > global config > default (false)
|
|
767
|
-
*/
|
|
768
|
-
function shouldCaptureRequestBody(url$1) {
|
|
769
|
-
const contextValue = _opentelemetry_api.context.active().getValue(_pingops_core.PINGOPS_CAPTURE_REQUEST_BODY);
|
|
770
|
-
if (contextValue !== void 0) return contextValue;
|
|
771
|
-
if (url$1) {
|
|
772
|
-
const domainRule = getDomainRule(url$1, getGlobalConfig()?.domainAllowList);
|
|
773
|
-
if (domainRule?.captureRequestBody !== void 0) return domainRule.captureRequestBody;
|
|
774
|
-
}
|
|
775
|
-
const globalConfig$1 = getGlobalConfig();
|
|
776
|
-
if (globalConfig$1?.captureRequestBody !== void 0) return globalConfig$1.captureRequestBody;
|
|
777
|
-
return false;
|
|
778
|
-
}
|
|
779
|
-
/**
|
|
780
|
-
* Determines if response body should be captured based on priority:
|
|
781
|
-
* context > domain rule > global config > default (false)
|
|
782
|
-
*/
|
|
783
|
-
function shouldCaptureResponseBody(url$1) {
|
|
784
|
-
const contextValue = _opentelemetry_api.context.active().getValue(_pingops_core.PINGOPS_CAPTURE_RESPONSE_BODY);
|
|
785
|
-
if (contextValue !== void 0) return contextValue;
|
|
786
|
-
if (url$1) {
|
|
787
|
-
const domainRule = getDomainRule(url$1, getGlobalConfig()?.domainAllowList);
|
|
788
|
-
if (domainRule?.captureResponseBody !== void 0) return domainRule.captureResponseBody;
|
|
789
|
-
}
|
|
790
|
-
const globalConfig$1 = getGlobalConfig();
|
|
791
|
-
if (globalConfig$1?.captureResponseBody !== void 0) return globalConfig$1.captureResponseBody;
|
|
792
|
-
return false;
|
|
793
|
-
}
|
|
794
791
|
var UndiciInstrumentation = class extends _opentelemetry_instrumentation.InstrumentationBase {
|
|
795
792
|
_recordFromReq = /* @__PURE__ */ new WeakMap();
|
|
796
793
|
constructor(config = {}) {
|
|
@@ -898,7 +895,9 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
|
|
|
898
895
|
[_opentelemetry_semantic_conventions.ATTR_URL_FULL]: requestUrl.toString(),
|
|
899
896
|
[_opentelemetry_semantic_conventions.ATTR_URL_PATH]: requestUrl.pathname,
|
|
900
897
|
[_opentelemetry_semantic_conventions.ATTR_URL_QUERY]: requestUrl.search,
|
|
901
|
-
[_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
|
|
902
901
|
};
|
|
903
902
|
const schemePorts = {
|
|
904
903
|
https: "443",
|
|
@@ -941,6 +940,10 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
|
|
|
941
940
|
responseBodyChunks: [],
|
|
942
941
|
requestBodySize: 0,
|
|
943
942
|
responseBodySize: 0,
|
|
943
|
+
requestBodyCaptureSize: 0,
|
|
944
|
+
responseBodyCaptureSize: 0,
|
|
945
|
+
requestBodyCaptureExceeded: false,
|
|
946
|
+
responseBodyCaptureExceeded: false,
|
|
944
947
|
url: requestUrl.toString()
|
|
945
948
|
});
|
|
946
949
|
}
|
|
@@ -987,11 +990,13 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
|
|
|
987
990
|
const record = this._recordFromReq.get(request);
|
|
988
991
|
if (!record) return;
|
|
989
992
|
const { span, attributes, startTime } = record;
|
|
993
|
+
span.setAttribute(HTTP_REQUEST_BODY_SIZE, record.requestBodySize);
|
|
994
|
+
span.setAttribute(HTTP_RESPONSE_BODY_SIZE, record.responseBodySize);
|
|
990
995
|
if (shouldCaptureResponseBody(record.url)) {
|
|
991
996
|
const maxResponseBodySize = this.getConfig().maxResponseBodySize ?? DEFAULT_MAX_RESPONSE_BODY_SIZE;
|
|
992
997
|
const contentEncoding = record.attributes?.["http.response.header.content-encoding"] ?? void 0;
|
|
993
998
|
const contentType = record.attributes?.["http.response.header.content-type"] ?? void 0;
|
|
994
|
-
if (record.
|
|
999
|
+
if (record.responseBodyCaptureExceeded) span.setAttribute(HTTP_RESPONSE_BODY, `[truncated response body; exceeded maxResponseBodySize=${maxResponseBodySize}; content-type=${contentType ?? "unknown"}; content-encoding=${contentEncoding ?? "identity"}]`);
|
|
995
1000
|
else if (record.responseBodyChunks.length > 0) try {
|
|
996
1001
|
const responseBodyBuffer = Buffer.concat(record.responseBodyChunks);
|
|
997
1002
|
if ((0, _pingops_core.isCompressedContentEncoding)(contentEncoding)) {
|
|
@@ -1013,8 +1018,10 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
|
|
|
1013
1018
|
const record = this._recordFromReq.get(request);
|
|
1014
1019
|
if (!record) return;
|
|
1015
1020
|
const { span, attributes, startTime } = record;
|
|
1021
|
+
span.setAttribute(HTTP_REQUEST_BODY_SIZE, record.requestBodySize);
|
|
1022
|
+
span.setAttribute(HTTP_RESPONSE_BODY_SIZE, record.responseBodySize);
|
|
1016
1023
|
if (shouldCaptureRequestBody(record.url)) {
|
|
1017
|
-
if (record.requestBodyChunks.length > 0 && record.
|
|
1024
|
+
if (record.requestBodyChunks.length > 0 && !record.requestBodyCaptureExceeded) try {
|
|
1018
1025
|
const requestBody = Buffer.concat(record.requestBodyChunks).toString("utf-8");
|
|
1019
1026
|
if (requestBody) span.setAttribute(HTTP_REQUEST_BODY, requestBody);
|
|
1020
1027
|
} catch (e) {
|
|
@@ -1035,24 +1042,29 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
|
|
|
1035
1042
|
onBodyChunkSent({ request, chunk }) {
|
|
1036
1043
|
const record = this._recordFromReq.get(request);
|
|
1037
1044
|
if (!record) return;
|
|
1045
|
+
record.requestBodySize += chunk.length;
|
|
1038
1046
|
if (!shouldCaptureRequestBody(record.url)) return;
|
|
1039
1047
|
const maxRequestBodySize = this.getConfig().maxRequestBodySize ?? DEFAULT_MAX_REQUEST_BODY_SIZE;
|
|
1040
|
-
if (record.
|
|
1048
|
+
if (!record.requestBodyCaptureExceeded && record.requestBodyCaptureSize + chunk.length <= maxRequestBodySize) {
|
|
1041
1049
|
record.requestBodyChunks.push(chunk);
|
|
1042
|
-
record.
|
|
1050
|
+
record.requestBodyCaptureSize += chunk.length;
|
|
1043
1051
|
} else {
|
|
1044
|
-
record.
|
|
1052
|
+
record.requestBodyCaptureExceeded = true;
|
|
1045
1053
|
record.requestBodyChunks = [];
|
|
1054
|
+
record.requestBodyCaptureSize = 0;
|
|
1046
1055
|
}
|
|
1047
1056
|
}
|
|
1048
1057
|
onBodySent({ request }) {
|
|
1049
1058
|
const record = this._recordFromReq.get(request);
|
|
1050
1059
|
if (!record) return;
|
|
1060
|
+
record.span.setAttribute(HTTP_REQUEST_BODY_SIZE, record.requestBodySize);
|
|
1051
1061
|
if (!shouldCaptureRequestBody(record.url)) {
|
|
1052
1062
|
record.requestBodyChunks = [];
|
|
1063
|
+
record.requestBodyCaptureSize = 0;
|
|
1064
|
+
record.requestBodyCaptureExceeded = false;
|
|
1053
1065
|
return;
|
|
1054
1066
|
}
|
|
1055
|
-
if (record.
|
|
1067
|
+
if (record.requestBodyCaptureExceeded) {
|
|
1056
1068
|
const maxRequestBodySize = this.getConfig().maxRequestBodySize ?? DEFAULT_MAX_REQUEST_BODY_SIZE;
|
|
1057
1069
|
record.span.setAttribute(HTTP_REQUEST_BODY, `[truncated request body; exceeded maxRequestBodySize=${maxRequestBodySize}]`);
|
|
1058
1070
|
} else if (record.requestBodyChunks.length > 0) try {
|
|
@@ -1062,18 +1074,22 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
|
|
|
1062
1074
|
this._diag.error("Error occurred while capturing request body:", e);
|
|
1063
1075
|
}
|
|
1064
1076
|
record.requestBodyChunks = [];
|
|
1077
|
+
record.requestBodyCaptureSize = 0;
|
|
1078
|
+
record.requestBodyCaptureExceeded = false;
|
|
1065
1079
|
}
|
|
1066
1080
|
onBodyChunkReceived({ request, chunk }) {
|
|
1067
1081
|
const record = this._recordFromReq.get(request);
|
|
1068
1082
|
if (!record) return;
|
|
1083
|
+
record.responseBodySize += chunk.length;
|
|
1069
1084
|
if (!shouldCaptureResponseBody(record.url)) return;
|
|
1070
1085
|
const maxResponseBodySize = this.getConfig().maxResponseBodySize ?? DEFAULT_MAX_RESPONSE_BODY_SIZE;
|
|
1071
|
-
if (record.
|
|
1086
|
+
if (!record.responseBodyCaptureExceeded && record.responseBodyCaptureSize + chunk.length <= maxResponseBodySize) {
|
|
1072
1087
|
record.responseBodyChunks.push(chunk);
|
|
1073
|
-
record.
|
|
1088
|
+
record.responseBodyCaptureSize += chunk.length;
|
|
1074
1089
|
} else {
|
|
1075
|
-
record.
|
|
1090
|
+
record.responseBodyCaptureExceeded = true;
|
|
1076
1091
|
record.responseBodyChunks = [];
|
|
1092
|
+
record.responseBodyCaptureSize = 0;
|
|
1077
1093
|
}
|
|
1078
1094
|
}
|
|
1079
1095
|
recordRequestDuration(attributes, startTime) {
|