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 // This error only appears in Safari 11 "undefined is not an object (evaluating 'window.__pad.performLoop')", 12]; 13 14const REPORTED_ERRORS_KEY = 'sentry:reportedErrors'; 15const TIMESTAMP_KEY = 'sentry:errorReportingInit'; 16const ONE_DAY_MS = 60 * 60 * 24 * 1000; 17 18export function preprocessSentryError(event: Event) { 19 const message = getMessage(event); 20 21 // If we don't know about this particular type of event then just pass it along 22 if (!message) { 23 return event; 24 } 25 26 // Discard any errors that we know we do not care about 27 if (ERRORS_TO_DISCARD.includes(message)) { 28 return null; 29 } 30 31 // Only attempt to check against cached reported messages if we have localStorage 32 if (isLocalStorageAvailable()) { 33 // Clear the saved error messages every day 34 maybeResetReportedErrorsCache(); 35 36 // Bail out if we have reported the error already 37 if (userHasReportedErrorMessage(message)) { 38 return null; 39 } 40 41 saveReportedErrorMessage(message); 42 } 43 44 return event; 45} 46 47// https://gist.github.com/paulirish/5558557 48function isLocalStorageAvailable() { 49 if (!window.localStorage) { 50 return false; 51 } 52 53 try { 54 localStorage.setItem('localStorage:test', 'value'); 55 localStorage.removeItem('localStorage:test'); 56 return true; 57 } catch (e) { 58 return false; 59 } 60} 61 62// Extract a stable event error message out of the Sentry event object 63function getMessage(event: Event) { 64 if (event.message) { 65 return event.message; 66 } 67 68 if (event.exception && event.exception.values) { 69 const value = event.exception.values[0].value; 70 if (value) { 71 return value; 72 } 73 } 74 75 return null; 76} 77 78function maybeResetReportedErrorsCache() { 79 const timestamp = parseInt(localStorage.getItem(TIMESTAMP_KEY) || '', 10); 80 const now = new Date().getTime(); 81 82 if (!timestamp) { 83 localStorage.setItem(TIMESTAMP_KEY, new Date().getTime().toString()); 84 } else if (now - timestamp >= ONE_DAY_MS) { 85 localStorage.removeItem(REPORTED_ERRORS_KEY); 86 localStorage.removeItem(TIMESTAMP_KEY); 87 } 88} 89 90function userHasReportedErrorMessage(message: string) { 91 const messages = getReportedErrorMessages(); 92 if (messages.includes(message)) { 93 return true; 94 } else { 95 return false; 96 } 97} 98 99function saveReportedErrorMessage(message: string) { 100 const messages = getReportedErrorMessages(); 101 localStorage.setItem(REPORTED_ERRORS_KEY, JSON.stringify([...messages, message])); 102} 103 104function getReportedErrorMessages(): string[] { 105 const messages = localStorage.getItem(REPORTED_ERRORS_KEY); 106 if (!messages) { 107 return []; 108 } 109 110 return JSON.parse(messages); 111} 112