@pingops/otel 0.2.3 → 0.2.4
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 +71 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +71 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -60,6 +60,21 @@ function getGlobalConfig() {
|
|
|
60
60
|
//#endregion
|
|
61
61
|
//#region src/span-processor.ts
|
|
62
62
|
const logger$2 = (0, _pingops_core.createLogger)("[PingOps Processor]");
|
|
63
|
+
function normalizePath$1(pathname) {
|
|
64
|
+
return pathname.replace(/\/+/g, "/").replace(/\/$/, "");
|
|
65
|
+
}
|
|
66
|
+
function isExporterRequestUrl$1(url$1, exporterUrl) {
|
|
67
|
+
try {
|
|
68
|
+
const request = new URL(url$1);
|
|
69
|
+
const exporter = new URL(exporterUrl);
|
|
70
|
+
if (request.origin !== exporter.origin) return false;
|
|
71
|
+
const requestPath = normalizePath$1(request.pathname);
|
|
72
|
+
const exporterPath = normalizePath$1(exporter.pathname);
|
|
73
|
+
return requestPath === exporterPath || requestPath.startsWith(`${exporterPath}/`);
|
|
74
|
+
} catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
63
78
|
/**
|
|
64
79
|
* Creates a filtered span wrapper that applies header filtering to attributes
|
|
65
80
|
*
|
|
@@ -93,6 +108,7 @@ function createFilteredSpan(span, domainAllowList, globalHeadersAllowList, globa
|
|
|
93
108
|
*/
|
|
94
109
|
var PingopsSpanProcessor = class {
|
|
95
110
|
processor;
|
|
111
|
+
exporterTraceUrl;
|
|
96
112
|
config;
|
|
97
113
|
/**
|
|
98
114
|
* Creates a new PingopsSpanProcessor instance.
|
|
@@ -102,8 +118,9 @@ var PingopsSpanProcessor = class {
|
|
|
102
118
|
constructor(config) {
|
|
103
119
|
const exportMode = config.exportMode ?? "batched";
|
|
104
120
|
const apiKey = config.apiKey || process.env.PINGOPS_API_KEY || "";
|
|
121
|
+
this.exporterTraceUrl = `${config.baseUrl}/v1/traces`;
|
|
105
122
|
const exporter = new _opentelemetry_exporter_trace_otlp_http.OTLPTraceExporter({
|
|
106
|
-
url:
|
|
123
|
+
url: this.exporterTraceUrl,
|
|
107
124
|
headers: {
|
|
108
125
|
Authorization: apiKey ? `Bearer ${apiKey}` : "",
|
|
109
126
|
"Content-Type": "application/json"
|
|
@@ -131,7 +148,7 @@ var PingopsSpanProcessor = class {
|
|
|
131
148
|
domainAllowList: config.domainAllowList,
|
|
132
149
|
maxRequestBodySize: config.maxRequestBodySize,
|
|
133
150
|
maxResponseBodySize: config.maxResponseBodySize,
|
|
134
|
-
exportTraceUrl:
|
|
151
|
+
exportTraceUrl: this.exporterTraceUrl
|
|
135
152
|
});
|
|
136
153
|
logger$2.info("Initialized PingopsSpanProcessor", {
|
|
137
154
|
baseUrl: config.baseUrl,
|
|
@@ -192,6 +209,14 @@ var PingopsSpanProcessor = class {
|
|
|
192
209
|
}
|
|
193
210
|
const attributes = span.attributes;
|
|
194
211
|
const url$1 = (0, _pingops_core.getHttpUrlFromAttributes)(attributes) ?? "";
|
|
212
|
+
if (url$1 && isExporterRequestUrl$1(url$1, this.exporterTraceUrl)) {
|
|
213
|
+
logger$2.debug("Skipping exporter span to prevent self-instrumentation", {
|
|
214
|
+
spanName: span.name,
|
|
215
|
+
spanId: spanContext.spanId,
|
|
216
|
+
url: url$1
|
|
217
|
+
});
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
195
220
|
logger$2.debug("Extracted URL for domain filtering", {
|
|
196
221
|
spanName: span.name,
|
|
197
222
|
url: url$1,
|
|
@@ -363,9 +388,12 @@ async function shutdownTracerProvider() {
|
|
|
363
388
|
//#region src/instrumentations/suppression-guard.ts
|
|
364
389
|
const logger = (0, _pingops_core.createLogger)("[PingOps SuppressionGuard]");
|
|
365
390
|
let hasLoggedSuppressionLeakWarning = false;
|
|
366
|
-
function
|
|
391
|
+
function normalizePath(pathname) {
|
|
392
|
+
return pathname.replace(/\/+/g, "/").replace(/\/$/, "");
|
|
393
|
+
}
|
|
394
|
+
function parseUrl(url$1) {
|
|
367
395
|
try {
|
|
368
|
-
return new URL(url$1)
|
|
396
|
+
return new URL(url$1);
|
|
369
397
|
} catch {
|
|
370
398
|
return null;
|
|
371
399
|
}
|
|
@@ -374,10 +402,20 @@ function isExporterRequestUrl(requestUrl) {
|
|
|
374
402
|
if (!requestUrl) return false;
|
|
375
403
|
const exporterUrl = getGlobalConfig()?.exportTraceUrl;
|
|
376
404
|
if (!exporterUrl) return false;
|
|
377
|
-
const
|
|
378
|
-
const
|
|
379
|
-
if (!
|
|
380
|
-
|
|
405
|
+
const parsedRequestUrl = parseUrl(requestUrl);
|
|
406
|
+
const parsedExporterUrl = parseUrl(exporterUrl);
|
|
407
|
+
if (!parsedRequestUrl || !parsedExporterUrl) return false;
|
|
408
|
+
if (parsedRequestUrl.origin !== parsedExporterUrl.origin) return false;
|
|
409
|
+
const requestPath = normalizePath(parsedRequestUrl.pathname);
|
|
410
|
+
const exporterPath = normalizePath(parsedExporterUrl.pathname);
|
|
411
|
+
return requestPath === exporterPath || requestPath.startsWith(`${exporterPath}/`);
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Determines whether an outbound request should be skipped from instrumentation
|
|
415
|
+
* to prevent exporter self-instrumentation loops.
|
|
416
|
+
*/
|
|
417
|
+
function shouldIgnoreOutboundInstrumentation(requestUrl) {
|
|
418
|
+
return isExporterRequestUrl(requestUrl);
|
|
381
419
|
}
|
|
382
420
|
/**
|
|
383
421
|
* Returns a context for outbound span creation that neutralizes leaked suppression
|
|
@@ -645,6 +683,14 @@ var PingopsHttpInstrumentation = class extends _opentelemetry_instrumentation_ht
|
|
|
645
683
|
/**
|
|
646
684
|
* HTTP instrumentation for OpenTelemetry
|
|
647
685
|
*/
|
|
686
|
+
function toRequestUrl$1(request) {
|
|
687
|
+
if (typeof request.href === "string" && request.href.length > 0) return request.href;
|
|
688
|
+
const protocol = typeof request.protocol === "string" && request.protocol.length > 0 ? request.protocol : "http:";
|
|
689
|
+
const hostnameOrHost = typeof request.hostname === "string" && request.hostname.length > 0 ? request.hostname : request.host;
|
|
690
|
+
if (!hostnameOrHost) return;
|
|
691
|
+
const hasPortInHost = hostnameOrHost.includes(":");
|
|
692
|
+
return `${protocol}//${hostnameOrHost}${request.port != null && !hasPortInHost ? `:${request.port}` : ""}${typeof request.path === "string" ? request.path : typeof request.pathname === "string" ? request.pathname : "/"}`;
|
|
693
|
+
}
|
|
648
694
|
/**
|
|
649
695
|
* Creates an HTTP instrumentation instance.
|
|
650
696
|
* All outgoing HTTP requests are instrumented when the SDK is initialized.
|
|
@@ -654,12 +700,16 @@ var PingopsHttpInstrumentation = class extends _opentelemetry_instrumentation_ht
|
|
|
654
700
|
*/
|
|
655
701
|
function createHttpInstrumentation(config) {
|
|
656
702
|
const globalConfig$1 = getGlobalConfig();
|
|
703
|
+
const userIgnoreOutgoingRequestHook = config?.ignoreOutgoingRequestHook;
|
|
657
704
|
return new PingopsHttpInstrumentation({
|
|
705
|
+
...config,
|
|
658
706
|
ignoreIncomingRequestHook: () => true,
|
|
659
|
-
ignoreOutgoingRequestHook: () =>
|
|
707
|
+
ignoreOutgoingRequestHook: (request) => {
|
|
708
|
+
if (shouldIgnoreOutboundInstrumentation(toRequestUrl$1(request))) return true;
|
|
709
|
+
return userIgnoreOutgoingRequestHook?.(request) ?? false;
|
|
710
|
+
},
|
|
660
711
|
maxRequestBodySize: globalConfig$1?.maxRequestBodySize,
|
|
661
|
-
maxResponseBodySize: globalConfig$1?.maxResponseBodySize
|
|
662
|
-
...config
|
|
712
|
+
maxResponseBodySize: globalConfig$1?.maxResponseBodySize
|
|
663
713
|
});
|
|
664
714
|
}
|
|
665
715
|
|
|
@@ -1038,6 +1088,13 @@ var UndiciInstrumentation = class extends _opentelemetry_instrumentation.Instrum
|
|
|
1038
1088
|
/**
|
|
1039
1089
|
* Undici instrumentation for OpenTelemetry
|
|
1040
1090
|
*/
|
|
1091
|
+
function toRequestUrl(request) {
|
|
1092
|
+
try {
|
|
1093
|
+
return new URL(request.path, request.origin).toString();
|
|
1094
|
+
} catch {
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1041
1098
|
/**
|
|
1042
1099
|
* Creates an Undici instrumentation instance.
|
|
1043
1100
|
* All requests are instrumented when the SDK is initialized.
|
|
@@ -1048,7 +1105,9 @@ function createUndiciInstrumentation() {
|
|
|
1048
1105
|
const globalConfig$1 = getGlobalConfig();
|
|
1049
1106
|
return new UndiciInstrumentation({
|
|
1050
1107
|
enabled: true,
|
|
1051
|
-
ignoreRequestHook: () =>
|
|
1108
|
+
ignoreRequestHook: (request) => {
|
|
1109
|
+
return shouldIgnoreOutboundInstrumentation(toRequestUrl(request));
|
|
1110
|
+
},
|
|
1052
1111
|
maxRequestBodySize: globalConfig$1?.maxRequestBodySize,
|
|
1053
1112
|
maxResponseBodySize: globalConfig$1?.maxResponseBodySize
|
|
1054
1113
|
});
|