-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Asynchronous Loading and Capturing Errors #169
Comments
+1 |
3 similar comments
+1 |
+1 |
+1 |
Everyone who's commented on this – what's wrong with the solution by @karolisdzeja? Ultimately, I'm not sure how we can add a feature to the Raven.js source that is supposed to work when Raven.js source isn't on the page. I think this will always ultimately be a custom solution; at best we could add a "how to" to our docs. |
@benvinegar the solution looks fine, but it would be better if this was officially supported and documented. I'm happier just having to trust the Sentry team over evaluating a random gist. |
Actually, a much better solution would be something like Twitter's JS SDK code: https://dev.twitter.com/web/javascript/loading Setup a function queue on page load that then gets consumed when the external js has been loaded, replacing the proxy object. And make sure all API calls go through something like a .ready() call to the proxy. This ensures that any call can be queued up before the js has loaded rather than just captureMessage, without having to proxy every function individually. I'd love to be able to just load raven.js asynchronously/deferred and not have to worry. |
Other problems with the gist: it clobbers window.onerror and introduces a few uncontained global variables. |
It also doesn't make use of the full-featured traceKitWindowOnError function that raven.js uses when it loads. |
I slightly redid the gist above: https://gist.github.com/oroce/ec3786ba7eff59963842220c3ffc56b4 There's no leaking variable. I kept the The biggest help at point be having I know we wouldn't have that very proper stacktrace but we would have something better than what we have now. |
There's other downsides with doing this:
So, you're trading potentially better performance for lower quality error reports. If async execution is important, I'd sooner recommend you bundle Raven with your own code so that it is served together. |
@benvinegar you are completely right. In applications which aren't public (aka google won't reach the pages) the classic (blocking) raven way is completely fine but as soon as you have a public facing site where the google page insight points do matter, we need to optimize how we load third party code (this is the price we are willing to pay in favour of ux, speed and better search result position). Moreover bundling raven into our bundle is a solution, but as soon as you get into optimizing your frontend code with above the fold optimizations, like splitting your bundle into multiple ones using tools like factor-bundle or you include multiple bundles to gain more speed, the above solution can be a better one imo, but I'm open for suggestions. |
They all have trade-offs, so it would be great if we can document all available strategies, so depend on per specific web application, we will apply different strategies. /**
* Setup Js error lazy tracking
* - Pros: doesn't block rendering, onload event
* - Cons: lower quality error reports for lazy errors
*
* @author vinhlh
*
* @param {object} window
* @param {object} labJs
* @param {string} ravenCdn
* @param {string} sentryDsn
*/
(function(window, labJs, ravenCdn, sentryDsn) {
var errors = [];
var oldOnError = window.onerror;
window.onerror = function() {
errors.push(arguments);
oldOnError && oldOnError.apply(this, arguments);
};
window.addEventListener('load', function() {
labJs
.script(ravenCdn)
.wait(function() {
window.onerror = oldOnError;
Raven.config(sentryDsn).install();
errors.forEach(function(args) {
window.onerror.apply(this, args);
});
});
});
})(window, $LAB, 'raven-js-3.8.1/dist/raven.js', 'https://[email protected]/9'); |
I think we'll probably just document an async snippet, like the ones provided above, but mention that it comes with tradeoffs. Just one other comment. Those tradeoffs might seem acceptable, but I deal with a lot of support tickets from users about low-fidelity errors they are experiencing that are (incorrectly) believed to derive from Raven.js. My fear is that if I encourage people to use the async approach, I'll have more and more people asking me "why is there no stack trace" and other complaints when it's because this approach is lower fidelity. I'm willing to accept that, but it's a tough pill to swallow. 😓 |
@benvinegar I totally get where you are coming from, I know what I'm doing so I don't expect stacktrace when there shouldn't be. For newcomers, less experienced ones or just users with different usecase it can be leading for sure. |
@oroce – yeah, this is 100% not a concern with people in this thread, but people who might pursue this strategy without understanding the caveats properly (e.g. just copy/pasting). I'll keep this issue open, with a plan to add the snippet to the Thanks again for your participation here / convincing me to do this. |
Here’s the snippet we use to queue calls to Raven methods transparently: https://gist.github.com/Kl0tl/ed0a9e74462a2294f4c8842f5389d8ea. The mock could certainly be improved but we don’t need to replicate more functionalities. |
Hey guys, just wondering if there is anything wrong with the way Raygun does that asynchronously? |
@kl0tl very nice, thank you |
This is very simple using a dynamic import. Still in stage3 but supported by webpack. We simply use the promise as a queue. Once fullfiled all callbacks are executed in order. const RavenPromise = import('raven-js'); // async load raven bundle
// initial setup
RavenPromise.then(Raven => {
Raven.config('url-to-sentry', options).install();
}):
// exported log function
export const logMessage = (level, logger, text) => {
RavenPromise.then(Raven => {
Raven.captureMessage(text, {level, logger});
});
}; |
Similarly to @zanona I would also prefer to have a simple tracking code like Raygun or Google Analytics has. Here is an example for analytics.js: <script async src="https://www.google-analytics.com/analytics.js"></script>
<script>
window.ga = window.ga || function () {
(ga.q = ga.q || []).push(arguments)
}
ga.l = +new Date
ga('create', 'UA-XXXXX-Y', 'auto')
ga('send', 'pageview')
</script> @benvinegar Does something like this possible with Raven.js? Maybe in the future? |
@kireerik it definitely will be implemented (most likely as a documentation how-to), but I cannot give you an accurate date estimate now. |
@kamilogorek Sounds great (I don't like the workaround as a solution). No problem! |
For anyone interested, I've put up a gist of yet another way to load ravenjs asynchronously: It's based on the code by @oroce but with the key differences being I use a regular |
@maxmilton Nice one! I have created my own flavor based on yours: <script async src="https://cdn.ravenjs.com/3.22.1/raven.min.js" crossorigin="anonymous" id="raven"></script>
<script>
(function (sentryDataSourceName) {
var raven = document.getElementById('raven')
, isNotLoaded = true
, errors = []
raven.onreadystatechange = raven.onload = function () {
if (isNotLoaded) {
Raven.config(sentryDataSourceName).install()
isNotLoaded = !isNotLoaded
errors.forEach(function (error) {
Raven.captureException(error[4] || new Error(error[0]), {
extra: {
file: error[1]
, line: error[2]
, col: error[3]
}
})
})
}
}
window.onerror = function (message, source, lineNumber, colmnNumber, error) {
if (isNotLoaded)
errors.push([message, source, lineNumber, colmnNumber, error])
}
})('https://<key>@sentry.io/<project>')
</script> I also have some question:
What do you think? What is the author's (@kamilogorek) opinion on this? |
@kireerik when you put You can just pass the error and it will work most of the time. The caveat being old browsers (e.g. Firefox before version 31) don't pass the columnNo or Error Object properties to the EDIT: Bonus tip: turns out when you put |
FYI, I've updated my previous gist to something that's much more production ready:
See the gist if you want to understand everything OR if you prefer a quick copy+paste, here's the minified version: <!-- Sentry JS error tracking -->
<script async src="https://cdn.ravenjs.com/3.22.0/raven.min.js" crossorigin id="raven"></script>
<script>
(function(b,e,c,d){b.onerror=function(a,b,d,f,g){c||e.push([a,b,d,f,g])};b.onunhandledrejection=function(a){c||e.push([a.reason.reason||a.reason.message,a.type,JSON.stringify(a.reason)])};d.onreadystatechange=d.onload=function(){c||(c=!0,
Raven.config("___PUBLIC_DSN___").install(),
b.onunhandledrejection=function(a){Raven.captureException(Error(a.reason.reason||a.reason.message),{extra:{type:a.type,reason:JSON.stringify(a.reason)}})},e.forEach(function(a){Raven.captureException(a[4]||Error(a[0]),{extra:{file:a[1],line:a[2],col:a[3]}})}))}})(window,[],!1,document.getElementById("raven"));
</script>
<link rel="preconnect" href="https://sentry.io"> Replace In my quick tests there hasn't been any issues so I don't see any reason not to use this over the default synchronous version. If anyone has an idea for a better approach I'm keen to hear it. Edit: Sorry for editing this comment so many times. It's at a stable level now. Was fun getting it to this state! 😃 |
Once the sentry library is loaded, error reporting quality is exactly the same as loading it sync? (I assume so, just checking) |
Also in the docs you might want to add, you can't use Raven till the lib is loaded, maybe provide a callback function in the options so you can set user context etc? |
agree with @dalyr95 . a callback function would be useful. maybe a custom raven load event. |
I have a similar request as @dalyr95. Right now the only way to call |
Hello, thanks for reporting the issue. We are in the process of working on the next major release of our SDK. Thanks for your understanding, |
My solution was to add |
With the async loader, I was able to set the user context and other info by passing it as a second argument to <script>
Raven.config("https://<mydsn>@sentry.io/<projectid>",
{"release":"0.3.1",
"environment":"dev",
"user": {"id":"7b031fa0-32ff-46fe-b94b-e6bc201c3c5f",
"organisation-id":"b1a50c41-b85e-4c50-9cec-c55ff36cf6d1"}}).install();
</script> I think everything already exists for this, it just could be better documented. |
@danielcompton can only get user information through backend api ? |
I want to be able to load raven.js asynchronously, but still be able to capture errors while the script is loading. (Something like how Google Analytics handles events by storing them in a variable until the library loads).
Here's what I have so far: https://gist.github.com/karolisdzeja/8010574
But doing it that way loses some of the information and detail that Raven usually provides. Is there a way to store the full error information similarly to this?
The text was updated successfully, but these errors were encountered: