+1
+1
+1
+1
对此发表评论的每个人 - @karolisdzeja 的解决方案有什么问题?
最终,我不确定我们如何向 Raven.js 源添加一个功能,当 Raven.js 源不在页面上时,该功能应该可以工作。 我认为这最终将始终是一个定制的解决方案; 充其量我们可以在我们的文档中添加“操作方法”。
@benvinegar解决方案看起来不错,但如果得到官方支持和记录会更好。 我很高兴能够信任 Sentry 团队而不是评估随机要点。
实际上,更好的解决方案类似于 Twitter 的 JS SDK 代码: https :
在页面加载时设置一个函数队列,然后在加载外部 js 时使用它,替换代理对象。 并确保所有 API 调用都经过类似于 .ready() 调用的代理。
这确保了任何调用都可以在 js 加载之前排队,而不仅仅是 captureMessage,而不必单独代理每个函数。
我希望能够异步/延迟加载 raven.js 而不必担心。
要点的其他问题:它破坏了 window.onerror 并引入了一些未包含的全局变量。
它也没有使用 raven.js 在加载时使用的全功能 traceKitWindowOnError 函数。
这样做还有其他缺点:
window.onerror
在加载 Raven 之前捕获错误,堆栈跟踪不适用于每个浏览器install()
会尝试/捕获检测因此,您可能会用更好的性能来换取较低质量的错误报告。 如果异步执行很重要,我会尽快建议您将 Raven 与您自己的代码捆绑在一起,以便它们一起提供。
@benvinegar你是完全正确的。 在非公开的应用程序中(即 google 无法访问页面),经典的(阻塞)raven 方式完全没问题,但是一旦您拥有一个面向公众的网站,其中 google 页面洞察点很重要,我们就需要优化我们如何加载第三方代码(这是我们愿意为支持用户体验、速度和更好的搜索结果位置而付出的代价)。
此外,将 raven 捆绑到我们的 bundle 中是一种解决方案,但是一旦您开始使用首屏优化来优化前端代码,例如使用factor-bundle等工具将您的 bundle 拆分为多个,或者您包含多个 bundle 以获得更高的速度,上述解决方案可能是一个更好的 imo,但我愿意接受建议。
他们都有权衡,所以如果我们能记录所有可用的策略,那就太好了,所以取决于每个特定的 Web 应用程序,我们将应用不同的策略。
使用异步策略,我们应该在onload
事件之后加载脚本,而不是只加载异步,以防止阻塞onload
事件,渲染。
/**
* Setup Js error lazy tracking
* - Pros: doesn't block rendering, onload event
* - Cons: lower quality error reports for lazy errors
*
* <strong i="9">@author</strong> vinhlh
*
* <strong i="10">@param</strong> {object} window
* <strong i="11">@param</strong> {object} labJs
* <strong i="12">@param</strong> {string} ravenCdn
* <strong i="13">@param</strong> {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');
我想我们可能只记录一个异步片段,就像上面提供的那样,但要提到它会带来一些权衡。
只是另一条评论。 这些权衡似乎是可以接受的,但我处理了很多来自用户的关于他们遇到的低保真错误的支持票,这些错误被(错误地)认为是从 Raven.js 派生的。 我担心的是,如果我鼓励人们使用异步方法,就会有越来越多的人问我“为什么没有堆栈跟踪”和其他抱怨,因为这种方法保真度较低。 我愿意接受这一点,但这是一颗难以下咽的药丸。 😓
@benvinegar我完全明白你来自哪里,我知道我在做什么,所以我不希望
@oroce – 是的,这 100% 不是这个线程中的人的问题,而是那些可能在没有正确理解警告的情况下(例如,只是复制/粘贴)而采取这种策略的人。
我会保持这个问题的开放性,并计划将代码片段添加到Install
文档中 - 我会在所有地方放置一堆警告。
再次感谢您的参与/说服我这样做。
这是我们用来透明地对 Raven 方法的调用进行排队的代码片段: https :
模拟当然可以改进,但我们不需要复制更多功能。 Object.defineProperty
允许我们在Raven
将自身附加到窗口后立即挂钩,但也许脚本加载事件就足够了。 有一种官方的方式来实现这一点会很好。
嘿伙计们,只是想知道 Raygun 异步执行此操作的方式是否有问题?
我不确定,但它似乎可以很好地处理边缘情况? 不过我可能错了:)
@Kl0tl非常好,谢谢
使用动态导入非常简单。 仍处于 stage3,但已被 webpack 支持。
我们只是将 promise 用作队列。 一旦完成,所有回调将按顺序执行。
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});
});
};
与@zanona类似,我也更喜欢像 Raygun 或Google Analytics这样的简单跟踪代码。 下面是一个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 使用 Raven.js是否可以实现类似的功能? 也许在将来?
@kireerik它肯定会实施(很可能作为文档操作方法),但我现在无法给您准确的日期估计。
@kamilogorek听起来很棒(我不喜欢将变通方法作为解决方案)。 没问题!
对于任何感兴趣的人,我已经提出了另一种异步加载 ravenjs 的方法的要点:
https://gist.github.com/MaxMilton/e2338b02b7381fc7bef2ccd96f434201
它基于@oroce的代码,但主要区别在于我在文档头中使用常规<script async src'='...">
标签以获得更好的性能(浏览器可以安排更早地获取资源)+我不费心把它包装起来IIFE 和其他小调整。
@MaxMilton不错的一个! 我根据你的口味创造了我自己的口味:
<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>
我也有一些疑问:
script
标签上定义crossorigin
属性?你怎么认为? 作者 (@kamilogorek) 对此有何看法?
@kireerik当您将crossorigin="anonymous"
放在脚本上时,它允许您使用window.onerror
事件完全捕获错误(来自该外部脚本)。 它还可以防止浏览器通过 fetch 请求发送凭据,这通常是您想要使用 3rd 方资源的方式。 MDN 参考资料 1 , MDN 参考资料 2 。
你可以只传递错误,它会在_大多数_时间工作。 需要注意的是旧浏览器(例如版本 31 之前的 Firefox)不会将 columnNo 或 Error Object 属性传递给window.onerror
事件。 因此,如果您想要真正良好的兼容性,那么您需要做一些额外的工作。 MDN 参考。
编辑:额外提示:事实证明,当您将crossorigin
放在没有任何价值的情况下时,它被视为与crossorigin="anonymous"
。
仅供参考,我已经将我之前的要点更新为更适合生产的内容:
如果您想了解所有内容,请参阅要点,或者如果您更喜欢快速复制+粘贴,这里是缩小版:
<!-- 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">
将___PUBLIC_DSN___
替换</head>
标签附近的头部某处。 或者,如果您是一个不再使用<head>
和<body>
标签的时髦人士,那么只需将其粘贴在任何关键/应用程序资源(例如 CSS)之后的顶部附近。 理想情况下,它应该在任何其他 JavaScript
在我的快速测试中,没有任何问题,所以我看不出有任何理由不在默认同步版本上使用它。
如果有人有更好的方法的想法,我很想听听。
编辑:抱歉多次编辑此评论。 现在处于一个稳定的水平。 达到这个状态很有趣! :笑脸:
加载哨兵库后,错误报告质量与加载同步完全相同? (我假设是这样,只是检查)
同样在您可能想要添加的文档中,在加载 lib 之前您不能使用 Raven,也许在选项中提供回调函数以便您可以设置用户上下文等?
同意@dalyr95 。 回调函数会很有用。 也许是自定义 raven 加载事件。
我和@dalyr95 有类似的要求。 现在调用setUserContext()
的唯一方法是修改加载器片段,它不像能够在主配置对象上传递回调那样干净。
您好,感谢您报告问题。
我们正在开发 SDK 的下一个主要版本。
因此,我们不得不搁置当前版本(主要或安全错误除外)的工作。
我们会尽快回复所有报告,请耐心等待。
谢谢你的理解,
干杯!
我的解决方案是在调用.install()
后立即添加'undefined'!=k.setup&&k.setup();
,然后我将一个名为setup
的函数添加到SENTRY_SDK
并使用我的 post init 代码。
使用异步加载器,我能够通过将其作为第二个参数传递给Raven.config
来设置用户上下文和其他信息:
<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>
我认为为此一切都已经存在,只是可以更好地记录下来。
@danielcompton只能通过后端 api 获取用户信息?
最有用的评论
这是我们用来透明地对 Raven 方法的调用进行排队的代码片段: https :
模拟当然可以改进,但我们不需要复制更多功能。
Object.defineProperty
允许我们在Raven
将自身附加到窗口后立即挂钩,但也许脚本加载事件就足够了。 有一种官方的方式来实现这一点会很好。