@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.mjs
CHANGED
|
@@ -32,6 +32,21 @@ function getGlobalConfig() {
|
|
|
32
32
|
//#endregion
|
|
33
33
|
//#region src/span-processor.ts
|
|
34
34
|
const logger$2 = createLogger("[PingOps Processor]");
|
|
35
|
+
function normalizePath$1(pathname) {
|
|
36
|
+
return pathname.replace(/\/+/g, "/").replace(/\/$/, "");
|
|
37
|
+
}
|
|
38
|
+
function isExporterRequestUrl$1(url, exporterUrl) {
|
|
39
|
+
try {
|
|
40
|
+
const request = new URL(url);
|
|
41
|
+
const exporter = new URL(exporterUrl);
|
|
42
|
+
if (request.origin !== exporter.origin) return false;
|
|
43
|
+
const requestPath = normalizePath$1(request.pathname);
|
|
44
|
+
const exporterPath = normalizePath$1(exporter.pathname);
|
|
45
|
+
return requestPath === exporterPath || requestPath.startsWith(`${exporterPath}/`);
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
35
50
|
/**
|
|
36
51
|
* Creates a filtered span wrapper that applies header filtering to attributes
|
|
37
52
|
*
|
|
@@ -65,6 +80,7 @@ function createFilteredSpan(span, domainAllowList, globalHeadersAllowList, globa
|
|
|
65
80
|
*/
|
|
66
81
|
var PingopsSpanProcessor = class {
|
|
67
82
|
processor;
|
|
83
|
+
exporterTraceUrl;
|
|
68
84
|
config;
|
|
69
85
|
/**
|
|
70
86
|
* Creates a new PingopsSpanProcessor instance.
|
|
@@ -74,8 +90,9 @@ var PingopsSpanProcessor = class {
|
|
|
74
90
|
constructor(config) {
|
|
75
91
|
const exportMode = config.exportMode ?? "batched";
|
|
76
92
|
const apiKey = config.apiKey || process.env.PINGOPS_API_KEY || "";
|
|
93
|
+
this.exporterTraceUrl = `${config.baseUrl}/v1/traces`;
|
|
77
94
|
const exporter = new OTLPTraceExporter({
|
|
78
|
-
url:
|
|
95
|
+
url: this.exporterTraceUrl,
|
|
79
96
|
headers: {
|
|
80
97
|
Authorization: apiKey ? `Bearer ${apiKey}` : "",
|
|
81
98
|
"Content-Type": "application/json"
|
|
@@ -103,7 +120,7 @@ var PingopsSpanProcessor = class {
|
|
|
103
120
|
domainAllowList: config.domainAllowList,
|
|
104
121
|
maxRequestBodySize: config.maxRequestBodySize,
|
|
105
122
|
maxResponseBodySize: config.maxResponseBodySize,
|
|
106
|
-
exportTraceUrl:
|
|
123
|
+
exportTraceUrl: this.exporterTraceUrl
|
|
107
124
|
});
|
|
108
125
|
logger$2.info("Initialized PingopsSpanProcessor", {
|
|
109
126
|
baseUrl: config.baseUrl,
|
|
@@ -164,6 +181,14 @@ var PingopsSpanProcessor = class {
|
|
|
164
181
|
}
|
|
165
182
|
const attributes = span.attributes;
|
|
166
183
|
const url = getHttpUrlFromAttributes(attributes) ?? "";
|
|
184
|
+
if (url && isExporterRequestUrl$1(url, this.exporterTraceUrl)) {
|
|
185
|
+
logger$2.debug("Skipping exporter span to prevent self-instrumentation", {
|
|
186
|
+
spanName: span.name,
|
|
187
|
+
spanId: spanContext.spanId,
|
|
188
|
+
url
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
167
192
|
logger$2.debug("Extracted URL for domain filtering", {
|
|
168
193
|
spanName: span.name,
|
|
169
194
|
url,
|
|
@@ -335,9 +360,12 @@ async function shutdownTracerProvider() {
|
|
|
335
360
|
//#region src/instrumentations/suppression-guard.ts
|
|
336
361
|
const logger = createLogger("[PingOps SuppressionGuard]");
|
|
337
362
|
let hasLoggedSuppressionLeakWarning = false;
|
|
338
|
-
function
|
|
363
|
+
function normalizePath(pathname) {
|
|
364
|
+
return pathname.replace(/\/+/g, "/").replace(/\/$/, "");
|
|
365
|
+
}
|
|
366
|
+
function parseUrl(url) {
|
|
339
367
|
try {
|
|
340
|
-
return new URL(url)
|
|
368
|
+
return new URL(url);
|
|
341
369
|
} catch {
|
|
342
370
|
return null;
|
|
343
371
|
}
|
|
@@ -346,10 +374,20 @@ function isExporterRequestUrl(requestUrl) {
|
|
|
346
374
|
if (!requestUrl) return false;
|
|
347
375
|
const exporterUrl = getGlobalConfig()?.exportTraceUrl;
|
|
348
376
|
if (!exporterUrl) return false;
|
|
349
|
-
const
|
|
350
|
-
const
|
|
351
|
-
if (!
|
|
352
|
-
|
|
377
|
+
const parsedRequestUrl = parseUrl(requestUrl);
|
|
378
|
+
const parsedExporterUrl = parseUrl(exporterUrl);
|
|
379
|
+
if (!parsedRequestUrl || !parsedExporterUrl) return false;
|
|
380
|
+
if (parsedRequestUrl.origin !== parsedExporterUrl.origin) return false;
|
|
381
|
+
const requestPath = normalizePath(parsedRequestUrl.pathname);
|
|
382
|
+
const exporterPath = normalizePath(parsedExporterUrl.pathname);
|
|
383
|
+
return requestPath === exporterPath || requestPath.startsWith(`${exporterPath}/`);
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Determines whether an outbound request should be skipped from instrumentation
|
|
387
|
+
* to prevent exporter self-instrumentation loops.
|
|
388
|
+
*/
|
|
389
|
+
function shouldIgnoreOutboundInstrumentation(requestUrl) {
|
|
390
|
+
return isExporterRequestUrl(requestUrl);
|
|
353
391
|
}
|
|
354
392
|
/**
|
|
355
393
|
* Returns a context for outbound span creation that neutralizes leaked suppression
|
|
@@ -617,6 +655,14 @@ var PingopsHttpInstrumentation = class extends HttpInstrumentation {
|
|
|
617
655
|
/**
|
|
618
656
|
* HTTP instrumentation for OpenTelemetry
|
|
619
657
|
*/
|
|
658
|
+
function toRequestUrl$1(request) {
|
|
659
|
+
if (typeof request.href === "string" && request.href.length > 0) return request.href;
|
|
660
|
+
const protocol = typeof request.protocol === "string" && request.protocol.length > 0 ? request.protocol : "http:";
|
|
661
|
+
const hostnameOrHost = typeof request.hostname === "string" && request.hostname.length > 0 ? request.hostname : request.host;
|
|
662
|
+
if (!hostnameOrHost) return;
|
|
663
|
+
const hasPortInHost = hostnameOrHost.includes(":");
|
|
664
|
+
return `${protocol}//${hostnameOrHost}${request.port != null && !hasPortInHost ? `:${request.port}` : ""}${typeof request.path === "string" ? request.path : typeof request.pathname === "string" ? request.pathname : "/"}`;
|
|
665
|
+
}
|
|
620
666
|
/**
|
|
621
667
|
* Creates an HTTP instrumentation instance.
|
|
622
668
|
* All outgoing HTTP requests are instrumented when the SDK is initialized.
|
|
@@ -626,12 +672,16 @@ var PingopsHttpInstrumentation = class extends HttpInstrumentation {
|
|
|
626
672
|
*/
|
|
627
673
|
function createHttpInstrumentation(config) {
|
|
628
674
|
const globalConfig$1 = getGlobalConfig();
|
|
675
|
+
const userIgnoreOutgoingRequestHook = config?.ignoreOutgoingRequestHook;
|
|
629
676
|
return new PingopsHttpInstrumentation({
|
|
677
|
+
...config,
|
|
630
678
|
ignoreIncomingRequestHook: () => true,
|
|
631
|
-
ignoreOutgoingRequestHook: () =>
|
|
679
|
+
ignoreOutgoingRequestHook: (request) => {
|
|
680
|
+
if (shouldIgnoreOutboundInstrumentation(toRequestUrl$1(request))) return true;
|
|
681
|
+
return userIgnoreOutgoingRequestHook?.(request) ?? false;
|
|
682
|
+
},
|
|
632
683
|
maxRequestBodySize: globalConfig$1?.maxRequestBodySize,
|
|
633
|
-
maxResponseBodySize: globalConfig$1?.maxResponseBodySize
|
|
634
|
-
...config
|
|
684
|
+
maxResponseBodySize: globalConfig$1?.maxResponseBodySize
|
|
635
685
|
});
|
|
636
686
|
}
|
|
637
687
|
|
|
@@ -1010,6 +1060,13 @@ var UndiciInstrumentation = class extends InstrumentationBase {
|
|
|
1010
1060
|
/**
|
|
1011
1061
|
* Undici instrumentation for OpenTelemetry
|
|
1012
1062
|
*/
|
|
1063
|
+
function toRequestUrl(request) {
|
|
1064
|
+
try {
|
|
1065
|
+
return new URL(request.path, request.origin).toString();
|
|
1066
|
+
} catch {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1013
1070
|
/**
|
|
1014
1071
|
* Creates an Undici instrumentation instance.
|
|
1015
1072
|
* All requests are instrumented when the SDK is initialized.
|
|
@@ -1020,7 +1077,9 @@ function createUndiciInstrumentation() {
|
|
|
1020
1077
|
const globalConfig$1 = getGlobalConfig();
|
|
1021
1078
|
return new UndiciInstrumentation({
|
|
1022
1079
|
enabled: true,
|
|
1023
|
-
ignoreRequestHook: () =>
|
|
1080
|
+
ignoreRequestHook: (request) => {
|
|
1081
|
+
return shouldIgnoreOutboundInstrumentation(toRequestUrl(request));
|
|
1082
|
+
},
|
|
1024
1083
|
maxRequestBodySize: globalConfig$1?.maxRequestBodySize,
|
|
1025
1084
|
maxResponseBodySize: globalConfig$1?.maxResponseBodySize
|
|
1026
1085
|
});
|