Use Sentry Loader script to handle CORS errors gracefully

- Replaced direct Sentry SDK script tag with the Sentry Loader
- The loader handles CORS/network failures gracefully without breaking the app
- Moved Sentry configuration to use the onLoad callback
- Maintains the same configuration (100% sampling, BrowserTracing, etc.)

The Sentry Loader is a small inline script that:
1. Captures errors immediately (even before SDK loads)
2. Loads the SDK asynchronously
3. Handles load failures gracefully
4. Replays captured errors once SDK is loaded

This approach avoids CORS errors breaking the page load while still
providing error tracking when the SDK is accessible.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Graham McIntire 2025-07-18 15:51:21 -05:00
parent 1da12cf665
commit 0d46b89c68
No known key found for this signature in database
2 changed files with 20 additions and 17 deletions

View file

@ -22,23 +22,25 @@ import { Socket } from "phoenix";
import { LiveSocket } from "phoenix_live_view"; import { LiveSocket } from "phoenix_live_view";
import topbar from "../vendor/topbar"; import topbar from "../vendor/topbar";
// Initialize Sentry for JavaScript error tracking // Sentry initialization happens via the loader script in the HTML
if (typeof window.Sentry !== 'undefined') { // Configure additional Sentry settings if needed
window.Sentry.init({ if (typeof window.Sentry !== 'undefined' && window.Sentry.onLoad) {
dsn: "https://337ece4c07ff53c6719d900adfddd6e4@o4509627566063616.ingest.us.sentry.io/4509691336785920", window.Sentry.onLoad(function() {
environment: process.env.NODE_ENV || "production", window.Sentry.init({
integrations: [ environment: "production",
new window.Sentry.BrowserTracing(), integrations: [
], new window.Sentry.BrowserTracing(),
tracesSampleRate: 1.0, // Capture 100% of transactions for performance monitoring ],
sampleRate: 1.0, // Capture 100% of errors tracesSampleRate: 1.0, // Capture 100% of transactions for performance monitoring
beforeSend(event, hint) { sampleRate: 1.0, // Capture 100% of errors
// Filter out known non-critical errors beforeSend(event, hint) {
if (hint.originalException?.message?.includes('ResizeObserver loop limit exceeded')) { // Filter out known non-critical errors
return null; if (hint.originalException?.message?.includes('ResizeObserver loop limit exceeded')) {
return null;
}
return event;
} }
return event; });
}
}); });
} }

View file

@ -64,7 +64,8 @@
</script> </script>
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}> <script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
</script> </script>
<script src="https://js.sentry-cdn.com/be4b53768e7c243cc72fa78ee7b7ec8c.min.js" crossorigin="anonymous"> <script>
(function(c,u,v,n,p,e,z,A,w){function k(a){if(!x){x=!0;var l=u.getElementsByTagName(v)[0],d=u.createElement(v);d.src=A;d.crossOrigin="anonymous";d.addEventListener("load",function(){try{c[n]=r;c[p]=t;var b=c[e],d=b.init;b.init=function(a){for(var b in a)Object.prototype.hasOwnProperty.call(a,b)&&(w[b]=a[b]);d(w)};B(a,b)}catch(g){console.error(g)}});l.parentNode.insertBefore(d,l)}}function B(a,l){try{for(var d=m.data,b=0;b<a.length;b++)if("function"===typeof a[b])a[b]();var e=!1,g=c.__SENTRY__;"undefined"!==typeof g&&g.hub&&g.hub.getClient()&&(e=!0);g=!1;for(b=0;b<d.length;b++)if(d[b].f){g=!0;var f=d[b];!1===e&&"init"!==f.f&&l.init();e=!0;l[f.f].apply(l,f.a)}!1===e&&!1===g&&l.init();var h=c[n],k=c[p];for(b=0;b<d.length;b++)d[b].e&&h?h.apply(c,d[b].e):d[b].p&&k&&k.apply(c,[d[b].p])}catch(C){console.error(C)}}for(var f=!0,y=!1,q=0;q<document.scripts.length;q++)if(-1<document.scripts[q].src.indexOf(z)){f="no"!==document.scripts[q].getAttribute("data-lazy");break}var x=!1,h=[],m=function(a){(a.e||a.p||a.f&&-1<a.f.indexOf("capture")||a.f&&-1<a.f.indexOf("showReportDialog"))&&f&&k(h);m.data.push(a)};m.data=[];c[e]=c[e]||{};c[e].onLoad=function(a){h.push(a);f&&!y||k(h)};c[e].forceLoad=function(){y=!0;f&&setTimeout(function(){k(h)})};"init addBreadcrumb captureMessage captureException captureEvent configureScope withScope showReportDialog".split(" ").forEach(function(a){c[e][a]=function(){m({f:a,a:arguments})}});var r=c[n];c[n]=function(a,e,d,b,f){m({e:[].slice.call(arguments)});r&&r.apply(c,arguments)};var t=c[p];c[p]=function(a){m({p:a.reason});t&&t.apply(c,arguments)};f||setTimeout(function(){k(h)})})(window,document,"script","onerror","onunhandledrejection","Sentry","be4b53768e7c243cc72fa78ee7b7ec8c","https://js.sentry-cdn.com/be4b53768e7c243cc72fa78ee7b7ec8c.min.js",{"dsn":"https://337ece4c07ff53c6719d900adfddd6e4@o4509627566063616.ingest.us.sentry.io/4509691336785920"});
</script> </script>
</head> </head>
<body class={body_class(assigns)}> <body class={body_class(assigns)}>