error-reporter.js 4.2 KB

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