1- import type { Span } from '@sentry/core' ;
2- import {
3- SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ,
4- SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ,
5- getActiveSpan ,
6- getCurrentScope ,
7- getDefaultIsolationScope ,
8- getIsolationScope ,
9- getTraceMetaTags ,
10- logger ,
11- setHttpStatus ,
12- startSpan ,
13- winterCGRequestToRequestData ,
14- withIsolationScope ,
15- } from '@sentry/core' ;
161import { continueTrace } from '@sentry/node' ;
17- import type { Handle , ResolveOptions } from '@sveltejs/kit' ;
2+ import type { Handle } from '@sveltejs/kit' ;
183
19- import { DEBUG_BUILD } from '../common/debug-build' ;
20- import { flushIfServerless , getTracePropagationData , sendErrorToSentry } from './utils' ;
21-
22- export type SentryHandleOptions = {
23- /**
24- * Controls whether the SDK should capture errors and traces in requests that don't belong to a
25- * route defined in your SvelteKit application.
26- *
27- * By default, this option is set to `false` to reduce noise (e.g. bots sending random requests to your server).
28- *
29- * Set this option to `true` if you want to monitor requests events without a route. This might be useful in certain
30- * scenarios, for instance if you registered other handlers that handle these requests.
31- * If you set this option, you might want adjust the the transaction name in the `beforeSendTransaction`
32- * callback of your server-side `Sentry.init` options. You can also use `beforeSendTransaction` to filter out
33- * transactions that you still don't want to be sent to Sentry.
34- *
35- * @default false
36- */
37- handleUnknownRoutes ?: boolean ;
38-
39- /**
40- * Controls if `sentryHandle` should inject a script tag into the page that enables instrumentation
41- * of `fetch` calls in `load` functions.
42- *
43- * @default true
44- */
45- injectFetchProxyScript ?: boolean ;
46-
47- /**
48- * If this option is set, the `sentryHandle` handler will add a nonce attribute to the script
49- * tag it injects into the page. This script is used to enable instrumentation of `fetch` calls
50- * in `load` functions.
51- *
52- * Use this if your CSP policy blocks the fetch proxy script injected by `sentryHandle`.
53- */
54- fetchProxyScriptNonce ?: string ;
55- } ;
56-
57- /**
58- * Exported only for testing
59- */
60- export const FETCH_PROXY_SCRIPT = `
61- const f = window.fetch;
62- if(f){
63- window._sentryFetchProxy = function(...a){return f(...a)}
64- window.fetch = function(...a){return window._sentryFetchProxy(...a)}
65- }
66- ` ;
67-
68- /**
69- * Adds Sentry tracing <meta> tags to the returned html page.
70- * Adds Sentry fetch proxy script to the returned html page if enabled in options.
71- * Also adds a nonce attribute to the script tag if users specified one for CSP.
72- *
73- * Exported only for testing
74- */
75- export function addSentryCodeToPage ( options : SentryHandleOptions ) : NonNullable < ResolveOptions [ 'transformPageChunk' ] > {
76- const { fetchProxyScriptNonce, injectFetchProxyScript } = options ;
77- // if injectFetchProxyScript is not set, we default to true
78- const shouldInjectScript = injectFetchProxyScript !== false ;
79- const nonce = fetchProxyScriptNonce ? `nonce="${ fetchProxyScriptNonce } "` : '' ;
80-
81- return ( { html } ) => {
82- const metaTags = getTraceMetaTags ( ) ;
83- const headWithMetaTags = metaTags ? `<head>\n${ metaTags } ` : '<head>' ;
84-
85- const headWithFetchScript = shouldInjectScript ? `\n<script ${ nonce } >${ FETCH_PROXY_SCRIPT } </script>` : '' ;
86-
87- const modifiedHead = `${ headWithMetaTags } ${ headWithFetchScript } ` ;
88-
89- return html . replace ( '<head>' , modifiedHead ) ;
90- } ;
91- }
4+ import { sentryHandleGeneric , SentryHandleOptions } from '../server-common/handle' ;
925
936/**
947 * A SvelteKit handle function that wraps the request for Sentry error and
@@ -106,87 +19,7 @@ export function addSentryCodeToPage(options: SentryHandleOptions): NonNullable<R
10619 * ```
10720 */
10821export function sentryHandle ( handlerOptions ?: SentryHandleOptions ) : Handle {
109- const options = {
110- handleUnknownRoutes : false ,
111- injectFetchProxyScript : true ,
112- ...handlerOptions ,
113- } ;
114-
115- const sentryRequestHandler : Handle = input => {
116- // event.isSubRequest was added in SvelteKit 1.21.0 and we can use it to check
117- // if we should create a new execution context or not.
118- // In case of a same-origin `fetch` call within a server`load` function,
119- // SvelteKit will actually just re-enter the `handle` function and set `isSubRequest`
120- // to `true` so that no additional network call is made.
121- // We want the `http.server` span of that nested call to be a child span of the
122- // currently active span instead of a new root span to correctly reflect this
123- // behavior.
124- // As a fallback for Kit < 1.21.0, we check if there is an active span only if there's none,
125- // we create a new execution context.
126- const isSubRequest = typeof input . event . isSubRequest === 'boolean' ? input . event . isSubRequest : ! ! getActiveSpan ( ) ;
127-
128- if ( isSubRequest ) {
129- return instrumentHandle ( input , options ) ;
130- }
131-
132- return withIsolationScope ( isolationScope => {
133- // We only call continueTrace in the initial top level request to avoid
134- // creating a new root span for the sub request.
135- isolationScope . setSDKProcessingMetadata ( {
136- normalizedRequest : winterCGRequestToRequestData ( input . event . request . clone ( ) ) ,
137- } ) ;
138- return continueTrace ( getTracePropagationData ( input . event ) , ( ) => instrumentHandle ( input , options ) ) ;
139- } ) ;
140- } ;
22+ const sentryRequestHandler = sentryHandleGeneric ( continueTrace , handlerOptions ) ;
14123
14224 return sentryRequestHandler ;
14325}
144-
145- async function instrumentHandle (
146- { event, resolve } : Parameters < Handle > [ 0 ] ,
147- options : SentryHandleOptions ,
148- ) : Promise < Response > {
149- if ( ! event . route ?. id && ! options . handleUnknownRoutes ) {
150- return resolve ( event ) ;
151- }
152-
153- const routeName = `${ event . request . method } ${ event . route ?. id || event . url . pathname } ` ;
154-
155- if ( getIsolationScope ( ) !== getDefaultIsolationScope ( ) ) {
156- getIsolationScope ( ) . setTransactionName ( routeName ) ;
157- } else {
158- DEBUG_BUILD && logger . warn ( 'Isolation scope is default isolation scope - skipping setting transactionName' ) ;
159- }
160-
161- try {
162- const resolveResult = await startSpan (
163- {
164- op : 'http.server' ,
165- attributes : {
166- [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.http.sveltekit' ,
167- [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] : event . route ?. id ? 'route' : 'url' ,
168- 'http.method' : event . request . method ,
169- } ,
170- name : routeName ,
171- } ,
172- async ( span ?: Span ) => {
173- getCurrentScope ( ) . setSDKProcessingMetadata ( {
174- normalizedRequest : winterCGRequestToRequestData ( event . request . clone ( ) ) ,
175- } ) ;
176- const res = await resolve ( event , {
177- transformPageChunk : addSentryCodeToPage ( options ) ,
178- } ) ;
179- if ( span ) {
180- setHttpStatus ( span , res . status ) ;
181- }
182- return res ;
183- } ,
184- ) ;
185- return resolveResult ;
186- } catch ( e : unknown ) {
187- sendErrorToSentry ( e , 'handle' ) ;
188- throw e ;
189- } finally {
190- await flushIfServerless ( ) ;
191- }
192- }
0 commit comments