-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(remix): Migrate to opentelemetry-instrumentation-remix
.
#12110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
a173582
feat(remix): Migrate to `opentelemetry-instrumentation-remix`.
onurtemizkan f3b8ec8
Fix linter.
onurtemizkan ca48da0
Return jest config for unit tests back.
onurtemizkan 07550da
Don't run Remix integration tests on Node 14
onurtemizkan f1e5c0c
Instrument function builds properly.
onurtemizkan ce315d3
Add ErrorBoundary propagation test.
onurtemizkan 4ec9e3a
Export a no-op function in place of `wrapExpressCreateRequestHandler`.
onurtemizkan 8419b7c
Add a custom `httpIntegration` for Remix.
onurtemizkan 21d606d
Switch to `import` pattern to load instrumentation on Express tests
onurtemizkan 4da9be8
Remove extra `Sentry.init`s in v2 e2e test app.
onurtemizkan 0799c50
Make auto-instrumentation opt-in.
onurtemizkan bd3feeb
Add legacy integration tests back.
onurtemizkan d4678b1
Fix broken request transactions.
onurtemizkan d9de72a
Use Remix `httpIntegration` for both otel and legacy instrumentations.
onurtemizkan b39929d
Add Remix v1 legacy e2e tests.
onurtemizkan 75977b7
Add Remix v2 legacy e2e tests.
onurtemizkan 17eb45a
Test error boundary rendering on v2 integration tests.
onurtemizkan 470749b
Add Remix/Express legacy e2e tests.
onurtemizkan dbb2b61
Fix passing client options.
onurtemizkan 6c642a6
Fix typo
onurtemizkan 673afef
Fix test init
onurtemizkan 6145bda
Add missing `generateInstrumentOnce` exports.
onurtemizkan e6403fa
Update JSDocs.
onurtemizkan 9fc99cd
Add missing `generateInstrumentOnce` export to @sentry/astro.
onurtemizkan 5dd340f
Switch to `.mjs` in express tests.
onurtemizkan 2cf2b91
Add test for loader span http.server connection.
onurtemizkan b44dd02
Move conditional logic inside `getRemixDefaultIntegrations`.
onurtemizkan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
dev-packages/e2e-tests/test-applications/create-remix-app-express-legacy/.eslintrc.cjs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/** | ||
* This is intended to be a basic starting point for linting in your app. | ||
* It relies on recommended configs out of the box for simplicity, but you can | ||
* and should modify this configuration to best suit your team's needs. | ||
*/ | ||
|
||
/** @type {import('eslint').Linter.Config} */ | ||
module.exports = { | ||
root: true, | ||
parserOptions: { | ||
ecmaVersion: 'latest', | ||
sourceType: 'module', | ||
ecmaFeatures: { | ||
jsx: true, | ||
}, | ||
}, | ||
env: { | ||
browser: true, | ||
commonjs: true, | ||
es6: true, | ||
}, | ||
|
||
// Base config | ||
extends: ['eslint:recommended'], | ||
|
||
overrides: [ | ||
// React | ||
{ | ||
files: ['**/*.{js,jsx,ts,tsx}'], | ||
plugins: ['react', 'jsx-a11y'], | ||
extends: [ | ||
'plugin:react/recommended', | ||
'plugin:react/jsx-runtime', | ||
'plugin:react-hooks/recommended', | ||
'plugin:jsx-a11y/recommended', | ||
], | ||
settings: { | ||
react: { | ||
version: 'detect', | ||
}, | ||
formComponents: ['Form'], | ||
linkComponents: [ | ||
{ name: 'Link', linkAttribute: 'to' }, | ||
{ name: 'NavLink', linkAttribute: 'to' }, | ||
], | ||
'import/resolver': { | ||
typescript: {}, | ||
}, | ||
}, | ||
}, | ||
|
||
// Typescript | ||
{ | ||
files: ['**/*.{ts,tsx}'], | ||
plugins: ['@typescript-eslint', 'import'], | ||
parser: '@typescript-eslint/parser', | ||
settings: { | ||
'import/internal-regex': '^~/', | ||
'import/resolver': { | ||
node: { | ||
extensions: ['.ts', '.tsx'], | ||
}, | ||
typescript: { | ||
alwaysTryTypes: true, | ||
}, | ||
}, | ||
}, | ||
extends: ['plugin:@typescript-eslint/recommended', 'plugin:import/recommended', 'plugin:import/typescript'], | ||
}, | ||
|
||
// Node | ||
{ | ||
files: ['.eslintrc.cjs', 'server.js'], | ||
env: { | ||
node: true, | ||
}, | ||
}, | ||
], | ||
}; |
6 changes: 6 additions & 0 deletions
6
dev-packages/e2e-tests/test-applications/create-remix-app-express-legacy/.gitignore
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
node_modules | ||
|
||
/.cache | ||
/build | ||
/public/build | ||
.env |
2 changes: 2 additions & 0 deletions
2
dev-packages/e2e-tests/test-applications/create-remix-app-express-legacy/.npmrc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
@sentry:registry=http://127.0.0.1:4873 | ||
@sentry-internal:registry=http://127.0.0.1:4873 |
46 changes: 46 additions & 0 deletions
46
...packages/e2e-tests/test-applications/create-remix-app-express-legacy/app/entry.client.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { RemixBrowser, useLocation, useMatches } from '@remix-run/react'; | ||
import * as Sentry from '@sentry/remix'; | ||
import { StrictMode, startTransition, useEffect } from 'react'; | ||
import { hydrateRoot } from 'react-dom/client'; | ||
|
||
Sentry.init({ | ||
environment: 'qa', // dynamic sampling bias to keep transactions | ||
dsn: window.ENV.SENTRY_DSN, | ||
integrations: [ | ||
Sentry.browserTracingIntegration({ | ||
useEffect, | ||
useLocation, | ||
useMatches, | ||
}), | ||
Sentry.replayIntegration(), | ||
], | ||
// Performance Monitoring | ||
tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production! | ||
replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production. | ||
replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur. | ||
tunnel: 'http://localhost:3031/', // proxy server | ||
}); | ||
|
||
Sentry.addEventProcessor(event => { | ||
if ( | ||
event.type === 'transaction' && | ||
(event.contexts?.trace?.op === 'pageload' || event.contexts?.trace?.op === 'navigation') | ||
) { | ||
const eventId = event.event_id; | ||
if (eventId) { | ||
window.recordedTransactions = window.recordedTransactions || []; | ||
window.recordedTransactions.push(eventId); | ||
} | ||
} | ||
|
||
return event; | ||
}); | ||
|
||
startTransition(() => { | ||
hydrateRoot( | ||
document, | ||
<StrictMode> | ||
<RemixBrowser /> | ||
</StrictMode>, | ||
); | ||
}); |
141 changes: 141 additions & 0 deletions
141
...packages/e2e-tests/test-applications/create-remix-app-express-legacy/app/entry.server.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import * as Sentry from '@sentry/remix'; | ||
|
||
import { PassThrough } from 'node:stream'; | ||
import * as isbotModule from 'isbot'; | ||
|
||
import type { AppLoadContext, EntryContext } from '@remix-run/node'; | ||
import { createReadableStreamFromReadable } from '@remix-run/node'; | ||
import { installGlobals } from '@remix-run/node'; | ||
import { RemixServer } from '@remix-run/react'; | ||
import { renderToPipeableStream } from 'react-dom/server'; | ||
|
||
installGlobals(); | ||
|
||
const ABORT_DELAY = 5_000; | ||
|
||
export const handleError = Sentry.wrapRemixHandleError; | ||
|
||
export default function handleRequest( | ||
request: Request, | ||
responseStatusCode: number, | ||
responseHeaders: Headers, | ||
remixContext: EntryContext, | ||
loadContext: AppLoadContext, | ||
) { | ||
return isBotRequest(request.headers.get('user-agent')) | ||
? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext) | ||
: handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext); | ||
} | ||
|
||
// We have some Remix apps in the wild already running with isbot@3 so we need | ||
// to maintain backwards compatibility even though we want new apps to use | ||
// isbot@4. That way, we can ship this as a minor Semver update to @remix-run/dev. | ||
function isBotRequest(userAgent: string | null) { | ||
if (!userAgent) { | ||
return false; | ||
} | ||
|
||
// isbot >= 3.8.0, >4 | ||
if ('isbot' in isbotModule && typeof isbotModule.isbot === 'function') { | ||
return isbotModule.isbot(userAgent); | ||
} | ||
|
||
// isbot < 3.8.0 | ||
if ('default' in isbotModule && typeof isbotModule.default === 'function') { | ||
return isbotModule.default(userAgent); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
function handleBotRequest( | ||
request: Request, | ||
responseStatusCode: number, | ||
responseHeaders: Headers, | ||
remixContext: EntryContext, | ||
) { | ||
return new Promise((resolve, reject) => { | ||
let shellRendered = false; | ||
const { pipe, abort } = renderToPipeableStream( | ||
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />, | ||
{ | ||
onAllReady() { | ||
shellRendered = true; | ||
const body = new PassThrough(); | ||
const stream = createReadableStreamFromReadable(body); | ||
|
||
responseHeaders.set('Content-Type', 'text/html'); | ||
|
||
resolve( | ||
new Response(stream, { | ||
headers: responseHeaders, | ||
status: responseStatusCode, | ||
}), | ||
); | ||
|
||
pipe(body); | ||
}, | ||
onShellError(error: unknown) { | ||
reject(error); | ||
}, | ||
onError(error: unknown) { | ||
responseStatusCode = 500; | ||
// Log streaming rendering errors from inside the shell. Don't log | ||
// errors encountered during initial shell rendering since they'll | ||
// reject and get logged in handleDocumentRequest. | ||
if (shellRendered) { | ||
console.error(error); | ||
} | ||
}, | ||
}, | ||
); | ||
|
||
setTimeout(abort, ABORT_DELAY); | ||
}); | ||
} | ||
|
||
function handleBrowserRequest( | ||
request: Request, | ||
responseStatusCode: number, | ||
responseHeaders: Headers, | ||
remixContext: EntryContext, | ||
) { | ||
return new Promise((resolve, reject) => { | ||
let shellRendered = false; | ||
const { pipe, abort } = renderToPipeableStream( | ||
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />, | ||
{ | ||
onShellReady() { | ||
shellRendered = true; | ||
const body = new PassThrough(); | ||
const stream = createReadableStreamFromReadable(body); | ||
|
||
responseHeaders.set('Content-Type', 'text/html'); | ||
|
||
resolve( | ||
new Response(stream, { | ||
headers: responseHeaders, | ||
status: responseStatusCode, | ||
}), | ||
); | ||
|
||
pipe(body); | ||
}, | ||
onShellError(error: unknown) { | ||
reject(error); | ||
}, | ||
onError(error: unknown) { | ||
responseStatusCode = 500; | ||
// Log streaming rendering errors from inside the shell. Don't log | ||
// errors encountered during initial shell rendering since they'll | ||
// reject and get logged in handleDocumentRequest. | ||
if (shellRendered) { | ||
console.error(error); | ||
} | ||
}, | ||
}, | ||
); | ||
|
||
setTimeout(abort, ABORT_DELAY); | ||
}); | ||
} |
80 changes: 80 additions & 0 deletions
80
dev-packages/e2e-tests/test-applications/create-remix-app-express-legacy/app/root.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { cssBundleHref } from '@remix-run/css-bundle'; | ||
import { LinksFunction, MetaFunction, json } from '@remix-run/node'; | ||
import { | ||
Links, | ||
LiveReload, | ||
Meta, | ||
Outlet, | ||
Scripts, | ||
ScrollRestoration, | ||
useLoaderData, | ||
useRouteError, | ||
} from '@remix-run/react'; | ||
import { captureRemixErrorBoundaryError, withSentry } from '@sentry/remix'; | ||
import type { SentryMetaArgs } from '@sentry/remix'; | ||
|
||
export const links: LinksFunction = () => [...(cssBundleHref ? [{ rel: 'stylesheet', href: cssBundleHref }] : [])]; | ||
|
||
export const loader = () => { | ||
return json({ | ||
ENV: { | ||
SENTRY_DSN: process.env.E2E_TEST_DSN, | ||
}, | ||
}); | ||
}; | ||
|
||
export const meta = ({ data }: SentryMetaArgs<MetaFunction<typeof loader>>) => { | ||
return [ | ||
{ | ||
env: data.ENV, | ||
}, | ||
{ | ||
name: 'sentry-trace', | ||
content: data.sentryTrace, | ||
}, | ||
{ | ||
name: 'baggage', | ||
content: data.sentryBaggage, | ||
}, | ||
]; | ||
}; | ||
|
||
export function ErrorBoundary() { | ||
const error = useRouteError(); | ||
const eventId = captureRemixErrorBoundaryError(error); | ||
|
||
return ( | ||
<div> | ||
<span>ErrorBoundary Error</span> | ||
<span id="event-id">{eventId}</span> | ||
</div> | ||
); | ||
} | ||
|
||
function App() { | ||
const { ENV } = useLoaderData(); | ||
|
||
return ( | ||
<html lang="en"> | ||
<head> | ||
<meta charSet="utf-8" /> | ||
<meta name="viewport" content="width=device-width,initial-scale=1" /> | ||
<script | ||
dangerouslySetInnerHTML={{ | ||
__html: `window.ENV = ${JSON.stringify(ENV)}`, | ||
}} | ||
/> | ||
<Meta /> | ||
<Links /> | ||
</head> | ||
<body> | ||
<Outlet /> | ||
<ScrollRestoration /> | ||
<Scripts /> | ||
<LiveReload /> | ||
</body> | ||
</html> | ||
); | ||
} | ||
|
||
export default withSentry(App); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.