xref: /expo/docs/common/sentry-utilities.ts (revision bb8f4f99)
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