Skip to content

Commit e661563

Browse files
authored
Merge pull request #16138 from ethereum/sentry-breadcrumbs
Sentry breadcrumbs improvements
2 parents c946b78 + 1cc219b commit e661563

13 files changed

+97
-31
lines changed

instrumentation-client.ts

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,49 @@ import * as Sentry from "@sentry/nextjs"
22

33
const environment = process.env.NEXT_PUBLIC_CONTEXT || "development"
44

5-
if (environment === "production") {
6-
Sentry.init({
7-
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
8-
tracesSampleRate: 0.1,
9-
debug: false,
10-
environment,
11-
})
5+
/**
6+
* Finds the closest element (including the element itself) that has an id attribute
7+
* @param element - The starting element to search from
8+
* @param maxDepth - Maximum number of parent levels to search (default: 3)
9+
* @returns The first found attribute value in priority order, null otherwise
10+
*/
11+
function findClosestElementId(
12+
element: Element | null | undefined,
13+
maxDepth: number = 3
14+
): string | null {
15+
if (!element || maxDepth < 0) return null
16+
17+
const sentryId = element.getAttribute("data-testid")
18+
if (sentryId) return sentryId
19+
20+
const ariaLabel = element.getAttribute("aria-label")
21+
if (ariaLabel) return ariaLabel
22+
23+
const id = element.getAttribute("id")
24+
if (id) return id
25+
26+
// Recursively check parent elements up to maxDepth
27+
return findClosestElementId(element.parentElement, maxDepth - 1)
1228
}
1329

30+
Sentry.init({
31+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
32+
tracesSampleRate: 0.1,
33+
debug: environment === "development",
34+
environment,
35+
enabled: environment === "production",
36+
beforeBreadcrumb(breadcrumb, hint) {
37+
if (breadcrumb.category === "ui.click") {
38+
const element = hint?.event?.target
39+
40+
const id = findClosestElementId(element)
41+
if (id) {
42+
breadcrumb.message = id + " (" + breadcrumb.message + ")"
43+
}
44+
}
45+
46+
return breadcrumb
47+
},
48+
})
49+
1450
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart

next.config.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,19 @@ module.exports = (phase, { defaultConfig }) => {
137137
},
138138
}
139139

140+
nextConfig = {
141+
...nextConfig,
142+
experimental: {
143+
...experimental,
144+
instrumentationHook: true,
145+
},
146+
}
147+
140148
if (phase !== PHASE_DEVELOPMENT_SERVER) {
141149
nextConfig = {
142150
...nextConfig,
143151
experimental: {
144-
...experimental,
152+
...nextConfig.experimental,
145153
outputFileTracingExcludes: {
146154
"*": [
147155
/**

sentry.edge.config.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ import * as Sentry from "@sentry/nextjs"
22

33
const environment = process.env.NEXT_PUBLIC_CONTEXT || "development"
44

5-
if (environment === "production") {
6-
Sentry.init({
7-
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
8-
tracesSampleRate: 0.1,
9-
debug: false,
10-
environment,
11-
})
12-
}
5+
Sentry.init({
6+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
7+
tracesSampleRate: 0.1,
8+
debug: environment === "development",
9+
environment,
10+
enabled: environment === "production",
11+
})

sentry.server.config.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ import * as Sentry from "@sentry/nextjs"
22

33
const environment = process.env.NEXT_PUBLIC_CONTEXT || "development"
44

5-
if (environment === "production") {
6-
Sentry.init({
7-
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
8-
tracesSampleRate: 0.1,
9-
debug: false,
10-
environment,
11-
})
12-
}
5+
Sentry.init({
6+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
7+
tracesSampleRate: 0.1,
8+
debug: environment === "development",
9+
environment,
10+
enabled: environment === "production",
11+
})

src/components/Footer.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ const Footer = ({ lastDeployLocaleTimestamp }: FooterProps) => {
323323
variant="outline"
324324
isSecondary
325325
onClick={() => scrollIntoView("body")}
326+
data-testid="footer-go-to-top"
326327
>
327328
<ChevronUp /> <Translation id="go-to-top" />
328329
</Button>

src/components/LanguagePicker/MobileCloseBar.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ export const MobileCloseBar = ({ handleClick }: MobileCloseBarProps) => {
1313

1414
return (
1515
<div className="sticky top-0 flex justify-end bg-background md:hidden">
16-
<Button className="self-end p-4" variant="ghost" onClick={handleClick}>
16+
<Button
17+
className="self-end p-4"
18+
variant="ghost"
19+
onClick={handleClick}
20+
data-testid="mobile-language-picker-close"
21+
>
1722
{t("close")}
1823
</Button>
1924
</div>

src/components/LanguagePicker/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ const LanguagePickerMenu = ({ languages, onClose, onSelect }) => {
174174

175175
<CommandInput
176176
placeholder={t("page-languages-filter-placeholder")}
177+
data-testid="language-filter-input"
177178
className="h-9"
178179
kbdShortcut="\"
179180
/>
@@ -188,6 +189,7 @@ const LanguagePickerMenu = ({ languages, onClose, onSelect }) => {
188189
key={"item-" + displayInfo.localeOption}
189190
displayInfo={displayInfo}
190191
onSelect={onSelect}
192+
data-testid={`language-option-${displayInfo.localeOption}`}
191193
/>
192194
))}
193195
</CommandGroup>

src/components/Nav/Mobile/HamburgerButton.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const HamburgerButton = forwardRef<HTMLButtonElement, HamburgerProps>(
3131
<Button
3232
ref={ref}
3333
id={HAMBURGER_BUTTON_ID}
34+
data-testid="mobile-menu-hamburger"
3435
aria-label={t("aria-toggle-menu-button")}
3536
className={cn("px-2 py-0 text-body", className)}
3637
variant="ghost"

src/components/Nav/Mobile/LvlAccordion.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as AccordionPrimitive from "@radix-ui/react-accordion"
44

55
import { cn } from "@/lib/utils/cn"
66
import { trackCustomEvent } from "@/lib/utils/matomo"
7+
import { slugify } from "@/lib/utils/url"
78
import { cleanPath } from "@/lib/utils/url"
89

910
import { Button } from "../../ui/buttons/Button"
@@ -131,6 +132,7 @@ const LvlAccordion = ({
131132
<AccordionTrigger
132133
heading={`h${lvl + 1}` as "h2" | "h3" | "h4" | "h5"}
133134
className={cn("text-body", nestedAccordionSpacingMap[lvl])}
135+
data-testid={`accordion-toggle-${slugify(label)}`}
134136
onClick={() => {
135137
trackCustomEvent({
136138
eventCategory: "Mobile navigation menu",

src/components/Nav/Mobile/MenuBody.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useLocale } from "next-intl"
33

44
import { cn } from "@/lib/utils/cn"
55
import { trackCustomEvent } from "@/lib/utils/matomo"
6+
import { slugify } from "@/lib/utils/url"
67

78
import { SECTION_LABELS } from "@/lib/constants"
89

@@ -46,6 +47,7 @@ const MenuBody = ({ linkSections, onToggle }: MenuBodyProps) => {
4647
>
4748
<AccordionTrigger
4849
className="text-body"
50+
data-testid={`menu-section-toggle-${slugify(label)}`}
4951
onClick={() => {
5052
trackCustomEvent({
5153
eventCategory: "Mobile navigation menu",

0 commit comments

Comments
 (0)