getsentry / sentry

Developer-first error tracking and performance monitoring
https://sentry.io
Other
38.61k stars 4.13k forks source link

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

Open fabis94 opened 3 years ago

fabis94 commented 3 years ago

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 commented 3 years ago

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: https://github.com/getsentry/sentry/blob/c3da5bd066b6f30358a455b78eab4ba075c0acf9/src/sentry/conf/server.py#L1862-L1870

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 commented 3 years ago

@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 commented 3 years ago

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

christopherowen commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

@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 commented 3 years ago

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

github-actions[bot] commented 3 years ago

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 commented 3 years ago

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

BYK commented 3 years ago

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

maxkachalin commented 3 years ago

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

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

caioricciuti commented 3 years ago

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

pauloriply commented 3 years ago

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

dakur commented 3 years ago

@pauloriply See https://github.com/getsentry/sentry/issues/22715#issuecomment-745151393

Daniel15 commented 2 years ago

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.

saint777 commented 2 years ago

@Daniel15 this is not bad at all!

Daniel15 commented 2 years ago

@Daniel15 this is not bad at all!

Thanks 😃 I started using it on https://dnstools.ws/ (https://github.com/Daniel15/dnstools/blob/9c51a38d1a86b84c9f13f940ff58108bd4468b1d/src/DnsTools.Web/ClientApp/src/index.tsx#L38) and it seems to be working fine.

Stormiks commented 2 years ago

I am new to Sentry. But I also ran into a similar issue when using it with Django + Vue.JS. Since in the current project it is necessary that the backend transfer the necessary configuration for the Sentry SDK in advance before returning the statics. When is a patch like this planned to be released?

makstech commented 2 years ago

Have there been any updates? Would be nice to have a working feature without workarounds

Aulig commented 2 years ago

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?

Didn't work for me. It correctly lazily loaded the bundle when an error occurred but didn't report the issue (there was no request happening in the browser console and therefore it obviously also didn't show up in sentry).

Got it to work by loading bundle.min.js instead of bundle.tracing.min.js.

JS_SDK_LOADER_DEFAULT_SDK_URL = "https://browser.sentry-cdn.com/%s/bundle.min.js"
zengzuo613 commented 1 year ago

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?

Didn't work for me. It correctly lazily loaded the bundle when an error occurred but didn't report the issue (there was no request happening in the browser console and therefore it obviously also didn't show up in sentry).

Got it to work by loading bundle.min.js instead of bundle.tracing.min.js.

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

That's right

itsmunim commented 1 year ago

Is this still in backlog? 😢

MoritzGiessmann commented 1 year ago

Same problem here. CDN is not an option for me for security and performance reasons.

hubertdeng123 commented 1 year ago

Yep, still on the backlog

MoritzGiessmann commented 1 year ago

Do you have any intention of fixing this in the near future? Asking because otherwise I need to search for another solution.

Daniel15 commented 1 year ago

otherwise I need to search for another solution.

@MoritzGiessmann You could do something similar to what I'm doing, as a workaround: https://github.com/Daniel15/LazySentry. I'm just using Webpack's lazy loading to load the Sentry code, and keeping all errors and calls to Sentry in a queue that's flushed once Sentry finishes loading.

hubertdeng123 commented 1 year ago

Do you have any intention of fixing this in the near future? Asking because otherwise I need to search for another solution.

Unfortunately not. You're welcome to try out the workaround mentioned above though and report back.

victorelec14 commented 1 year ago

I have the same problem, on the sentry website I can open the js without problems, but on-premise it doesn't let me see the content.

Sentry.io: image image

On-Premise ( Self-hosted):

image

Thanks

juslintek commented 1 year ago

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?

Now I am having this problem: I had problem on ⁠self-hosted with sentry script being noop functions. But fixed this by adding:

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

to

sentry/sentry.conf.py

using docker setup and then rerunning ./install.sh, I am on main branch.

But now I am having another issue, which is causing envelope to show 403, read on some github issues, that it is deprecated endpoint or something like that, though using latest bundle.

Any ideas anyone how to fix this? Response on sentry itself is 403 from web server. And when I using script provided by my self-hosted instance. It points to:

{{self-hosted.domain}}/api/1/envelope/?sentry_key={{project_token}}&sentry_version=7&sentry_client=sentry.javascript.browser%2F7.64.0

which responds also with 403 HTTP code. But also provides this payload:

{"detail":"event submission rejected with_reason: Cors"}

In browser response headers seem to allow all origins and all headers. Not sure where this is coming from and how to fix it.

P.S. Script request returns proper header:

curl --head https://{{self-hosted.domain}}/js-sdk-loader/{{project_token}}.min.js | grep -i access
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0  2479    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
Access-Control-Allow-Origin: *

UPDATE: Did everything without loader with script directly from CDN. By configuring everything with Sentry class/prototype. But it seems that it is not sending any requests at all. Even though integrations and specified my self-hosted instance URL. No errors are made, due to the fact, that nothing is sent and I can see it from networks tab, by filtering sentry keyword or domain: *sentry*

SOLVED: Solved by disabling Verify SSL/TLS In projects General Settings. Not sure what is happening, but seems like sentry is unable to verify some ssl certificate. I use Let's Encrypt ones. But they are set at nginx proxy and service that is being monitored. No sure what I have to configure on sentry.conf.py or conf.py so it would work with verifications.

chadwhitacre commented 1 year ago

SOLVED:

Does this mean solved solved? Like, we can close out this issue now? 😬

chadwhitacre commented 1 year ago

Also is there a reason why this issue is here in sentry and not in sentry-javascript?

juslintek commented 1 year ago

Also is there a reason why this issue is here in sentry and not in sentry-javascript?

IMHO do not think that javascript has control over cors headers that come from server. And response comes from backend not from frontend. I might be wrong.

getsantry[bot] commented 1 year ago

Routing to @getsentry/product-owners-apis for triage ⏲️

tieb62 commented 1 year ago

Any news ?

hubertdeng123 commented 1 year ago

No news. I can add the line JS_SDK_LOADER_DEFAULT_SDK_URL = "https://browser.sentry-cdn.com/%s/bundle%s.min.js" into the sentry.conf.exmaple.py that ships with self-hosted to give more visibility though.

SDohle commented 9 months ago

Hi, any news?

azaslavsky commented 9 months ago

No news - this isn't something we're actively looking at at the moment. PRs are welcome from folks that would like to try and debug though.

reza305z commented 8 months ago

I have the same problem, on the sentry website I can open the js without problems, but on-premise it doesn't let me see the content.

Sentry.io: image image

On-Premise ( Self-hosted):

image

Thanks

I have the same issue.

MaewenPell commented 8 months ago

I have the same problem, on the sentry website I can open the js without problems, but on-premise it doesn't let me see the content. Sentry.io: image image On-Premise ( Self-hosted): image Thanks

I have the same issue.

Exacty the same on my side aswell

gander commented 7 months ago

Still waiting.

ganyanchuan1989 commented 7 months ago

Exacty the same on my side aswell

use self-hosted ,have the same problem

ganyanchuan1989 commented 7 months ago

i see code:https://github.com/getsentry/sentry/blob/e18f0922fe90eeb2a542528892869aba8de42929/src/sentry/web/frontend/js_sdk_loader.py#L184

JS_SDK_LOADER_DEFAULT_SDK_URL is not found. i use self-hosted , How should I configure it in the .env file ?

hubertdeng123 commented 7 months ago

We have still not worked on this. Thank you for your patience.

gander commented 7 months ago

Is this still in backlog? 😢 Originally posted by @dibosh in https://github.com/getsentry/sentry/issues/22715#issuecomment-1645079243

Yep, still on the backlog Originally posted by @hubertdeng123 in https://github.com/getsentry/sentry/issues/22715#issuecomment-1650271724

No, this is in never-gonna-happen backlog 😟


We have still not worked on this. Thank you for your patience. Originally posted by @hubertdeng123 in https://github.com/getsentry/sentry/issues/22715#issuecomment-1920015457

No news. I can add the line JS_SDK_LOADER_DEFAULT_SDK_URL = "https://browser.sentry-cdn.com/%s/bundle%s.min.js" into the sentry.conf.exmaple.py that ships with self-hosted to give more visibility though. Originally posted by @hubertdeng123 in https://github.com/getsentry/sentry/issues/22715#issuecomment-1726408337

You wrote this 4 months ago.

chadwhitacre commented 7 months ago

I put this on our backlog for this quarter. Wish us luck! 😅

aldy505 commented 3 months ago

The quarter of Q1 2024 has passed. Since someone on Discord pointed this out again, so..

@hubertdeng123 @azaslavsky Can you guys help by asking around internally about how this is being done on the infra team? Things like:

One solution that I can think of is that we can make a pretty simple and small service (let's do Rust for its' small footprint) that run as a single container, that resolves the URL for js-loader and CDN stuff. Then we can route those js-loader endpoint on the self-hosted's nginx.conf file.

Steps that can be done by that "simple and small service for serving browser bundles":

  1. Clone the getsentry/sentry-javascript repo, then run this script: https://github.com/getsentry/sentry-javascript/blob/93c467d6a0babb02fc28ca325c50767cb924a25a/packages/browser/package.json#L65
  2. From the generated JS browser bundle, we can host them without the Sentry CDN, grab that URL.
  3. On the code, generate the loader script from this template, acquire the DSN public key from the URL parameter, point the jsSdkUrl to the URL that we have beforehand (while generating the JS browser bundle), then set everything else (other than the DSN on the Sentry config) to the default value.

A new container, I know, but at least this might solve the problem for the loader script.

UPDATE: I made a proof-of-concept for such service, it's here https://github.com/aldy505/sentry-loader

BYK commented 3 months ago

Why not implement that logic in Nginx with Lua or something?

aldy505 commented 3 months ago

Why not implement that logic in Nginx with Lua or something?

I don't know how to do that 😂. But it turns out, I need to fetch the project ID that the public key belongs to, so I need to call some Sentry APIs to retrieve that.

radalin commented 3 months ago

The way I setup is this:

  1. I copied the cdn url for the Javascript include: https://browser.sentry-cdn.com/7.116.0/bundle.tracing.replay.min.js (Make sure to select the correct version that matches your own installation, current sentry.io has 8, my self hosted installation was 7.x (seen on the settings))
  2. Setup the replay manually as indicated here: https://docs.sentry.io/platforms/javascript/guides/angular/session-replay/#set-up . To be specific it was like this:
            Sentry.onLoad(function() {
                Sentry.init({
                  dsn: 'yourkey@your-dsn.com/here',
                  replaysSessionSampleRate: 0.001,
                  replaysOnErrorSampleRate: 0.2,
                  integrations: [
                    Sentry.replayIntegration({
                      // Additional SDK configuration goes in here, for example:
                      maskAllText: true,
                      blockAllMedia: true,
                    }),
                  ],
                });
            });

And it's working at the moment. If you want to host it yourself, you can copy the js bundle. Be prepared that based on the sample rates it might use a lot of resources though.