Backend: Error Monitoring
Backend: Logging
Go
JS
Python
Ruby
Java
Rust
Hosting Providers
Backend: Tracing
Native OpenTelemetry
Fullstack Frameworks
Overview
Self Host & Local Dev
Menu
Remix Walkthrough
Overview
Our Remix SDK gives you access to frontend session replays and server-side monitoring, all-in-one.
- Use
<HighlightInit />
to track session replay and client-side errors. - Use
H.init
to instrument Remix'snodejs
server.
Installation
# with yarn yarn add @highlight-run/remix
Client Instrumentation
- Inject
<HighlightInit />
into your app root. - Optionally configure
excludedHostnames
to block a full or partial hostname. For example,excludedHostnames={['staging']}
would not initialize Highlight onstaging.highlight.io
. - Configure
tracingOrigins
andnetworkRecording
See Fullstack Mapping for details.
// app/root.tsx import { useLoaderData } from '@remix-run/react' import { HighlightInit } from '@highlight-run/remix/client' import { json } from '@remix-run/node' export async function loader() { return json({ ENV: { HIGHLIGHT_PROJECT_ID: process.env.HIGHLIGHT_PROJECT_ID, }, }) } export default function App() { const { ENV } = useLoaderData() return ( <html lang="en"> <HighlightInit excludedHostnames={['localhost']} projectId={ENV.HIGHLIGHT_PROJECT_ID} serviceName="my-remix-frontend" tracingOrigins networkRecording={{ enabled: true, recordHeadersAndBody: true }} /> {/* Render head, body, <Outlet />, etc. */} </html> ) }
- Optionally Create an
ErrorBoundary
component and export it fromapp/root.tsx
// app/components/error-boundary.tsx import { isRouteErrorResponse, useRouteError } from '@remix-run/react' import { ReportDialog } from '@highlight-run/remix/report-dialog' export function ErrorBoundary() { const error = useRouteError() if (isRouteErrorResponse(error)) { return ( <div> <h1> {error.status} {error.statusText} </h1> <p>{error.data}</p> </div> ) } else if (error instanceof Error) { return ( <div> <script src="https://unpkg.com/highlight.run"></script> <script dangerouslySetInnerHTML={{ __html: ` H.init('\${process.env.HIGHLIGHT_PROJECT_ID}'); `, }} /> <h1>Error</h1> <p>{error.message}</p> <p>The stack trace is:</p> <pre>{error.stack}</pre> <ReportDialog /> </div> ) } else { return <h1>Unknown Error</h1> } }
// app/root.tsx export { ErrorBoundary } from '~/components/error-boundary'
Server Instrumentation
- Use
H.init
from@highlight-run/remix/server
to instrument the Remix server on Node.js. - Import
HandleError
from@highlight-run/remix/server
and exporthandleError
after settingnodeOptions
.
// app/entry.server.tsx import { HandleError } from '@highlight-run/remix/server' const nodeOptions = { projectID: process.env.HIGHLIGHT_PROJECT_ID } export const handleError = HandleError(nodeOptions) // Handle server requests
Alternatively, you can wrap Highlight's error handler and execute your own custom error handling code as well.
// app/entry.server.tsx import type { DataFunctionArgs } from '@remix-run/node' import { H, HandleError } from '@highlight-run/remix/server' const nodeOptions = { projectID: process.env.HIGHLIGHT_PROJECT_ID } export function handleError( error: unknown, dataFunctionArgs: DataFunctionArgs, ) { const handleError = HandleError(nodeOptions) handleError(error, dataFunctionArgs) // custom error handling logic here } H.init(nodeOptions) // Handle server requests
Handle streaming HTML responses using the onError
handler of renderToPipeableStream
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() responseHeaders.set('Content-Type', 'text/html') resolve( new Response(body, { headers: responseHeaders, status: responseStatusCode, }), ) pipe(body) }, onShellError(error: unknown) { reject(error) }, onError(error: unknown) { if (shellRendered) { logError(error, request) } }, }, ) setTimeout(abort, ABORT_DELAY) }) } function logError(error: unknown, request?: Request) { const parsed = request ? H.parseHeaders(Object.fromEntries(request.headers)) : undefined if (error instanceof Error) { H.consumeError(error, parsed?.secureSessionId, parsed?.requestId) } else { H.consumeError( new Error(`Unknown error: ${JSON.stringify(error)}`), parsed?.secureSessionId, parsed?.requestId, ) } console.error(error) }