-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathindex.ts
More file actions
159 lines (135 loc) · 5.91 KB
/
Copy pathindex.ts
File metadata and controls
159 lines (135 loc) · 5.91 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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import { context } from '@opentelemetry/api';
import {
applySdkMetadata,
getCapturedScopesOnSpan,
getCurrentScope,
getGlobalScope,
getIsolationScope,
getRootSpan,
GLOBAL_OBJ,
registerSpanErrorInstrumentation,
SEMANTIC_ATTRIBUTE_SENTRY_OP,
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
setCapturedScopesOnSpan,
spanToJSON,
stripUrlQueryAndFragment,
vercelWaitUntil,
} from '@sentry/core';
import { getScopesFromContext } from '@sentry/opentelemetry';
import type { VercelEdgeOptions } from '@sentry/vercel-edge';
import { getDefaultIntegrations, init as vercelEdgeInit } from '@sentry/vercel-edge';
import { addHeadersAsAttributes } from '../common/utils/addHeadersAsAttributes';
import { isBuild } from '../common/utils/isBuild';
import { flushSafelyWithTimeout } from '../common/utils/responseEnd';
import { setUrlProcessingMetadata } from '../common/utils/setUrlProcessingMetadata';
import { distDirRewriteFramesIntegration } from './distDirRewriteFramesIntegration';
export * from '@sentry/vercel-edge';
export * from '../common';
export { captureUnderscoreErrorException } from '../common/pages-router-instrumentation/_error';
// Override core span methods with Next.js-specific implementations that support Cache Components
export { startSpan, startSpanManual, startInactiveSpan } from '../common/utils/nextSpan';
export { wrapApiHandlerWithSentry } from './wrapApiHandlerWithSentry';
export type EdgeOptions = VercelEdgeOptions;
const globalWithInjectedValues = GLOBAL_OBJ as typeof GLOBAL_OBJ & {
_sentryRewriteFramesDistDir?: string;
_sentryRelease?: string;
};
/** Inits the Sentry NextJS SDK on the Edge Runtime. */
export function init(options: VercelEdgeOptions = {}): void {
registerSpanErrorInstrumentation();
if (isBuild()) {
return;
}
const customDefaultIntegrations = getDefaultIntegrations(options);
// This value is injected at build time, based on the output directory specified in the build config. Though a default
// is set there, we set it here as well, just in case something has gone wrong with the injection.
const distDirName = process.env._sentryRewriteFramesDistDir || globalWithInjectedValues._sentryRewriteFramesDistDir;
if (distDirName) {
customDefaultIntegrations.push(distDirRewriteFramesIntegration({ distDirName }));
}
const opts = {
defaultIntegrations: customDefaultIntegrations,
release: process.env._sentryRelease || globalWithInjectedValues._sentryRelease,
...options,
};
applySdkMetadata(opts, 'nextjs', ['nextjs', 'vercel-edge']);
const client = vercelEdgeInit(opts);
client?.on('spanStart', span => {
const spanAttributes = spanToJSON(span).data;
const rootSpan = getRootSpan(span);
const isRootSpan = span === rootSpan;
// Mark all spans generated by Next.js as 'auto'
if (spanAttributes?.['next.span_type'] !== undefined) {
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto');
}
// Make sure middleware spans get the right op
if (spanAttributes?.['next.span_type'] === 'Middleware.execute') {
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.server.middleware');
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'url');
if (isRootSpan) {
// Fork isolation scope for middleware requests
const scopes = getCapturedScopesOnSpan(span);
const isolationScope = (scopes.isolationScope || getIsolationScope()).clone();
const scope = scopes.scope || getCurrentScope();
const currentScopesPointer = getScopesFromContext(context.active());
if (currentScopesPointer) {
currentScopesPointer.isolationScope = isolationScope;
}
setCapturedScopesOnSpan(span, scope, isolationScope);
}
}
if (isRootSpan) {
// todo: check if we can set request headers for edge on sdkProcessingMetadata
const headers = getIsolationScope().getScopeData().sdkProcessingMetadata?.normalizedRequest?.headers;
addHeadersAsAttributes(headers, rootSpan);
}
});
// Use the preprocessEvent hook instead of an event processor, so that the users event processors receive the most
// up-to-date value, but also so that the logic that detects changes to the transaction names to set the source to
// "custom", doesn't trigger.
client?.on('preprocessEvent', event => {
// The otel auto inference will clobber the transaction name because the span has an http.target
if (
event.type === 'transaction' &&
event.contexts?.trace?.data?.['next.span_type'] === 'Middleware.execute' &&
event.contexts?.trace?.data?.['next.span_name']
) {
if (event.transaction) {
// Older nextjs versions pass the full url appended to the middleware name, which results in high cardinality transaction names.
// We want to remove the url from the name here.
const spanName = event.contexts.trace.data['next.span_name'];
if (typeof spanName === 'string') {
const match = spanName.match(/^middleware (GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)/);
if (match) {
const normalizedName = `middleware ${match[1]}`;
event.transaction = normalizedName;
} else {
event.transaction = stripUrlQueryAndFragment(event.contexts.trace.data['next.span_name']);
}
}
}
}
setUrlProcessingMetadata(event);
});
client?.on('spanEnd', span => {
if (span === getRootSpan(span)) {
vercelWaitUntil(flushSafelyWithTimeout());
}
});
try {
// @ts-expect-error `process.turbopack` is a magic string that will be replaced by Next.js
if (process.turbopack) {
getGlobalScope().setTag('turbopack', true);
}
} catch {
// Noop
// The statement above can throw because process is not defined on the client
}
}
/**
* Just a passthrough in case this is imported from the client.
*/
export function withSentryConfig<T>(exportedUserNextConfig: T): T {
return exportedUserNextConfig;
}