@@ -83,42 +83,42 @@ npx react-router reveal
83
83
84
84
Initialize the Sentry React SDK in your ` entry.client.tsx ` file:
85
85
86
- ``` tsx {filename: entry.client.tsx}
87
- import * as Sentry from " @sentry/react-router" ;
88
- import { startTransition , StrictMode } from " react" ;
89
- import { hydrateRoot } from " react-dom/client" ;
90
- import { HydratedRouter } from " react-router/dom" ;
91
-
92
- Sentry .init ({
93
- dsn: " ___PUBLIC_DSN___" ,
94
-
95
- // Adds request headers and IP for users, for more info visit:
96
- // https://docs.sentry.io/platforms/javascript/guides/react-router/configuration/options/#sendDefaultPii
97
- sendDefaultPii: true ,
98
-
99
- integrations: [
100
- // ___PRODUCT_OPTION_START___ performance
101
- Sentry .browserTracingIntegration (),
102
- // ___PRODUCT_OPTION_END___ performance
103
- // ___PRODUCT_OPTION_START___ session-replay
104
- Sentry .replayIntegration (),
105
- // ___PRODUCT_OPTION_END___ session-replay
106
- ],
107
- // ___PRODUCT_OPTION_START___ performance
108
-
109
- tracesSampleRate: 1.0 , // Capture 100% of the transactions
110
-
111
- // Set `tracePropagationTargets` to declare which URL(s) should have trace propagation enabled
112
- tracePropagationTargets: [/ ^ \/ / , / ^ https:\/\/ yourserver\. io\/ api/ ],
113
- // ___PRODUCT_OPTION_END___ performance
114
- // ___PRODUCT_OPTION_START___ session-replay
115
-
116
- // Capture Replay for 10% of all sessions,
117
- // plus 100% of sessions with an error
118
- replaysSessionSampleRate: 0.1 ,
119
- replaysOnErrorSampleRate: 1.0 ,
120
- // ___PRODUCT_OPTION_END___ session-replay
121
- });
86
+ ``` tsx {diff} { filename: entry.client.tsx}
87
+ + import * as Sentry from " @sentry/react-router" ;
88
+ import { startTransition , StrictMode } from " react" ;
89
+ import { hydrateRoot } from " react-dom/client" ;
90
+ import { HydratedRouter } from " react-router/dom" ;
91
+
92
+ + Sentry .init ({
93
+ + dsn: " ___PUBLIC_DSN___" ,
94
+ +
95
+ + // Adds request headers and IP for users, for more info visit:
96
+ + // https://docs.sentry.io/platforms/javascript/guides/react-router/configuration/options/#sendDefaultPii
97
+ + sendDefaultPii: true ,
98
+ +
99
+ + integrations: [
100
+ + // ___PRODUCT_OPTION_START___ performance
101
+ + Sentry .browserTracingIntegration (),
102
+ + // ___PRODUCT_OPTION_END___ performance
103
+ + // ___PRODUCT_OPTION_START___ session-replay
104
+ + Sentry .replayIntegration (),
105
+ + // ___PRODUCT_OPTION_END___ session-replay
106
+ + ],
107
+ + // ___PRODUCT_OPTION_START___ performance
108
+ +
109
+ + tracesSampleRate: 1.0 , // Capture 100% of the transactions
110
+ +
111
+ + // Set `tracePropagationTargets` to declare which URL(s) should have trace propagation enabled
112
+ + tracePropagationTargets: [/ ^ \/ / , / ^ https:\/\/ yourserver\. io\/ api/ ],
113
+ + // ___PRODUCT_OPTION_END___ performance
114
+ + // ___PRODUCT_OPTION_START___ session-replay
115
+ +
116
+ + // Capture Replay for 10% of all sessions,
117
+ + // plus 100% of sessions with an error
118
+ + replaysSessionSampleRate: 0.1 ,
119
+ + replaysOnErrorSampleRate: 1.0 ,
120
+ + // ___PRODUCT_OPTION_END___ session-replay
121
+ + });
122
122
123
123
startTransition (() => {
124
124
hydrateRoot (
@@ -133,7 +133,7 @@ startTransition(() => {
133
133
Now, update your ` app/root.tsx ` file to report any unhandled errors from your error boundary:
134
134
135
135
``` tsx {diff} {filename: app/root.tsx}
136
- import * as Sentry from " @sentry/react-router" ;
136
+ + import * as Sentry from " @sentry/react-router" ;
137
137
138
138
export function ErrorBoundary({ error }: Route .ErrorBoundaryProps ) {
139
139
let message = " Oops!" ;
@@ -199,40 +199,110 @@ Sentry.init({
199
199
});
200
200
```
201
201
202
- In your ` entry.server.tsx ` file, export the ` handleError ` function :
202
+ Update your ` entry.server.tsx ` file:
203
203
204
204
``` tsx {diff} {filename: entry.server.tsx}
205
- import * as Sentry from " @sentry/react-router" ;
206
- import { type HandleErrorFunction } from " react-router" ;
205
+ + import * as Sentry from ' @sentry/react-router' ;
206
+ import { createReadableStreamFromReadable } from ' @react-router/node' ;
207
+ import { renderToPipeableStream } from ' react-dom/server' ;
208
+ import { ServerRouter } from ' react-router' ;
209
+ import { type HandleErrorFunction } from ' react-router' ;
210
+
211
+ + const handleRequest = Sentry .createSentryHandleRequest ({
212
+ + ServerRouter ,
213
+ + renderToPipeableStream ,
214
+ + createReadableStreamFromReadable ,
215
+ +});
216
+
217
+ export default handleRequest ;
207
218
208
219
export const handleError: HandleErrorFunction = (error , { request }) => {
209
- // React Router may abort some interrupted requests, report those
220
+ // React Router may abort some interrupted requests, don't log those
210
221
if (! request .signal .aborted ) {
211
222
+ Sentry .captureException (error );
212
-
213
- // make sure to still log the error so you can see it
223
+ // optionally log the error so you can see it
214
224
console .error (error );
215
225
}
216
226
};
217
227
218
- - export default function handleRequest(
219
- + function handleRequest (
220
- request : Request ,
221
- responseStatusCode : number ,
222
- responseHeaders : Headers ,
223
- routerContext : EntryContext ,
224
- loadContext : AppLoadContext ,
225
- ) {
226
- return new Promise ((resolve , reject ) => {
227
- // ...
228
- }
229
- }
230
-
231
- + export default Sentry .sentryHandleRequest (handleRequest );
232
228
233
229
// ... rest of your server entry
234
230
```
235
231
232
+ <Expandable title = " Do you need to customize your handleRequest function?" >
233
+ If you need to update the logic of your ` handleRequest ` function you'll need to include the provided Sentry helper functions (` getMetaTagTransformer ` and ` wrapSentryHandleRequest ` ) manually:
234
+
235
+ ``` tsx {1-4, 44-45, 69-70}
236
+ import { getMetaTagTransformer , wrapSentryHandleRequest } from ' @sentry/react-router' ;
237
+ // ... other imports
238
+
239
+ const handleRequest = function handleRequest(
240
+ request : Request ,
241
+ responseStatusCode : number ,
242
+ responseHeaders : Headers ,
243
+ routerContext : EntryContext ,
244
+ _loadContext : AppLoadContext ,
245
+ ): Promise <Response > {
246
+ return new Promise ((resolve , reject ) => {
247
+ let shellRendered = false ;
248
+ const userAgent = request .headers .get (' user-agent' );
249
+
250
+ // Determine if we should use onAllReady or onShellReady
251
+ const isBot = typeof userAgent === ' string' && botRegex .test (userAgent );
252
+ const isSpaMode = !! (routerContext as { isSpaMode? : boolean }).isSpaMode ;
253
+
254
+ const readyOption = isBot || isSpaMode ? ' onAllReady' : ' onShellReady' ;
255
+
256
+ const { pipe, abort } = renderToPipeableStream (
257
+ <ServerRouter context = { routerContext } url = { request .url } />,
258
+ {
259
+ [readyOption ]() {
260
+ shellRendered = true ;
261
+ const body = new PassThrough ();
262
+
263
+ const stream = createReadableStreamFromReadable (body );
264
+
265
+ responseHeaders .set (' Content-Type' , ' text/html' );
266
+
267
+ resolve (
268
+ new Response (stream , {
269
+ headers: responseHeaders ,
270
+ status: responseStatusCode ,
271
+ }),
272
+ );
273
+
274
+ // this enables distributed tracing between client and server
275
+ pipe (getMetaTagTransformer (body ));
276
+ },
277
+ onShellError(error : unknown ) {
278
+ reject (error );
279
+ },
280
+ onError(error : unknown ) {
281
+ // eslint-disable-next-line no-param-reassign
282
+ responseStatusCode = 500 ;
283
+ // Log streaming rendering errors from inside the shell. Don't log
284
+ // errors encountered during initial shell rendering since they'll
285
+ // reject and get logged in handleDocumentRequest.
286
+ if (shellRendered ) {
287
+ // eslint-disable-next-line no-console
288
+ console .error (error );
289
+ }
290
+ },
291
+ },
292
+ );
293
+
294
+ // Abort the rendering stream after the `streamTimeout`
295
+ setTimeout (abort , streamTimeout );
296
+ });
297
+ };
298
+
299
+ // wrap the default export
300
+ export default wrapSentryHandleRequest (handleRequest );
301
+
302
+ // ... rest of your entry.server.ts file
303
+ ```
304
+ </Expandable >
305
+
236
306
### Update Scripts
237
307
238
308
Since React Router is running in ESM mode, you need to use the ` --import ` command line options to load our server-side instrumentation module before the application starts.
0 commit comments