diff --git a/instrumentation-client.ts b/instrumentation-client.ts index 62a3f8740cf..360cc470a3a 100644 --- a/instrumentation-client.ts +++ b/instrumentation-client.ts @@ -33,6 +33,37 @@ Sentry.init({ debug: environment === "development", environment, enabled: environment === "production", + // Normalize transaction names for parameterized routes to enable per-page analysis + // Sentry uses formats like "/:locale/:slug*" for catch-all routes + beforeSendTransaction(event) { + const op = event.contexts?.trace?.op + const transaction = event.transaction + + // Matches patterns like ":locale", ":slug*", ":id", ":post", etc. + const isParameterizedRoute = transaction && /:\w+/.test(transaction) + const isPageTransaction = op === "pageload" || op === "navigation" + + if (isParameterizedRoute && isPageTransaction) { + const url = event.request?.url || (event.tags?.url as string | undefined) + if (url) { + try { + const pathname = new URL(url).pathname + // Remove locale prefix (e.g., "/en/", "/fil/", "/zh-tw/", "/pt-br/"), keeping just the page path + // e.g., "/en/developers/docs" -> "/developers/docs" + // Only match complete path segments (must be followed by "/" or end of string) + const normalizedPath = pathname.replace( + /^\/[a-z]{2,3}(-[a-z]{2})?(?=\/|$)/, + "" + ) + event.transaction = normalizedPath || "/" + } catch { + // Keep original transaction name if URL parsing fails + } + } + } + return event + }, + beforeBreadcrumb(breadcrumb, hint) { if (breadcrumb.category === "ui.click") { const element = hint?.event?.target