1import { Event } from '@sentry/types'; 2/* 3 * Error logging filtering - prevent users from submitting errors we do not care about, 4 * eg: specific error messages that are caused by extensions or other scripts 5 * out of our control, or the same error being reported many times. 6 */ 7 8// These exact error messages may be different depending on the browser! 9const ERRORS_TO_DISCARD = [ 10 // Filter out errors from extensions 11 'chrome-extension://', 12 'moz-extension://', 13 'safari-extension://', 14 // This error only appears in Safari 15 "undefined is not an object (evaluating 'window.__pad.performLoop')", 16 // This error appears in Firefox related to local storage and flooded our Sentry bandwidth 17 'SecurityError: The operation is insecure.', 18]; 19 20const REPORTED_ERRORS_KEY = 'sentry:reportedErrors'; 21const TIMESTAMP_KEY = 'sentry:errorReportingInit'; 22const ONE_DAY_MS = 60 * 60 * 24 * 1000; 23 24export function preprocessSentryError(event: Event) { 25 const message = getMessage(event); 26 27 // If we don't know about this particular type of event then just pass it along 28 if (!message) { 29 return event; 30 } 31 32 // Discard any errors that we know we do not care about 33 if (ERRORS_TO_DISCARD.includes(message)) { 34 return null; 35 } 36 37 // Only attempt to check against cached reported messages if we have localStorage 38 if (isLocalStorageAvailable()) { 39 // Clear the saved error messages every day 40 maybeResetReportedErrorsCache(); 41 42 // Bail out if we have reported the error already 43 if (userHasReportedErrorMessage(message)) { 44 return null; 45 } 46 47 saveReportedErrorMessage(message); 48 } 49 50 return event; 51} 52 53// https://gist.github.com/paulirish/5558557 54function isLocalStorageAvailable(): boolean { 55 try { 56 if (!window.localStorage) { 57 return false; 58 } 59 60 localStorage.setItem('localStorage:test', 'value'); 61 localStorage.removeItem('localStorage:test'); 62 return true; 63 } catch { 64 return false; 65 } 66} 67 68// Extract a stable event error message out of the Sentry event object 69function getMessage(event: Event) { 70 if (event.message) { 71 return event.message; 72 } 73 74 if (event.exception && event.exception.values) { 75 const value = event.exception.values[0].value; 76 if (value) { 77 return value; 78 } 79 } 80 81 return null; 82} 83 84function maybeResetReportedErrorsCache() { 85 const timestamp = parseInt(localStorage.getItem(TIMESTAMP_KEY) || '', 10); 86 const now = new Date().getTime(); 87 88 if (!timestamp) { 89 localStorage.setItem(TIMESTAMP_KEY, new Date().getTime().toString()); 90 } else if (now - timestamp >= ONE_DAY_MS) { 91 localStorage.removeItem(REPORTED_ERRORS_KEY); 92 localStorage.removeItem(TIMESTAMP_KEY); 93 } 94} 95 96function userHasReportedErrorMessage(message: string) { 97 const messages = getReportedErrorMessages(); 98 if (messages.includes(message)) { 99 return true; 100 } else { 101 return false; 102 } 103} 104 105function saveReportedErrorMessage(message: string) { 106 const messages = getReportedErrorMessages(); 107 localStorage.setItem(REPORTED_ERRORS_KEY, JSON.stringify([...messages, message])); 108} 109 110function getReportedErrorMessages(): string[] { 111 const messages = localStorage.getItem(REPORTED_ERRORS_KEY); 112 if (!messages) { 113 return []; 114 } 115 116 return JSON.parse(messages); 117} 118