-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathdropMiddlewareTunnelRequests.ts
More file actions
95 lines (80 loc) · 3.1 KB
/
dropMiddlewareTunnelRequests.ts
File metadata and controls
95 lines (80 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';
import {
getClient,
GLOBAL_OBJ,
isSentryRequestUrl,
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
type Span,
type SpanAttributes,
} from '@sentry/core';
import { ATTR_NEXT_SPAN_TYPE } from '../nextSpanAttributes';
import { TRANSACTION_ATTR_SHOULD_DROP_TRANSACTION } from '../span-attributes-with-logic-attached';
const globalWithInjectedValues = GLOBAL_OBJ as typeof GLOBAL_OBJ & {
_sentryRewritesTunnelPath?: string;
};
/**
* Drops spans for tunnel requests from middleware or fetch instrumentation.
* This catches both:
* 1. Requests to the local tunnel route (before rewrite)
* 2. Requests to Sentry ingest (after rewrite)
*/
export function dropMiddlewareTunnelRequests(span: Span, attrs: SpanAttributes | undefined): void {
// Only filter middleware spans or HTTP fetch spans
const isMiddleware = attrs?.[ATTR_NEXT_SPAN_TYPE] === 'Middleware.execute';
// The fetch span could be originating from rewrites re-writing a tunnel request
// So we want to filter it out
const isFetchSpan = attrs?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] === 'auto.http.otel.node_fetch';
// If the span is not a middleware span or a fetch span, return
if (!isMiddleware && !isFetchSpan) {
return;
}
// Check if this is either a tunnel route request or a Sentry ingest request
const isTunnel = isTunnelRouteSpan(attrs || {});
const isSentry = isSentryRequestSpan(span);
if (isTunnel || isSentry) {
// Mark the span to be dropped
span.setAttribute(TRANSACTION_ATTR_SHOULD_DROP_TRANSACTION, true);
}
}
/**
* Local copy of `@sentry/opentelemetry`'s `isSentryRequestSpan`, to avoid pulling the whole package into Edge bundles.
*/
function isSentryRequestSpan(span: Span): boolean {
const attributes = spanToAttributes(span);
if (!attributes) {
return false;
}
const httpUrl = attributes['http.url'] || attributes['url.full'];
if (!httpUrl) {
return false;
}
return isSentryRequestUrl(httpUrl.toString(), getClient());
}
function spanToAttributes(span: Span): Record<string, unknown> | undefined {
// OTEL spans expose attributes in different shapes depending on implementation.
// We only need best-effort read access.
type MaybeSpanAttributes = {
attributes?: Record<string, unknown>;
_attributes?: Record<string, unknown>;
};
const maybeSpan = span as unknown as MaybeSpanAttributes;
const attrs = maybeSpan.attributes || maybeSpan._attributes;
return attrs;
}
/**
* Checks if a span's HTTP target matches the tunnel route.
*/
function isTunnelRouteSpan(spanAttributes: Record<string, unknown>): boolean {
const tunnelPath = globalWithInjectedValues._sentryRewritesTunnelPath || process.env._sentryRewritesTunnelPath;
if (!tunnelPath) {
return false;
}
// eslint-disable-next-line deprecation/deprecation
const httpTarget = spanAttributes[SEMATTRS_HTTP_TARGET];
if (typeof httpTarget === 'string') {
// Extract pathname from the target (e.g., "/tunnel?o=123&p=456" -> "/tunnel")
const pathname = httpTarget.split('?')[0] || '';
return pathname.startsWith(tunnelPath);
}
return false;
}