From 1fca1f8d3eceff314084cafdd42a6ec662a6f8a4 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 28 Mar 2023 16:14:57 +0200 Subject: [PATCH 1/8] WIP add base config for JS loader --- .../templates/sentry/js-sdk-loader.js.tmpl | 23 ++++++++--- .../sentry/js-sdk-loader.min.js.tmpl | 8 ++-- src/sentry/web/frontend/js_sdk_loader.py | 41 +++++++++++++++---- .../sentry/web/frontend/test_js_sdk_loader.py | 36 +++++++++++++++- 4 files changed, 88 insertions(+), 20 deletions(-) diff --git a/src/sentry/templates/sentry/js-sdk-loader.js.tmpl b/src/sentry/templates/sentry/js-sdk-loader.js.tmpl index aee842cba6b899..e2767e79b543a3 100644 --- a/src/sentry/templates/sentry/js-sdk-loader.js.tmpl +++ b/src/sentry/templates/sentry/js-sdk-loader.js.tmpl @@ -80,6 +80,10 @@ var oldInit = SDK.init; + if (_config.tracesSampleRate) { + _config.integrations = [new Sentry.BrowserTracing()]; + } + // Configure it using provided DSN and config object SDK.init = function(options) { var target = _config; @@ -92,6 +96,12 @@ }; sdkLoaded(callbacks, SDK); + + // Ensure we load the SDK in non-lazy mode, even if no Sentry.onLoad() was provided + // If it were provided, it would have already been called by sdkLoaded() above. + if (!sdkIsLoaded() && !lazy) { + SDK.init(); + } } catch (o_O) { console.error(o_O); } @@ -100,6 +110,12 @@ _currentScriptTag.parentNode.insertBefore(_newScriptTag, _currentScriptTag); } + function sdkIsLoaded() { + var __sentry = _window['__SENTRY__']; + // If there is a global __SENTRY__ that means that in any of the callbacks init() was already invoked + return !(typeof __sentry === 'undefined') && __sentry.hub && __sentry.hub.getClient(); + } + function sdkLoaded(callbacks, SDK) { try { var data = queue.data; @@ -111,12 +127,7 @@ } } - var initAlreadyCalled = false; - var __sentry = _window['__SENTRY__']; - // If there is a global __SENTRY__ that means that in any of the callbacks init() was already invoked - if (!(typeof __sentry === 'undefined') && __sentry.hub && __sentry.hub.getClient()) { - initAlreadyCalled = true; - } + var initAlreadyCalled = sdkIsLoaded(); // We want to replay all calls to Sentry and also make sure that `init` is called if it wasn't already // We replay all calls to `Sentry.*` now diff --git a/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl b/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl index 39b0f855f22bea..90ab4591d64ddb 100644 --- a/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl +++ b/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl @@ -1,4 +1,4 @@ -{% load sentry_helpers %}(function(c,w,C,q,r,k,D,E,x,l){function t(a){if(!y){y=!0;var f=w.scripts[0],d=w.createElement(C);d.src=E;d.crossOrigin="anonymous";d.addEventListener("load",function(){try{c[q]=u;c[r]=v;c.SENTRY_SDK_SOURCE="loader";var b=c[k],g=b.init;b.init=function(e){for(var h in e)Object.prototype.hasOwnProperty.call(e,h)&&(x[h]=e[h]);g(x)};F(a,b)}catch(e){console.error(e)}});f.parentNode.insertBefore(d,f)}}function F(a,f){try{for(var d=m.data,b=0;b None: pass - def _get_bundle_kind_modifier(self, key: ProjectKey, sdk_version: str) -> Tuple[str, bool]: + def _get_bundle_kind_modifier( + self, key: ProjectKey, sdk_version: str + ) -> Tuple[str, bool, bool, bool, bool]: """Returns a string that is used to modify the bundle name""" is_v7_sdk = sdk_version >= Version("7.0.0") is_lazy = True bundle_kind_modifier = "" + has_replay = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_REPLAY) + has_performance = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_PERFORMANCE) + has_debug = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_DEBUG) # The order in which these modifiers are added is important, as the # bundle name is built up from left to right. # https://docs.sentry.io/platforms/javascript/install/cdn/ - if get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_PERFORMANCE): + if has_performance: bundle_kind_modifier += ".tracing" is_lazy = False - has_replay = get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_REPLAY) - # If the project does not have a v7 sdk set, we cannot load the replay bundle. if is_v7_sdk and has_replay: bundle_kind_modifier += ".replay" @@ -69,10 +76,10 @@ def _get_bundle_kind_modifier(self, key: ProjectKey, sdk_version: str) -> Tuple[ if is_v7_sdk and not has_replay: bundle_kind_modifier += ".es5" - if get_dynamic_sdk_loader_option(key, DynamicSdkLoaderOption.HAS_DEBUG): + if has_debug: bundle_kind_modifier += ".debug" - return bundle_kind_modifier, is_lazy + return bundle_kind_modifier, is_lazy, has_performance, has_replay, has_debug def _get_context( self, key: Optional[ProjectKey] @@ -89,7 +96,13 @@ def _get_context( sdk_version = get_browser_sdk_version(key) - bundle_kind_modifier, is_lazy = self._get_bundle_kind_modifier(key, sdk_version) + ( + bundle_kind_modifier, + is_lazy, + has_performance, + has_replay, + has_debug, + ) = self._get_bundle_kind_modifier(key, sdk_version) js_sdk_loader_default_sdk_url_template_slot_count = ( settings.JS_SDK_LOADER_DEFAULT_SDK_URL.count("%s") @@ -108,9 +121,21 @@ def _get_context( except TypeError: sdk_url = "" # It fails if it cannot inject the version in the string + config: SdkConfig = {"dsn": key.dsn_public} + + if has_debug: + config["debug"] = True + + if has_performance: + config["tracesSampleRate"] = 0.1 + + if has_replay: + config["replaysSessionSampleRate"] = 0.1 + config["replaysOnErrorSampleRate"] = 1 + return ( { - "config": {"dsn": key.dsn_public}, + "config": config, "jsSdkUrl": sdk_url, "publicKey": key.public_key, "isLazy": is_lazy, diff --git a/tests/sentry/web/frontend/test_js_sdk_loader.py b/tests/sentry/web/frontend/test_js_sdk_loader.py index 39b3b66fafd161..7e2d35cf6d602b 100644 --- a/tests/sentry/web/frontend/test_js_sdk_loader.py +++ b/tests/sentry/web/frontend/test_js_sdk_loader.py @@ -8,6 +8,7 @@ from sentry.loader.dynamic_sdk_options import DynamicSdkLoaderOption from sentry.testutils import TestCase +from sentry.utils import json class JavaScriptSdkLoaderTest(TestCase): @@ -113,7 +114,9 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse settings.JS_SDK_LOADER_DEFAULT_SDK_URL = "https://browser.sentry-cdn.com/%s/bundle%s.min.js" settings.JS_SDK_LOADER_SDK_VERSION = "7.32.0" - for data, expected in [ + dsn = self.projectkey.get_dsn(public=True) + + for data, expected_bundle, expected_options in [ ( { "dynamicSdkLoaderOptions": { @@ -121,6 +124,7 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse } }, b"/7.37.0/bundle.tracing.es5.min.js", + {"dsn": dsn, "tracesSampleRate": 0.1}, ), ( { @@ -129,6 +133,7 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse } }, b"/7.37.0/bundle.es5.debug.min.js", + {"dsn": dsn, "debug": True}, ), ( { @@ -137,6 +142,7 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse } }, b"/7.37.0/bundle.replay.min.js", + {"dsn": dsn, "replaysSessionSampleRate": 0.1, "replaysOnErrorSampleRate": 1}, ), ( { @@ -146,6 +152,12 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse } }, b"/7.37.0/bundle.tracing.replay.min.js", + { + "dsn": dsn, + "tracesSampleRate": 0.1, + "replaysSessionSampleRate": 0.1, + "replaysOnErrorSampleRate": 1, + }, ), ( { @@ -155,6 +167,12 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse } }, b"/7.37.0/bundle.replay.debug.min.js", + { + "dsn": dsn, + "replaysSessionSampleRate": 0.1, + "replaysOnErrorSampleRate": 1, + "debug": True, + }, ), ( { @@ -164,6 +182,7 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse } }, b"/7.37.0/bundle.tracing.es5.debug.min.js", + {"dsn": dsn, "tracesSampleRate": 0.1, "debug": True}, ), ( { @@ -174,6 +193,13 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse } }, b"/7.37.0/bundle.tracing.replay.debug.min.js", + { + "dsn": dsn, + "tracesSampleRate": 0.1, + "replaysSessionSampleRate": 0.1, + "replaysOnErrorSampleRate": 1, + "debug": True, + }, ), ]: self.projectkey.data = data @@ -181,7 +207,13 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse resp = self.client.get(self.path) assert resp.status_code == 200 self.assertTemplateUsed(resp, "sentry/js-sdk-loader.js.tmpl") - assert expected in resp.content + assert expected_bundle in resp.content + + for key in expected_options: + # Convert to e.g. "option_name": 0.1 + single_option = {key: expected_options[key]} + assert bytes(json.dumps(single_option)[1:-1], "utf-8") in resp.content + self.projectkey.data = {} self.projectkey.save() From ecd3e151bd898b2a4abab674e05a715fee72e121 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 28 Mar 2023 16:26:20 +0200 Subject: [PATCH 2/8] add replay integation --- src/sentry/templates/sentry/js-sdk-loader.js.tmpl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sentry/templates/sentry/js-sdk-loader.js.tmpl b/src/sentry/templates/sentry/js-sdk-loader.js.tmpl index e2767e79b543a3..2a7bd054cc63d4 100644 --- a/src/sentry/templates/sentry/js-sdk-loader.js.tmpl +++ b/src/sentry/templates/sentry/js-sdk-loader.js.tmpl @@ -80,9 +80,18 @@ var oldInit = SDK.init; - if (_config.tracesSampleRate) { - _config.integrations = [new Sentry.BrowserTracing()]; - } + var integrations = []; + if (_config.tracesSampleRate) { + integrations.push(new Sentry.BrowserTracing()); + } + + if (_config.replaysSessionSampleRate || _config.replaysOnErrorSampleRate) { + integrations.push(new Sentry.Replay()); + } + + if(integrations.length) { + _config.integrations = integrations; + } // Configure it using provided DSN and config object SDK.init = function(options) { From 82d2788830518a25eb4fe433c3f10a78ac6725b0 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 28 Mar 2023 16:30:09 +0200 Subject: [PATCH 3/8] add minified loader --- src/sentry/templates/sentry/js-sdk-loader.js.tmpl | 2 +- src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sentry/templates/sentry/js-sdk-loader.js.tmpl b/src/sentry/templates/sentry/js-sdk-loader.js.tmpl index 2a7bd054cc63d4..349e9871521914 100644 --- a/src/sentry/templates/sentry/js-sdk-loader.js.tmpl +++ b/src/sentry/templates/sentry/js-sdk-loader.js.tmpl @@ -89,7 +89,7 @@ integrations.push(new Sentry.Replay()); } - if(integrations.length) { + if (integrations.length) { _config.integrations = integrations; } diff --git a/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl b/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl index 90ab4591d64ddb..1b998b7b0c7eb9 100644 --- a/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl +++ b/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl @@ -1,4 +1,4 @@ -{% load sentry_helpers %}(function(c,x,D,q,r,k,E,F,t,l){function m(a){("e"in a||"p"in a||a.f&&-1 Date: Thu, 30 Mar 2023 09:53:45 +0200 Subject: [PATCH 4/8] loader adjustments --- .../templates/sentry/js-sdk-loader.js.tmpl | 16 ++++++---------- .../templates/sentry/js-sdk-loader.min.js.tmpl | 8 ++++---- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/sentry/templates/sentry/js-sdk-loader.js.tmpl b/src/sentry/templates/sentry/js-sdk-loader.js.tmpl index 349e9871521914..443629778fe585 100644 --- a/src/sentry/templates/sentry/js-sdk-loader.js.tmpl +++ b/src/sentry/templates/sentry/js-sdk-loader.js.tmpl @@ -105,12 +105,6 @@ }; sdkLoaded(callbacks, SDK); - - // Ensure we load the SDK in non-lazy mode, even if no Sentry.onLoad() was provided - // If it were provided, it would have already been called by sdkLoaded() above. - if (!sdkIsLoaded() && !lazy) { - SDK.init(); - } } catch (o_O) { console.error(o_O); } @@ -122,13 +116,11 @@ function sdkIsLoaded() { var __sentry = _window['__SENTRY__']; // If there is a global __SENTRY__ that means that in any of the callbacks init() was already invoked - return !(typeof __sentry === 'undefined') && __sentry.hub && __sentry.hub.getClient(); + return !!(!(typeof __sentry === 'undefined') && __sentry.hub && __sentry.hub.getClient()); } function sdkLoaded(callbacks, SDK) { try { - var data = queue.data; - // We have to make sure to call all callbacks first for (var i = 0; i < callbacks.length; i++) { if (typeof callbacks[i] === 'function') { @@ -136,8 +128,13 @@ } } + var data = queue.data; + var initAlreadyCalled = sdkIsLoaded(); + // Call init first, if provided + data.sort((a, b) => a.f === 'init' ? -1 : 1); + // We want to replay all calls to Sentry and also make sure that `init` is called if it wasn't already // We replay all calls to `Sentry.*` now var calledSentry = false; @@ -196,7 +193,6 @@ } }; - [ 'init', 'addBreadcrumb', diff --git a/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl b/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl index 1b998b7b0c7eb9..e29d4f3f3fc03c 100644 --- a/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl +++ b/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl @@ -1,4 +1,4 @@ -{% load sentry_helpers %}(function(c,y,D,t,u,k,E,F,l,m){function v(a){if(!z){z=!0;var f=y.scripts[0],d=y.createElement(D);d.src=F;d.crossOrigin="anonymous";d.addEventListener("load",function(){try{c[t]=w;c[u]=x;c.SENTRY_SDK_SOURCE="loader";var b=c[k],n=b.init,g=[];l.tracesSampleRate&&g.push(new Sentry.BrowserTracing);(l.replaysSessionSampleRate||l.replaysOnErrorSampleRate)&&g.push(new Sentry.Replay);g.length&&(l.integrations=g);b.init=function(e){for(var p in e)Object.prototype.hasOwnProperty.call(e,p)&&(l[p]=e[p]);n(l)}; -G(a,b);A()||h||b.init()}catch(e){console.error(e)}});f.parentNode.insertBefore(d,f)}}function A(){var a=c.__SENTRY__;return"undefined"!==typeof a&&a.hub&&a.hub.getClient()}function G(a,f){try{for(var d=q.data,b=0;b Date: Thu, 30 Mar 2023 14:49:52 +0200 Subject: [PATCH 5/8] update loader script --- .../templates/sentry/js-sdk-loader.js.tmpl | 18 ++++++++++++------ .../templates/sentry/js-sdk-loader.min.js.tmpl | 8 ++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/sentry/templates/sentry/js-sdk-loader.js.tmpl b/src/sentry/templates/sentry/js-sdk-loader.js.tmpl index 443629778fe585..8e933c5387b44b 100644 --- a/src/sentry/templates/sentry/js-sdk-loader.js.tmpl +++ b/src/sentry/templates/sentry/js-sdk-loader.js.tmpl @@ -67,11 +67,15 @@ _newScriptTag.crossOrigin = 'anonymous'; // Once our SDK is loaded - _newScriptTag.addEventListener('load', function() { + _newScriptTag.addEventListener('load', function () { try { - // Restore onerror/onunhandledrejection handlers - _window[_onerror] = _oldOnerror; - _window[_onunhandledrejection] = _oldOnunhandledrejection; + // Restore onerror/onunhandledrejection handlers - only if not mutated in the meanwhile + if (_window[_onerror] && _window[_onerror].__SENTRY_LOADER__) { + _window[_onerror] = _oldOnerror; + } + if (_window[_onunhandledrejection] && _window[_onunhandledrejection].__SENTRY_LOADER__) { + _window[_onunhandledrejection] = _oldOnunhandledrejection; + } // Add loader as SDK source _window.SENTRY_SDK_SOURCE = 'loader'; @@ -133,7 +137,7 @@ var initAlreadyCalled = sdkIsLoaded(); // Call init first, if provided - data.sort((a, b) => a.f === 'init' ? -1 : 1); + data.sort((a, b) => a.f === 'init' ? -1 : 0); // We want to replay all calls to Sentry and also make sure that `init` is called if it wasn't already // We replay all calls to `Sentry.*` now @@ -211,7 +215,7 @@ // Store reference to the old `onerror` handler and override it with our own function // that will just push exceptions to the queue and call through old handler if we found one var _oldOnerror = _window[_onerror]; - _window[_onerror] = function(message, source, lineno, colno, exception) { + _window[_onerror] = function() { // Use keys as "data type" to save some characters" queue({ e: [].slice.call(arguments) @@ -219,6 +223,7 @@ if (_oldOnerror) _oldOnerror.apply(_window, arguments); }; + _window[_onerror].__SENTRY_LOADER__ = true; // Do the same store/queue/call operations for `onunhandledrejection` event var _oldOnunhandledrejection = _window[_onunhandledrejection]; @@ -228,6 +233,7 @@ }); if (_oldOnunhandledrejection) _oldOnunhandledrejection.apply(_window, arguments); }; + _window[_onunhandledrejection].__SENTRY_LOADER__ = true; if (!lazy) { setTimeout(function () { diff --git a/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl b/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl index e29d4f3f3fc03c..d45f71e760f234 100644 --- a/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl +++ b/src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl @@ -1,4 +1,4 @@ -{% load sentry_helpers %}(function(c,z,D,t,u,h,E,F,k,l){function m(a){("e"in a||"p"in a||a.f&&-1 Date: Thu, 30 Mar 2023 16:53:25 +0200 Subject: [PATCH 6/8] fix loader for performance to v7 --- src/sentry/web/frontend/js_sdk_loader.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sentry/web/frontend/js_sdk_loader.py b/src/sentry/web/frontend/js_sdk_loader.py index b3b93887f8d123..9c814a6181bf6a 100644 --- a/src/sentry/web/frontend/js_sdk_loader.py +++ b/src/sentry/web/frontend/js_sdk_loader.py @@ -59,7 +59,8 @@ def _get_bundle_kind_modifier( # bundle name is built up from left to right. # https://docs.sentry.io/platforms/javascript/install/cdn/ - if has_performance: + # We depend on fixes in the tracing bundle that are only available in v7 + if is_v7_sdk and has_performance: bundle_kind_modifier += ".tracing" is_lazy = False From 55d73e359f22c7f53d207bbeeef9ee6123155a96 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 31 Mar 2023 10:39:59 +0200 Subject: [PATCH 7/8] Update src/sentry/web/frontend/js_sdk_loader.py Co-authored-by: Daniel Griesser --- src/sentry/web/frontend/js_sdk_loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/web/frontend/js_sdk_loader.py b/src/sentry/web/frontend/js_sdk_loader.py index 9c814a6181bf6a..3a3e3522a1dc40 100644 --- a/src/sentry/web/frontend/js_sdk_loader.py +++ b/src/sentry/web/frontend/js_sdk_loader.py @@ -128,7 +128,7 @@ def _get_context( config["debug"] = True if has_performance: - config["tracesSampleRate"] = 0.1 + config["tracesSampleRate"] = 1 if has_replay: config["replaysSessionSampleRate"] = 0.1 From 6d3ef015ffdda0178c24d3db3a268e08df797e1c Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 31 Mar 2023 11:08:19 +0200 Subject: [PATCH 8/8] fix test --- tests/sentry/web/frontend/test_js_sdk_loader.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/sentry/web/frontend/test_js_sdk_loader.py b/tests/sentry/web/frontend/test_js_sdk_loader.py index 7e2d35cf6d602b..bb012aa708568a 100644 --- a/tests/sentry/web/frontend/test_js_sdk_loader.py +++ b/tests/sentry/web/frontend/test_js_sdk_loader.py @@ -124,7 +124,7 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse } }, b"/7.37.0/bundle.tracing.es5.min.js", - {"dsn": dsn, "tracesSampleRate": 0.1}, + {"dsn": dsn, "tracesSampleRate": 1}, ), ( { @@ -154,7 +154,7 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse b"/7.37.0/bundle.tracing.replay.min.js", { "dsn": dsn, - "tracesSampleRate": 0.1, + "tracesSampleRate": 1, "replaysSessionSampleRate": 0.1, "replaysOnErrorSampleRate": 1, }, @@ -182,7 +182,7 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse } }, b"/7.37.0/bundle.tracing.es5.debug.min.js", - {"dsn": dsn, "tracesSampleRate": 0.1, "debug": True}, + {"dsn": dsn, "tracesSampleRate": 1, "debug": True}, ), ( { @@ -195,7 +195,7 @@ def test_bundle_kind_modifiers(self, load_version_from_file, get_selected_browse b"/7.37.0/bundle.tracing.replay.debug.min.js", { "dsn": dsn, - "tracesSampleRate": 0.1, + "tracesSampleRate": 1, "replaysSessionSampleRate": 0.1, "replaysOnErrorSampleRate": 1, "debug": True,