@pingops/otel 0.2.4 → 0.2.5
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 +55 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +56 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { HTTP_RESPONSE_CONTENT_ENCODING, PINGOPS_CAPTURE_REQUEST_BODY, PINGOPS_C
|
|
|
4
4
|
import { INVALID_SPAN_CONTEXT, ROOT_CONTEXT, SpanKind, SpanStatusCode, ValueType, context, propagation, trace } from "@opentelemetry/api";
|
|
5
5
|
import "@opentelemetry/sdk-trace-node";
|
|
6
6
|
import "@opentelemetry/resources";
|
|
7
|
-
import { ATTR_ERROR_TYPE, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_REQUEST_METHOD_ORIGINAL, ATTR_HTTP_RESPONSE_STATUS_CODE,
|
|
7
|
+
import { ATTR_ERROR_TYPE, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_REQUEST_METHOD_ORIGINAL, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_NETWORK_PEER_ADDRESS, ATTR_NETWORK_PEER_PORT, ATTR_SERVER_ADDRESS, ATTR_SERVER_PORT, ATTR_URL_FULL, ATTR_URL_PATH, ATTR_URL_QUERY, ATTR_URL_SCHEME, ATTR_USER_AGENT_ORIGINAL, METRIC_HTTP_CLIENT_REQUEST_DURATION } from "@opentelemetry/semantic-conventions";
|
|
8
8
|
import { ClientRequest, IncomingMessage } from "http";
|
|
9
9
|
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
|
|
10
10
|
import { hrTime, hrTimeDuration, hrTimeToMilliseconds, isTracingSuppressed } from "@opentelemetry/core";
|
|
@@ -411,6 +411,7 @@ function resolveOutboundSpanParentContext(activeContext, requestUrl) {
|
|
|
411
411
|
*/
|
|
412
412
|
const DEFAULT_MAX_REQUEST_BODY_SIZE$1 = 4 * 1024;
|
|
413
413
|
const DEFAULT_MAX_RESPONSE_BODY_SIZE$1 = 4 * 1024;
|
|
414
|
+
const LEGACY_ATTR_HTTP_URL = "http.url";
|
|
414
415
|
const PingopsSemanticAttributes = {
|
|
415
416
|
HTTP_REQUEST_BODY: "http.request.body",
|
|
416
417
|
HTTP_RESPONSE_BODY: "http.response.body"
|
|
@@ -418,12 +419,18 @@ const PingopsSemanticAttributes = {
|
|
|
418
419
|
/**
|
|
419
420
|
* Manually flattens a nested object into dot-notation keys
|
|
420
421
|
*/
|
|
422
|
+
function isPlainObject(value) {
|
|
423
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Buffer);
|
|
424
|
+
}
|
|
425
|
+
function isPrimitiveArray(value) {
|
|
426
|
+
return value.every((item) => typeof item === "string" || typeof item === "number" || typeof item === "boolean");
|
|
427
|
+
}
|
|
421
428
|
function flatten(obj, prefix = "") {
|
|
422
429
|
const result = {};
|
|
423
430
|
for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
424
431
|
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
425
432
|
const value = obj[key];
|
|
426
|
-
if (
|
|
433
|
+
if (isPlainObject(value)) Object.assign(result, flatten(value, newKey));
|
|
427
434
|
else result[newKey] = value;
|
|
428
435
|
}
|
|
429
436
|
return result;
|
|
@@ -434,11 +441,9 @@ function flatten(obj, prefix = "") {
|
|
|
434
441
|
function setAttributeValue(span, attrName, attrValue) {
|
|
435
442
|
if (typeof attrValue === "string" || typeof attrValue === "number" || typeof attrValue === "boolean") span.setAttribute(attrName, attrValue);
|
|
436
443
|
else if (attrValue instanceof Buffer) span.setAttribute(attrName, attrValue.toString("utf8"));
|
|
437
|
-
else if (
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (typeof firstElement === "string" || typeof firstElement === "number" || typeof firstElement === "boolean") span.setAttribute(attrName, attrValue);
|
|
441
|
-
} else span.setAttribute(attrName, attrValue);
|
|
444
|
+
else if (Array.isArray(attrValue)) {
|
|
445
|
+
if (attrValue.length === 0 || isPrimitiveArray(attrValue)) span.setAttribute(attrName, attrValue);
|
|
446
|
+
} else if (isPlainObject(attrValue)) span.setAttributes(flatten({ [attrName]: attrValue }));
|
|
442
447
|
}
|
|
443
448
|
/**
|
|
444
449
|
* Extracts domain from URL
|
|
@@ -504,9 +509,16 @@ function captureRequestBody(span, data, maxSize, semanticAttr, url) {
|
|
|
504
509
|
/**
|
|
505
510
|
* Captures response body from chunks
|
|
506
511
|
*/
|
|
507
|
-
function captureResponseBody(span, chunks, semanticAttr, responseHeaders, url) {
|
|
512
|
+
function captureResponseBody(span, chunks, semanticAttr, responseHeaders, url, maxSize) {
|
|
508
513
|
if (!shouldCaptureResponseBody$1(url)) return;
|
|
509
|
-
if (chunks
|
|
514
|
+
if (chunks === null) {
|
|
515
|
+
const contentEncoding = responseHeaders?.["content-encoding"];
|
|
516
|
+
const contentType = responseHeaders?.["content-type"];
|
|
517
|
+
const toHeaderString = (value) => typeof value === "string" ? value : Array.isArray(value) ? value.join(", ") : "unknown";
|
|
518
|
+
setAttributeValue(span, semanticAttr, `[truncated response body; exceeded maxResponseBodySize=${maxSize ?? DEFAULT_MAX_RESPONSE_BODY_SIZE$1}; content-type=${toHeaderString(contentType)}; content-encoding=${toHeaderString(contentEncoding)}]`);
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
if (chunks.length) try {
|
|
510
522
|
const concatedChunks = Buffer.concat(chunks);
|
|
511
523
|
const contentEncoding = responseHeaders?.["content-encoding"];
|
|
512
524
|
if (isCompressedContentEncoding(contentEncoding)) {
|
|
@@ -569,7 +581,7 @@ function extractRequestUrlFromSpanOptions(options) {
|
|
|
569
581
|
const attrs = options.attributes;
|
|
570
582
|
if (!attrs) return;
|
|
571
583
|
if (typeof attrs[ATTR_URL_FULL] === "string") return attrs[ATTR_URL_FULL];
|
|
572
|
-
if (typeof attrs[
|
|
584
|
+
if (typeof attrs[LEGACY_ATTR_HTTP_URL] === "string") return attrs[LEGACY_ATTR_HTTP_URL];
|
|
573
585
|
const scheme = typeof attrs[ATTR_URL_SCHEME] === "string" ? attrs[ATTR_URL_SCHEME] : "http";
|
|
574
586
|
const host = typeof attrs[ATTR_SERVER_ADDRESS] === "string" ? attrs[ATTR_SERVER_ADDRESS] : void 0;
|
|
575
587
|
if (!host) return;
|
|
@@ -608,17 +620,19 @@ var PingopsHttpInstrumentation = class extends HttpInstrumentation {
|
|
|
608
620
|
if (headers) captureRequestHeaders(span, headers);
|
|
609
621
|
if (request instanceof ClientRequest) {
|
|
610
622
|
const maxRequestBodySize = config?.maxRequestBodySize || DEFAULT_MAX_REQUEST_BODY_SIZE$1;
|
|
611
|
-
const
|
|
623
|
+
const hostHeader = request.getHeader("host");
|
|
624
|
+
const host = typeof hostHeader === "string" ? hostHeader : Array.isArray(hostHeader) ? hostHeader.join(",") : typeof hostHeader === "number" ? String(hostHeader) : void 0;
|
|
625
|
+
const url = request.path && host ? `${request.protocol || "http:"}//${host}${request.path}` : void 0;
|
|
612
626
|
const originalWrite = request.write.bind(request);
|
|
613
627
|
const originalEnd = request.end.bind(request);
|
|
614
|
-
request.write = (data) => {
|
|
628
|
+
request.write = ((data, ...rest) => {
|
|
615
629
|
if (typeof data === "string" || data instanceof Buffer) captureRequestBody(span, data, maxRequestBodySize, PingopsSemanticAttributes.HTTP_REQUEST_BODY, url);
|
|
616
|
-
return originalWrite(data);
|
|
617
|
-
};
|
|
618
|
-
request.end = (data) => {
|
|
630
|
+
return originalWrite(data, ...rest);
|
|
631
|
+
});
|
|
632
|
+
request.end = ((data, ...rest) => {
|
|
619
633
|
if (typeof data === "string" || data instanceof Buffer) captureRequestBody(span, data, maxRequestBodySize, PingopsSemanticAttributes.HTTP_REQUEST_BODY, url);
|
|
620
|
-
return originalEnd(data);
|
|
621
|
-
};
|
|
634
|
+
return originalEnd(data, ...rest);
|
|
635
|
+
});
|
|
622
636
|
}
|
|
623
637
|
if (originalRequestHook) originalRequestHook(span, request);
|
|
624
638
|
};
|
|
@@ -635,15 +649,24 @@ var PingopsHttpInstrumentation = class extends HttpInstrumentation {
|
|
|
635
649
|
const shouldCapture = shouldCaptureResponseBody$1(url);
|
|
636
650
|
response.prependListener("data", (chunk) => {
|
|
637
651
|
if (!chunk || !shouldCapture) return;
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
652
|
+
let chunkBuffer = null;
|
|
653
|
+
if (typeof chunk === "string") chunkBuffer = Buffer.from(chunk);
|
|
654
|
+
else if (Buffer.isBuffer(chunk)) chunkBuffer = chunk;
|
|
655
|
+
else if (chunk instanceof Uint8Array) chunkBuffer = Buffer.from(chunk);
|
|
656
|
+
if (!chunkBuffer) return;
|
|
657
|
+
totalSize += chunkBuffer.length;
|
|
658
|
+
if (chunks && totalSize <= maxResponseBodySize) chunks.push(chunkBuffer);
|
|
659
|
+
else chunks = null;
|
|
646
660
|
});
|
|
661
|
+
let finalized = false;
|
|
662
|
+
const finalizeCapture = () => {
|
|
663
|
+
if (finalized) return;
|
|
664
|
+
finalized = true;
|
|
665
|
+
captureResponseBody(span, chunks, PingopsSemanticAttributes.HTTP_RESPONSE_BODY, headers, url, maxResponseBodySize);
|
|
666
|
+
};
|
|
667
|
+
response.prependOnceListener("end", finalizeCapture);
|
|
668
|
+
response.prependOnceListener("close", finalizeCapture);
|
|
669
|
+
response.prependOnceListener("aborted", finalizeCapture);
|
|
647
670
|
}
|
|
648
671
|
if (originalResponseHook) originalResponseHook(span, response);
|
|
649
672
|
};
|
|
@@ -897,11 +920,11 @@ var UndiciInstrumentation = class extends InstrumentationBase {
|
|
|
897
920
|
const record = this._recordFromReq.get(request);
|
|
898
921
|
if (!record) return;
|
|
899
922
|
const { span } = record;
|
|
900
|
-
const
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
923
|
+
const spanAttributes = {};
|
|
924
|
+
const remoteAddress = typeof socket.remoteAddress === "string" ? socket.remoteAddress : void 0;
|
|
925
|
+
const remotePort = typeof socket.remotePort === "number" ? socket.remotePort : void 0;
|
|
926
|
+
if (remoteAddress) spanAttributes[ATTR_NETWORK_PEER_ADDRESS] = remoteAddress;
|
|
927
|
+
if (remotePort !== void 0) spanAttributes[ATTR_NETWORK_PEER_PORT] = remotePort;
|
|
905
928
|
const headersMap = this.parseRequestHeaders(request);
|
|
906
929
|
for (const [name, value] of headersMap.entries()) {
|
|
907
930
|
const attrValue = Array.isArray(value) ? value.join(", ") : value;
|
|
@@ -970,14 +993,15 @@ var UndiciInstrumentation = class extends InstrumentationBase {
|
|
|
970
993
|
this._diag.error("Error occurred while capturing request body:", e);
|
|
971
994
|
}
|
|
972
995
|
}
|
|
996
|
+
const errorMessage = error.message;
|
|
973
997
|
span.recordException(error);
|
|
974
998
|
span.setStatus({
|
|
975
999
|
code: SpanStatusCode.ERROR,
|
|
976
|
-
message:
|
|
1000
|
+
message: errorMessage
|
|
977
1001
|
});
|
|
978
1002
|
span.end();
|
|
979
1003
|
this._recordFromReq.delete(request);
|
|
980
|
-
attributes[ATTR_ERROR_TYPE] =
|
|
1004
|
+
attributes[ATTR_ERROR_TYPE] = errorMessage;
|
|
981
1005
|
this.recordRequestDuration(attributes, startTime);
|
|
982
1006
|
}
|
|
983
1007
|
onBodyChunkSent({ request, chunk }) {
|