error-reporter.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. // Conditionally enable Sentry based on whether the DSN token is set
  2. import getMeta from '../utils/meta'
  3. import OError from '@overleaf/o-error'
  4. const reporterPromise = window.ExposedSettings?.sentryDsn
  5. ? sentryReporter()
  6. : nullReporter()
  7. function sentryReporter() {
  8. return (
  9. import(/* webpackMode: "eager" */ '@sentry/browser')
  10. .then(Sentry => {
  11. let eventCount = 0
  12. Sentry.init({
  13. dsn: window.ExposedSettings.sentryDsn,
  14. environment: window.ExposedSettings.sentryEnvironment,
  15. release: window.ExposedSettings.sentryRelease,
  16. autoSessionTracking: false,
  17. // Ignore errors unless they come from our origins
  18. // Adapted from: https://docs.sentry.io/platforms/javascript/configuration/filtering/#decluttering-sentry
  19. allowUrls: [
  20. new RegExp(window.ExposedSettings.sentryAllowedOriginRegex),
  21. ],
  22. ignoreErrors: [
  23. // Ignore very noisy error
  24. 'SecurityError: Permission denied to access property "pathname" on cross-origin object',
  25. // Ignore unhandled error that is "expected" - see https://github.com/overleaf/issues/issues/3321
  26. /^Missing PDF/,
  27. // Ignore "expected" error from aborted fetch - see https://github.com/overleaf/issues/issues/3321
  28. /^AbortError/,
  29. // Ignore spurious error from Ace internals - see https://github.com/overleaf/issues/issues/3321
  30. 'ResizeObserver loop limit exceeded',
  31. 'ResizeObserver loop completed with undelivered notifications.',
  32. // Microsoft Outlook SafeLink crawler
  33. // https://forum.sentry.io/t/unhandledrejection-non-error-promise-rejection-captured-with-value/14062
  34. /Non-Error promise rejection captured with value: Object Not Found Matching Id/,
  35. // Ignore CM6 error until upgraded
  36. "Cannot read properties of undefined (reading 'length')",
  37. ],
  38. denyUrls: [
  39. // Chrome extensions
  40. /extensions\//i,
  41. /^chrome:\/\//i,
  42. ],
  43. beforeSend(event) {
  44. // Limit number of events sent to Sentry to 100 events "per page load",
  45. // (i.e. the cap will be reset if the page is reloaded). This prevent
  46. // hitting their server-side event cap.
  47. eventCount++
  48. if (eventCount > 100) {
  49. return null // Block the event from sending
  50. }
  51. // Do not send events related to third party code (extensions)
  52. if (
  53. event.extra?.arguments?.[0]?.type === 'UNSTABLE_editor:extensions'
  54. ) {
  55. return null // Block the event from sending
  56. }
  57. return event
  58. },
  59. })
  60. Sentry.setUser({ id: window.user_id })
  61. const splitTestAssignments = getMeta('ol-splitTestVariants')
  62. if (splitTestAssignments) {
  63. for (const [name, value] of Object.entries(splitTestAssignments)) {
  64. // Ensure Sentry tag name is within the 32-character limit
  65. Sentry.setTag(`ol.${name}`.slice(0, 32), value.toString())
  66. }
  67. }
  68. return Sentry
  69. })
  70. // If Sentry fails to load, use the null reporter instead
  71. .catch(error => {
  72. console.error(error)
  73. return nullReporter()
  74. })
  75. )
  76. }
  77. function nullReporter() {
  78. return Promise.resolve({
  79. captureException: console.error,
  80. captureMessage: console.error,
  81. })
  82. }
  83. export function captureException(err, options) {
  84. options = options || {}
  85. const extra = Object.assign(OError.getFullInfo(err), options.extra || {})
  86. const fullStack = OError.getFullStack(err)
  87. if (err.stack !== fullStack) {
  88. // Attach tracebacks from OError.tag() and OError.cause.
  89. extra.fullStack = fullStack
  90. }
  91. reporterPromise.then(reporter =>
  92. reporter.captureException(err, {
  93. ...options,
  94. extra,
  95. })
  96. )
  97. }
  98. export function captureMessage(...args) {
  99. reporterPromise.then(reporter => reporter.captureMessage(...args))
  100. }