Adding Sentry is basically done now. We have a nice lil component to help us get started to!

This commit is contained in:
2025-06-07 14:30:25 -05:00
parent eebc022928
commit 23efcdee80
16 changed files with 3538 additions and 1207 deletions

View File

@@ -0,0 +1,16 @@
import { NextResponse } from 'next/server';
export const dynamic = 'force-dynamic';
class SentryExampleAPIError extends Error {
constructor(message: string | undefined) {
super(message);
this.name = 'SentryExampleAPIError';
}
}
// A faulty API route to test Sentry's error monitoring
export function GET() {
throw new SentryExampleAPIError(
'This error is raised on the backend called by the example page.',
);
return NextResponse.json({ data: 'Testing Sentry Error...' });
}

View File

@@ -4,14 +4,51 @@ import { FetchDataSteps } from '@/components/default/tutorial';
import { InfoIcon } from 'lucide-react';
import { getUser } from '@/lib/actions';
import type { User } from '@/utils/supabase';
import Link from 'next/link';
import { TestSentryCard } from '@/components/default/sentry';
import {
Button,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Separator,
} from '@/components/ui';
const HomePage = async () => {
const response = await getUser();
if (!response.success || !response.data) {
return (
<main className='w-full items-center justify-center'>
<div className='flex p-5 items-center justify-center'>
<h1>Make sure you can sign in!</h1>
<div className='flex flex-col p-5 items-center justify-center space-y-6'>
<Card className='md:min-w-2xl'>
<CardHeader className='flex flex-col items-center'>
<CardTitle className='text-3xl'>
Welcome to the T3 Supabase Template!
</CardTitle>
<CardDescription className='text-[1.0rem] mb-2'>
A great place to start is by creating a new user account &
ensuring you can sign in!
</CardDescription>
<Button
asChild
size='sm'
variant={'default'}
className='w-1/3 items-center'
>
<Link href='/sign-up'>Sign up</Link>
</Button>
</CardHeader>
<Separator className='bg-accent' />
<CardContent className='flex flex-col px-5 py-2 items-center justify-center'>
<CardTitle className='text-lg mb-6 w-2/3 text-center'>
You can also test out your connection to Sentry if you want to
start there!
</CardTitle>
<TestSentryCard />
</CardContent>
</Card>
</div>
</main>
);
@@ -30,14 +67,15 @@ const HomePage = async () => {
</div>
</div>
<div className='flex flex-col gap-2 items-start'>
<h2 className='font-bold text-2xl mb-4'>Your user details</h2>
<h2 className='font-bold text-3xl mb-4'>Your user details</h2>
<pre
className='text-xs font-mono p-3 rounded
border max-h-32 overflow-auto'
className='text-sm font-mono p-3 rounded
border max-h-50 overflow-auto'
>
{JSON.stringify(user, null, 2)}
</pre>
</div>
<TestSentryCard />
<div>
<h2 className='font-bold text-2xl mb-4'>Next steps</h2>
<FetchDataSteps />

View File

@@ -0,0 +1,125 @@
'use client';
import * as Sentry from '@sentry/nextjs';
import { useState, useEffect } from 'react';
import {
Button,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Separator,
} from '@/components/ui';
import Link from 'next/link';
import { CheckCircle, MessageCircleWarning } from 'lucide-react';
class SentryExampleFrontendError extends Error {
constructor(message: string | undefined) {
super(message);
this.name = 'SentryExampleFrontendError';
}
}
export const TestSentryCard = () => {
const [hasSentError, setHasSentError] = useState(false);
const [isConnected, setIsConnected] = useState(true);
useEffect(() => {
const checkConnectivity = async () => {
const result = await Sentry.diagnoseSdkConnectivity();
setIsConnected(result !== 'sentry-unreachable');
};
checkConnectivity().catch((error) => {
console.error('Error trying to connect to Sentry: ', error);
});
}, []);
const createError = async () => {
await Sentry.startSpan(
{
name: 'Example Frontend Span',
op: 'test',
},
async () => {
const res = await fetch('/api/sentry/example');
if (!res.ok) {
setHasSentError(true);
throw new SentryExampleFrontendError(
'This error is raised in our TestSentry component on the main page.',
);
}
},
);
};
return (
<Card>
<CardHeader>
<div className='flex flex-row my-auto space-x-4'>
<svg
height='40'
width='40'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M21.85 2.995a3.698 3.698 0 0 1 1.353 1.354l16.303 28.278a3.703 3.703 0 0 1-1.354 5.053 3.694 3.694 0 0 1-1.848.496h-3.828a31.149 31.149 0 0 0 0-3.09h3.815a.61.61 0 0 0 .537-.917L20.523 5.893a.61.61 0 0 0-1.057 0l-3.739 6.494a28.948 28.948 0 0 1 9.63 10.453 28.988 28.988 0 0 1 3.499 13.78v1.542h-9.852v-1.544a19.106 19.106 0 0 0-2.182-8.85 19.08 19.08 0 0 0-6.032-6.829l-1.85 3.208a15.377 15.377 0 0 1 6.382 12.484v1.542H3.696A3.694 3.694 0 0 1 0 34.473c0-.648.17-1.286.494-1.849l2.33-4.074a8.562 8.562 0 0 1 2.689 1.536L3.158 34.17a.611.611 0 0 0 .538.917h8.448a12.481 12.481 0 0 0-6.037-9.09l-1.344-.772 4.908-8.545 1.344.77a22.16 22.16 0 0 1 7.705 7.444 22.193 22.193 0 0 1 3.316 10.193h3.699a25.892 25.892 0 0 0-3.811-12.033 25.856 25.856 0 0 0-9.046-8.796l-1.344-.772 5.269-9.136a3.698 3.698 0 0 1 3.2-1.849c.648 0 1.285.17 1.847.495Z'
fill='currentcolor'
/>
</svg>
<CardTitle className='text-3xl my-auto'>Test Sentry</CardTitle>
</div>
<CardDescription className='text-[1.0rem]'>
Click the button below & view the sample error on{' '}
<Link
href={`${process.env.NEXT_PUBLIC_SENTRY_URL}`}
className='text-accent-foreground underline hover:text-primary'
>
the Sentry website
</Link>
. Navigate to the {"'"}Issues{"'"} page & you should see the sample
error!
</CardDescription>
</CardHeader>
<CardContent>
<div className='flex flex-row gap-4 my-auto'>
<Button
type='button'
onClick={createError}
className='cursor-pointer text-md my-auto py-6'
>
<span>Throw Sample Error</span>
</Button>
{hasSentError ? (
<div className='rounded-md bg-green-500/80 dark:bg-green-500/50 py-2 px-4 flex flex-row gap-2 my-auto'>
<CheckCircle size={30} className='my-auto' />
<p className='text-lg'>Sample error was sent to Sentry!</p>
</div>
) : !isConnected ? (
<div className='rounded-md bg-red-600/50 dark:bg-red-500/50 py-2 px-4 flex flex-row gap-2 my-auto'>
<MessageCircleWarning size={40} className='my-auto' />
<p>
Wait! The Sentry SDK is not able to reach Sentry right now -
this may be due to an adblocker. For more information, see{' '}
<Link
href='https://docs.sentry.io/platforms/javascript/guides/nextjs/troubleshooting/#the-sdk-is-not-sending-any-data'
className='text-accent-foreground underline hover:text-primary'
>
the troubleshooting guide.
</Link>
</p>
</div>
) : (
<div className='success_placeholder' />
)}
</div>
<Separator className='my-4 bg-accent' />
<p className='description'>
Warning! Sometimes Adblockers will prevent errors from being sent to
Sentry.
</p>
</CardContent>
</Card>
);
};

View File

@@ -0,0 +1 @@
export { TestSentryCard } from './TestSentry';

View File

@@ -7,11 +7,12 @@ export const env = createEnv({
* This way you can ensure the app isn't built with invalid env vars.
*/
server: {
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
NODE_ENV: z
.enum(['development', 'test', 'production'])
.default('development'),
NEXT_RUNTIME: z.enum(['nodejs', 'edge']).default('nodejs'),
SENTRY_URL: z.string().url().default('https://sentry.gbrown.org'),
SENTRY_AUTH_TOKEN: z.string().min(1),
CI: z.enum(['true', 'false']).default('true'),
CI: z.enum(['true', 'false']).default('false'),
},
/**
@@ -24,6 +25,10 @@ export const env = createEnv({
NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
NEXT_PUBLIC_SITE_URL: z.string().url().default('http://localhost:3000'),
NEXT_PUBLIC_SENTRY_DSN: z.string().min(1),
NEXT_PUBLIC_SENTRY_URL: z
.string()
.url()
.default('https://sentry.gbrown.org'),
},
/**
@@ -33,7 +38,6 @@ export const env = createEnv({
runtimeEnv: {
NODE_ENV: process.env.NODE_ENV,
NEXT_RUNTIME: process.env.NEXT_RUNTIME,
SENTRY_URL: process.env.SENTRY_URL,
SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN,
CI: process.env.CI,
@@ -41,6 +45,7 @@ export const env = createEnv({
NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL,
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
NEXT_PUBLIC_SENTRY_URL: process.env.NEXT_PUBLIC_SENTRY_URL,
},
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially

View File

@@ -0,0 +1,36 @@
// This file configures the initialization of Sentry on the client.
// The added config here will be used whenever a users loads a page in their browser.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN!,
// Adds request headers and IP for users, for more info visit:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#sendDefaultPii
sendDefaultPii: true,
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for tracing.
// We recommend adjusting this value in production
// Learn more at
// https://docs.sentry.io/platforms/javascript/configuration/options/#traces-sample-rate
tracesSampleRate: 1.0,
// Replay may only be enabled for the client-side
integrations: [Sentry.replayIntegration()],
// Capture Replay for 10% of all sessions,
// plus for 100% of sessions with an error
// Learn more at
// https://docs.sentry.io/platforms/javascript/session-replay/configuration/#general-integration-configuration
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
// Note: if you want to override the automatic release value, do not set a
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
// that it will also get attached to your source maps
});
// This export will instrument router navigations, and is only relevant if you enable tracing.
// `captureRouterTransitionStart` is available from SDK version 9.12.0 onwards
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;

View File

@@ -1,7 +1,6 @@
import * as Sentry from '@sentry/nextjs';
import type { Instrumentation } from 'next';
export const register = async () => {
if (process.env.NEXT_RUNTIME === 'edge') {
await import('../sentry.edge.config');