Skip to content

Commit a8c5b65

Browse files
committed
Add Sentry example routes and associate errors with releases
1 parent f676fd8 commit a8c5b65

7 files changed

Lines changed: 317 additions & 67 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as Sentry from '@sentry/react-router'
2+
import { requireUserWithRole } from '../../utils/permissions.server.ts'
3+
import { type Route } from './+types/api.sentry-example-api.ts'
4+
5+
class SentryExampleBackendError extends Error {
6+
constructor(message: string | undefined) {
7+
super(message)
8+
this.name = 'SentryExampleBackendError'
9+
}
10+
}
11+
12+
export async function loader({ request }: Route.LoaderArgs) {
13+
await requireUserWithRole(request, 'admin')
14+
15+
await Sentry.startSpan(
16+
{
17+
name: 'Example Backend Span',
18+
op: 'test',
19+
},
20+
async () => {
21+
// Simulate some backend work
22+
await new Promise((resolve) => setTimeout(resolve, 100))
23+
},
24+
)
25+
26+
throw new SentryExampleBackendError(
27+
'This error is raised on the backend API route.',
28+
)
29+
}

app/routes/sentry/example-page.tsx

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import * as Sentry from '@sentry/react-router'
2+
import { useState, useEffect } from 'react'
3+
import { requireUserWithRole } from '../../utils/permissions.server.ts'
4+
import { type Route } from './+types/example-page.ts'
5+
6+
class SentryExampleFrontendError extends Error {
7+
constructor(message: string | undefined) {
8+
super(message)
9+
this.name = 'SentryExampleFrontendError'
10+
}
11+
}
12+
13+
export const meta = () => {
14+
return [{ title: 'sentry-example-page' }]
15+
}
16+
17+
export async function loader({ request }: Route.LoaderArgs) {
18+
await requireUserWithRole(request, 'admin')
19+
}
20+
21+
export default function ExamplePage() {
22+
const [hasSentError, setHasSentError] = useState(false)
23+
const [isConnected, setIsConnected] = useState(true)
24+
25+
useEffect(() => {
26+
async function checkConnectivity() {
27+
const result = await Sentry.diagnoseSdkConnectivity()
28+
setIsConnected(result !== 'sentry-unreachable')
29+
}
30+
void checkConnectivity()
31+
}, [setIsConnected])
32+
33+
return (
34+
<div>
35+
<main>
36+
<div className="flex-spacer" />
37+
<svg
38+
height="40"
39+
width="40"
40+
fill="none"
41+
xmlns="http://www.w3.org/2000/svg"
42+
>
43+
<path
44+
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"
45+
fill="currentcolor"
46+
/>
47+
</svg>
48+
<h1>sentry-example-page</h1>
49+
<button
50+
type="button"
51+
onClick={async () => {
52+
await Sentry.startSpan(
53+
{
54+
name: 'Example Frontend Span',
55+
op: 'test',
56+
},
57+
async () => {
58+
const res = await fetch('/sentry/api/sentry-example-api')
59+
if (!res.ok) {
60+
setHasSentError(true)
61+
}
62+
},
63+
)
64+
throw new SentryExampleFrontendError(
65+
'This error is raised on the frontend of the example page.',
66+
)
67+
}}
68+
disabled={!isConnected}
69+
>
70+
<span>Throw Sample Error</span>
71+
</button>
72+
73+
{hasSentError ? (
74+
<p className="success">Sample error was sent to Sentry.</p>
75+
) : !isConnected ? (
76+
<div className="connectivity-error">
77+
<p>
78+
It looks like network requests to Sentry are being blocked, which
79+
will prevent errors from being captured. Try disabling your
80+
ad-blocker to complete the test.
81+
</p>
82+
</div>
83+
) : (
84+
<div className="success_placeholder" />
85+
)}
86+
87+
<div className="flex-spacer" />
88+
</main>
89+
90+
{/* Not for production use! We're just saving you from having to delete an extra CSS file ;) */}
91+
<style dangerouslySetInnerHTML={{ __html: styles }}></style>
92+
</div>
93+
)
94+
}
95+
96+
const styles = `
97+
main {
98+
display: flex;
99+
min-height: 100vh;
100+
flex-direction: column;
101+
justify-content: center;
102+
align-items: center;
103+
gap: 16px;
104+
padding: 16px;
105+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
106+
}
107+
108+
h1 {
109+
padding: 0px 4px;
110+
border-radius: 4px;
111+
background-color: rgba(24, 20, 35, 0.03);
112+
font-family: monospace;
113+
font-size: 20px;
114+
line-height: 1.2;
115+
}
116+
117+
p {
118+
margin: 0;
119+
font-size: 20px;
120+
}
121+
122+
a {
123+
color: #6341F0;
124+
text-decoration: underline;
125+
cursor: pointer;
126+
127+
@media (prefers-color-scheme: dark) {
128+
color: #B3A1FF;
129+
}
130+
}
131+
132+
button {
133+
border-radius: 8px;
134+
color: white;
135+
cursor: pointer;
136+
background-color: #553DB8;
137+
border: none;
138+
padding: 0;
139+
margin-top: 4px;
140+
141+
& > span {
142+
display: inline-block;
143+
padding: 12px 16px;
144+
border-radius: inherit;
145+
font-size: 20px;
146+
font-weight: bold;
147+
line-height: 1;
148+
background-color: #7553FF;
149+
border: 1px solid #553DB8;
150+
transform: translateY(-4px);
151+
}
152+
153+
&:hover > span {
154+
transform: translateY(-8px);
155+
}
156+
157+
&:active > span {
158+
transform: translateY(0);
159+
}
160+
161+
&:disabled {
162+
cursor: not-allowed;
163+
opacity: 0.6;
164+
165+
& > span {
166+
transform: translateY(0);
167+
border: none;
168+
}
169+
}
170+
}
171+
172+
.description {
173+
text-align: center;
174+
color: #6E6C75;
175+
max-width: 500px;
176+
line-height: 1.5;
177+
font-size: 20px;
178+
179+
@media (prefers-color-scheme: dark) {
180+
color: #A49FB5;
181+
}
182+
}
183+
184+
.flex-spacer {
185+
flex: 1;
186+
}
187+
188+
.success {
189+
padding: 12px 16px;
190+
border-radius: 8px;
191+
font-size: 20px;
192+
line-height: 1;
193+
background-color: #00F261;
194+
border: 1px solid #00BF4D;
195+
color: #181423;
196+
}
197+
198+
.success_placeholder {
199+
height: 46px;
200+
}
201+
202+
.connectivity-error {
203+
padding: 12px 16px;
204+
background-color: #E50045;
205+
border-radius: 8px;
206+
width: 500px;
207+
color: #FFFFFF;
208+
border: 1px solid #A80033;
209+
text-align: center;
210+
margin: 0;
211+
}
212+
213+
.connectivity-error a {
214+
color: #FFFFFF;
215+
text-decoration: underline;
216+
}
217+
`

docs/monitoring.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ workflow. Note that these do not need to be added to the
6363
used during the build and not the runtime.
6464

6565
The Sentry Vite plugin in [`vite.config.ts`](../vite.config.ts) will create
66-
sentry releases for you and automatically associate commits during the vite
66+
sentry releases for you and automatically upload sourcemaps during the vite
6767
build once the `SENTRY_AUTH_TOKEN` is set. In this setup we have utilized a
6868
simple strategy for naming releases of using the commit sha, passed in as a
6969
build arg via the GitHub action workflow.
70+
71+
Once the deploy is done, the "Create Sentry release" step in the
72+
[`deploy`](../.github/workflows/deploy.yml) script will associate commits to
73+
this release and mark the release as deployed and finalized.

instrument.server.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { nodeProfilingIntegration } from '@sentry/profiling-node'
22
import * as Sentry from '@sentry/react-router'
33

44
Sentry.init({
5-
release: process.env.COMMIT_SHA,
65
// Sentry will only send requests if the dsn is defined
76
dsn:
87
process.env.NODE_ENV === 'production' ? process.env.SENTRY_DSN : undefined,

other/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ ENV CACHE_DATABASE_PATH="$LITEFS_DIR/$CACHE_DATABASE_FILENAME"
6464
ENV INTERNAL_PORT="8080"
6565
ENV PORT="8081"
6666
ENV NODE_ENV="production"
67+
ENV SENTRY_RELEASE=$COMMIT_SHA
6768
# For WAL support: https://github.com/prisma/prisma-engines/issues/4675#issuecomment-1914383246
6869
ENV PRISMA_SCHEMA_DISABLE_ADVISORY_LOCK = "1"
6970

0 commit comments

Comments
 (0)