Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sentry JS lazily-loadable loader doesn't work #22715

Open
Tracked by #232
fabis94 opened this issue Nov 12, 2020 · 69 comments · Fixed by getsentry/self-hosted#3415
Open
Tracked by #232

Sentry JS lazily-loadable loader doesn't work #22715

fabis94 opened this issue Nov 12, 2020 · 69 comments · Fixed by getsentry/self-hosted#3415

Comments

@fabis94
Copy link

fabis94 commented Nov 12, 2020

Version Information

Version: 20.10.1

Description

According to the documentation (https://docs.sentry.io/platforms/javascript/install/lazy-load-sentry/#sdk-version) Sentry offers a lightweight lazy-loadable version of the Sentry JS SDK which is a lot smaller than the default bundles.

I tried to use it, but when I open the script that's shown inside the "JavaScript Loader" input box as shown in the docs, the entire contents of the script look like this:

function _sentry_noopWarning() {
  console.warn("The Sentry loader you are trying to use isn't working anymore, check your configuration.");
}
var Sentry = {
    addBreadcrumb: _sentry_noopWarning,
    captureEvent: _sentry_noopWarning,
    captureException: _sentry_noopWarning,
    captureMessage: _sentry_noopWarning,
    configureScope: _sentry_noopWarning,
    forceLoad: _sentry_noopWarning,
    init: _sentry_noopWarning,
    onLoad: _sentry_noopWarning,
    showReportDialog: _sentry_noopWarning,
    withScope: _sentry_noopWarning,
};
_sentry_noopWarning();

I tried grep-ing through the Sentry source code about this, but I was only able to find the template that generates this script (https://github.com/getsentry/sentry/blob/master/src/sentry/templates/sentry/js-sdk-loader-noop.js.tmpl) and couldn't figure out further how does Sentry decide to show this broken version instead of the real script.

The documentation isn't helpful either.

Steps to Reproduce

Install Sentry on-premises and check the JavaScript Loader, I guess.

Logs

No logs

@BYK
Copy link
Member

BYK commented Nov 13, 2020

Hi, I think this is intended to be used by https://sentry.io or when you are using a CDN yourself. The definitions are here:

# CDN
# If this is an absolute url like e.g.: https://js.sentry-cdn.com/
# the full url will look like this: https://js.sentry-cdn.com/<public_key>.min.js
# otherwise django reverse url lookup will be used.
JS_SDK_LOADER_CDN_URL = ""
# Version of the SDK - Used in header Surrogate-Key sdk/JS_SDK_LOADER_SDK_VERSION
JS_SDK_LOADER_SDK_VERSION = ""
# This should be the url pointing to the JS SDK
JS_SDK_LOADER_DEFAULT_SDK_URL = ""

Can you share where you are seeing this script linked to? Maybe this is just a UI issue that we need to disable for on-premise (or when this setting is not set).

@fabis94
Copy link
Author

fabis94 commented Nov 13, 2020

@BYK Same place it's shown in the docs, in the Client Keys (DSN) settings, when pressing "Configure" on the DSN key.

image

It would be great if this feature could be used on-premises as well, since there's no other way to lazily load the JS SDK

@BYK
Copy link
Member

BYK commented Nov 13, 2020

@fabis94 thanks a lot for the quick turn around! I'll be investigating this.

@BYK BYK self-assigned this Nov 16, 2020
@christopherowen
Copy link

I can confirm that I am seeing this is the latest release of on premise as well.

Like the original poster, I followed the same path and was led to this ticket.

I’d be happy to debug or offer feedback.

@christopherowen
Copy link

I've tried a number of attempts at setting system.JS_SDK_LOADER_DEFAULT_SDK_URL in sentry/config.yml and sentry/sentry.conf.py without luck so far.

I receive various versions of "Unknown config option found:" in the logs.

@christopherowen
Copy link

I was able to get this working by setting the following in sentry/sentry.conf.py

JS_SDK_LOADER_DEFAULT_SDK_URL = "https://browser.sentry-cdn.com/%s/bundle.tracing.min.js"

May I suggest that this be set as a default in the sentry.conf.example.py?

@fabis94
Copy link
Author

fabis94 commented Dec 15, 2020

@christopherowen Is that really lazy loading, though? It looks like it just loads the standard bundle instead of the specialized lazy-loadable version that is supposedly served by the cloud version of Sentry.

@christopherowen
Copy link

yes it is, this url is loaded by the lazy loader.

@BYK BYK transferred this issue from getsentry/self-hosted Dec 16, 2020
@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2021

This issue has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you label it Status: Accepted, I will leave it alone ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

@maxkachalin
Copy link

maxkachalin commented Apr 3, 2021

Any updates on this? Problem is still here in Sentry 21.4.0.dev07313f65.

@BYK
Copy link
Member

BYK commented Apr 5, 2021

@maxkachalin it is on our backlog, just didn't have time to get to it yet.

@maxkachalin
Copy link

@BYK, I see, thank you very much in advance.

I've been tangled by the bot which closed issue due to inactivity.

@caioricciuti
Copy link

Same here! Following @christopherowen suggestion, it worked! Thanks!

@pauloriply
Copy link

Hi all! Any workaround for this issue at this moment?

@dakur
Copy link

dakur commented Sep 8, 2021

@pauloriply See #22715 (comment)

@Daniel15
Copy link

Daniel15 commented Sep 24, 2021

I was able to get this working by setting the following in sentry/sentry.conf.py

JS_SDK_LOADER_DEFAULT_SDK_URL = "https://browser.sentry-cdn.com/%s/bundle.tracing.min.js"

The downside of this approach is that the SDK is loading from a third-party CDN, which is not ideal in some cases (eg. slower load time due to having to resolve one extra hostname, you can't customize the code at all, regulatory requirements with regards to third-party servers, etc).

Until this is fixed, I'm going to try lazy loading via Webpack's lazy loading support. I've got this so far:

Edit: Code is in a Github repo now: https://github.com/Daniel15/LazySentry

// LazySentryImport.ts
// Only export the things you use, to try and keep bundle size small (tree shaking)

import {Integrations} from '@sentry/tracing';
const {BrowserTracing} = Integrations;

export {
  init,
  ErrorBoundary,
  addBreadcrumb,
  captureMessage,
  captureException,
  captureEvent,
  configureScope,
  withScope,
} from '@sentry/react';
export {BrowserTracing};
// LazySentry.ts

type SentryImportType = typeof import('./LazySentryImports');

const isDev = window.location.hostname.includes('.localdev.');

let queue: Array<(sentry: SentryImportType) => void> = [];
let errorQueue: Array<Parameters<OnErrorEventHandlerNonNull>> = [];
let rejectionQueue: Array<PromiseRejectionEvent> = [];

// Before Sentry has loaded, these functions will push calls into a queue
// After Sentry has loaded, these will be replaced with the real functions
export let addBreadcrumb: SentryImportType['addBreadcrumb'] = (...args) => {
  queue.push(x => x.addBreadcrumb(...args));
};
export let captureMessage: SentryImportType['captureMessage'] = (...args) => {
  queue.push(x => x.captureMessage(...args));
  return '';
};
export let captureException: SentryImportType['captureException'] = (
  ...args
) => {
  queue.push(x => x.captureException(...args));
  return '';
};
export let captureEvent: SentryImportType['captureEvent'] = (...args) => {
  queue.push(x => x.captureEvent(...args));
  return '';
};
export let configureScope: SentryImportType['configureScope'] = (...args) =>
  queue.push(x => x.configureScope(...args));
export let withScope: SentryImportType['withScope'] = (...args) =>
  queue.push(x => x.withScope(...args));

export function initLazy() {
  const oldOnError = window.onerror;
  const oldOnUnhandledRejection = window.onunhandledrejection;
  window.onerror = (...args) => errorQueue.push(args);
  window.onunhandledrejection = (e: PromiseRejectionEvent) =>
    rejectionQueue.push(e);

  import('./LazySentryImports').then(Sentry => {
    window.onerror = oldOnError;
    window.onunhandledrejection = oldOnUnhandledRejection;
    Sentry.init({
      dsn: 'https://xxxxxxxxxx/3',
      debug: isDev,
      environment: isDev ? 'development' : 'production',
      integrations: [new Sentry.BrowserTracing()],
      tracesSampleRate: 1.0,
    });

    // Override the placeholder functions with the real ones
    addBreadcrumb = Sentry.addBreadcrumb;
    captureMessage = Sentry.captureMessage;
    captureException = Sentry.captureException;
    captureEvent = Sentry.captureEvent;
    configureScope = Sentry.configureScope;
    withScope = Sentry.withScope;
    // TODO: React ErrorBoundary

    // Replay queued calls and errors through Sentry's handlers
    queue.forEach(call => call(Sentry));
    errorQueue.forEach(x => window.onerror?.(...x));
    rejectionQueue.forEach(e => window.onunhandledrejection?.(e));
  });
}

Then just use the LazySentry module in your app and call initLazy somewhere (eg after page load, in a requestIdleCallback callback, etc). Basically the same as the Sentry lazy loader (https://github.com/getsentry/sentry-javascript/blob/master/packages/browser/src/loader.js) but with more modern JS, less obfuscated, TypeScript typing, and lazy loading your own built JS file rather than using the Sentry CDN.

No guarantees on the above code and I still need to finish testing it, but on an initial test it seems to work.

@aldy505
Copy link
Contributor

aldy505 commented Oct 7, 2024

Okay so here's another PR to enable hosting the JS SDK from your own instance: getsentry/self-hosted#3365

@aldy505
Copy link
Contributor

aldy505 commented Oct 24, 2024

Been 2 weeks. But has anyone enabled this on their instance (starting 24.10.0) yet? Any good news?

https://github.com/getsentry/self-hosted/blob/8fd24d02312f9fd7990c1ad0808d561c7b4f80b5/.env#L20-L21

Set that ☝ to SETUP_JS_SDK_ASSETS=1. With optional hidden feature flag of SETUP_JS_SDK_KEEP_OLD_ASSETS=1 that keeps the old assets so the JS SDK cleanup script won't be executed.

https://github.com/getsentry/self-hosted/blob/8fd24d02312f9fd7990c1ad0808d561c7b4f80b5/sentry/sentry.conf.example.py#L381-L391

The URL should include the /js-sdk prefix like so:

JS_SDK_LOADER_DEFAULT_SDK_URL = "https://sentry.example.com/js-sdk/%s/bundle%s.min.js"

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Oct 24, 2024
@gander
Copy link

gander commented Oct 24, 2024

I forgot to turn it on during the upgrade. When I set everything up and wanted to run the package installation to get it started, it turned out that the installer is broken and could not be started during the installation. So, I waiting for a newer version to enable it during the next upgrade.

@aldy505
Copy link
Contributor

aldy505 commented Oct 24, 2024

I forgot to turn it on during the upgrade. When I set everything up and wanted to run the package installation to get it started, it turned out that the installer is broken and could not be started during the installation. So, I waiting for a newer version to enable it during the next upgrade.

@gander Can you share the "broken installer" message?

@gander
Copy link

gander commented Oct 24, 2024

I forgot to turn it on during the upgrade. When I set everything up and wanted to run the package installation to get it started, it turned out that the installer is broken and could not be started during the installation. So, I waiting for a newer version to enable it during the next upgrade.

@gander Can you share the "broken installer" message?

getsentry/self-hosted#3393

@gander
Copy link

gander commented Oct 24, 2024

Is this what it was supposed to look like?

!function (n, e, r, t, o, i, a, c, s) {
    for (var u = s, f = 0; f < document.scripts.length; f++) if (document.scripts[f].src.indexOf(i) > -1) {
        u && "no" === document.scripts[f].getAttribute("data-lazy") && (u = !1);
        break;
    }
    var p = [];

    function l(n) {
        return "e" in n;
    }

    function d(n) {
        return "p" in n;
    }

    function _(n) {
        return "f" in n;
    }

    var v = [];

    function y(n) {
        u && (l(n) || d(n) || _(n) && n.f.indexOf("capture") > -1 || _(n) && n.f.indexOf("showReportDialog") > -1) && L(), v.push(n);
    }

    function h() {
        y({e: [].slice.call(arguments)});
    }

    function g(n) {
        y({p: n});
    }

    function E() {
        try {
            n.SENTRY_SDK_SOURCE = "loader";
            var e = n[o], i = e.init;
            e.init = function (o) {
                n.removeEventListener(r, h), n.removeEventListener(t, g);
                var a = c;
                for (var s in o) Object.prototype.hasOwnProperty.call(o, s) && (a[s] = o[s]);
                !function (n, e) {
                    var r = n.integrations || [];
                    if (!Array.isArray(r)) return;
                    var t = r.map((function (n) {
                        return n.name;
                    }));
                    n.tracesSampleRate && -1 === t.indexOf("BrowserTracing") && (e.browserTracingIntegration ? r.push(e.browserTracingIntegration({enableInp: !0})) : e.BrowserTracing && r.push(new e.BrowserTracing));
                    (n.replaysSessionSampleRate || n.replaysOnErrorSampleRate) && -1 === t.indexOf("Replay") && (e.replayIntegration ? r.push(e.replayIntegration()) : e.Replay && r.push(new e.Replay));
                    n.integrations = r;
                }(a, e), i(a);
            }, setTimeout((function () {
                return function (e) {
                    try {
                        "function" == typeof n.sentryOnLoad && (n.sentryOnLoad(), n.sentryOnLoad = void 0);
                    } catch (n) {
                        console.error("Error while calling `sentryOnLoad` handler:"), console.error(n);
                    }
                    try {
                        for (var r = 0; r < p.length; r++) "function" == typeof p[r] && p[r]();
                        p.splice(0);
                        for (r = 0; r < v.length; r++) {
                            _(i = v[r]) && "init" === i.f && e.init.apply(e, i.a);
                        }
                        m() || e.init();
                        var t = n.onerror, o = n.onunhandledrejection;
                        for (r = 0; r < v.length; r++) {
                            var i;
                            if (_(i = v[r])) {
                                if ("init" === i.f) continue;
                                e[i.f].apply(e, i.a);
                            } else l(i) && t ? t.apply(n, i.e) : d(i) && o && o.apply(n, [i.p]);
                        }
                    } catch (n) {
                        console.error(n);
                    }
                }(e);
            }));
        } catch (n) {
            console.error(n);
        }
    }

    var O = !1;

    function L() {
        if (!O) {
            O = !0;
            var n = e.scripts[0], r = e.createElement("script");
            r.src = a, r.crossOrigin = "anonymous", r.addEventListener("load", E, {once: !0, passive: !0}), n.parentNode.insertBefore(r, n);
        }
    }

    function m() {
        var e = n.__SENTRY__, r = void 0 !== e && e.version;
        return r ? !!e[r] : !(void 0 === e || !e.hub || !e.hub.getClient());
    }

    n[o] = n[o] || {}, n[o].onLoad = function (n) {
        m() ? n() : p.push(n);
    }, n[o].forceLoad = function () {
        setTimeout((function () {
            L();
        }));
    }, ["init", "addBreadcrumb", "captureMessage", "captureException", "captureEvent", "configureScope", "withScope", "showReportDialog"].forEach((function (e) {
        n[o][e] = function () {
            y({f: e, a: arguments});
        };
    })), n.addEventListener(r, h), n.addEventListener(t, g), u || setTimeout((function () {
        L();
    }));
}(window, document, "error", "unhandledrejection", "Sentry", 'REDACTED', 'https://browser.sentry-cdn.com/8.34.0/bundle.tracing.replay.debug.min.js', {"dsn": "https://[email protected]/3", "debug": true, "tracesSampleRate": 1, "replaysSessionSampleRate": 0.1, "replaysOnErrorSampleRate": 1}, false);

@dlouzan
Copy link

dlouzan commented Nov 5, 2024

@BYK @aldy505 I'm not sure the new feature introduced in getsentry/self-hosted#3365 is working as intended for self-hosting. I tried adapting the feature into our self-hosted setup to make it not depend on the external CDN, and I'm seeing the following issues:

  • The sdk install script is only installing the 7.x & 8.x versions to be served by the self-hosted nginx, but when going to a project configuration, any user of our system can see sdk versions going all the way back to 4.x, so that will probably cause issues for people that expect that those versions still work (see screenshot below)
  • The script is also downloading several variants of the js files (bundle, feedback, replay, etc), but not the base unnamed variants that end up as {version}/bundle.min.js and that are the defaults selected when no config options are chosen on the web UI.

Image

If I'm missing anything here, I'll be happy to learn something. Thanks!

/cc @max-wittig @ercanucan

@aldy505
Copy link
Contributor

aldy505 commented Nov 6, 2024

  • The sdk install script is only installing the 7.x & 8.x versions to be served by the self-hosted nginx, but when going to a project configuration, any user of our system can see sdk versions going all the way back to 4.x, so that will probably cause issues for people that expect that those versions still work (see screenshot below)

@dlouzan Interesting to see that your dropdown has 6.x and lower. I need to find out what's giving that output, but probably what we need to do best is to provide the SDK version all the way to 4.x.

Image

  • The script is also downloading several variants of the js files (bundle, feedback, replay, etc), but not the base unnamed variants that end up as {version}/bundle.min.js and that are the defaults chosen when no config options are chosen on the web UI.

This is easy, will add that to the script! Thanks for giving these feedbacks!

@dlouzan
Copy link

dlouzan commented Nov 6, 2024

I made a comment on the PR you linked, but for completeness, there was another issue: busybox's wget does not play nice with our corporate proxy, so I replaced the part in the script with curl.

BYK pushed a commit to getsentry/self-hosted that referenced this issue Nov 7, 2024
Hopefully fixes getsentry/sentry#22715 (comment)

### Legal Boilerplate

Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. and is gonna need some rights from me in order to utilize my contributions in this here PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.
@aldy505
Copy link
Contributor

aldy505 commented Nov 7, 2024

@BYK can you reopen this ticket?

@BYK BYK reopened this Nov 7, 2024
@aldy505
Copy link
Contributor

aldy505 commented Nov 19, 2024

@dlouzan Hey! (and to a bunch of people subscribing to the issue here) 24.11.0 has been released. Would you mind try upgrading & check if the implementation is working as expected? https://github.com/getsentry/self-hosted/releases/tag/24.11.0

@gander
Copy link

gander commented Nov 19, 2024

what should I expect? nothing has changed since 2-3 versions, but I really don't know what should be visible there, because no one has explained the effect of the changes comprehensively and unambiguously. when someone finally comes along to explain in a simple way what the effect of the changes should look like, then I can confirm or deny whether it works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Archived in project
Status: No status
Development

Successfully merging a pull request may close this issue.