Live video on the AT Protocol
at natb/temp-error-reporting 187 lines 4.7 kB view raw
1// Super quick and dirty hooks to get error reporting in 2 3import { Platform } from "react-native"; 4import pkg from "../package.json"; 5 6let errorReportingConfig = { 7 ip: "", 8}; 9 10// random string set on load 11let randomString = Math.random().toString(36); 12 13// get it from cdn-cgi/trace 14// cloudflare only! 15const fetchIp = async () => { 16 try { 17 const res = await fetch("https://api.ipify.org?format=json"); 18 const j = await res.json(); 19 errorReportingConfig.ip = j.ip; 20 } catch (e) { 21 // ignore 22 } 23}; 24fetchIp(); 25 26const getReportingUrl = (): string => { 27 return "/api/player-event"; 28}; 29 30export const register = () => { 31 console.log("file registered, error reporting should be ok"); 32}; 33 34const getExtraInfo = () => { 35 return { 36 os: Platform.OS, 37 osVersion: Platform.Version, 38 appVersion: pkg.version, 39 environment: __DEV__ ? "development" : "production", 40 ip: errorReportingConfig.ip, 41 }; 42}; 43 44const getPlayerId = (): string => { 45 if (errorReportingConfig.ip) { 46 const encoded = "app-" + btoa(errorReportingConfig.ip); 47 return encoded; 48 } 49 50 return `app-${randomString}`; 51}; 52 53const sendPlayerEvent = async ( 54 eventType: string, 55 message: string, 56 meta: any = {}, 57) => { 58 try { 59 const reportingURL = getReportingUrl(); 60 61 const playerEventData = { 62 time: new Date().toISOString(), 63 playerId: getPlayerId(), 64 eventType, 65 meta: { 66 message, 67 timestamp: new Date().toISOString(), 68 userAgent: 69 typeof navigator !== "undefined" ? navigator.userAgent : undefined, 70 url: typeof window !== "undefined" ? window.location?.href : undefined, 71 ...getExtraInfo(), 72 ...meta, 73 }, 74 }; 75 76 await fetch(reportingURL, { 77 method: "POST", 78 headers: { 79 "Content-Type": "application/json", 80 }, 81 body: JSON.stringify(playerEventData), 82 }); 83 } catch (e) { 84 console.warn("Failed to send error event:", e); 85 } 86}; 87 88// Hook into React Native unhandled errors 89// @ts-expect-error 90if (global.ErrorUtils) { 91 // @ts-expect-error 92 const defaultHandler = global.ErrorUtils.getGlobalHandler(); 93 // @ts-expect-error 94 global.ErrorUtils.setGlobalHandler( 95 (error: { message: string; stack: any }, isFatal: any) => { 96 sendPlayerEvent("unhandled-error", error.message, { 97 stack: error.stack, 98 isFatal, 99 source: "react-native-error-utils", 100 }); 101 102 if (defaultHandler) { 103 defaultHandler(error, isFatal); 104 } 105 }, 106 ); 107} 108if (typeof process !== "undefined" && process.on) { 109 process.on("uncaughtException", (error: Error) => { 110 sendPlayerEvent("uncaught-exception", error.message, { 111 stack: error.stack, 112 source: "node-uncaught-exception", 113 }); 114 }); 115 116 process.on("unhandledRejection", (reason: any, promise: Promise<any>) => { 117 const message = reason instanceof Error ? reason.message : String(reason); 118 const stack = reason instanceof Error ? reason.stack : undefined; 119 120 sendPlayerEvent( 121 "unhandled-rejection", 122 `Unhandled Promise Rejection: ${message}`, 123 { 124 stack, 125 source: "node-unhandled-rejection", 126 reason: reason instanceof Error ? undefined : reason, 127 }, 128 ); 129 }); 130} 131 132// Hook into uncaught errors (Browser) 133if (typeof window !== "undefined" && Platform.OS === "web") { 134 window.addEventListener("error", (event: ErrorEvent) => { 135 sendPlayerEvent("javascript-error", event.message, { 136 stack: event.error?.stack, 137 source: "browser-window-error", 138 filename: event.filename, 139 lineno: event.lineno, 140 colno: event.colno, 141 }); 142 }); 143 144 window.addEventListener( 145 "unhandledrejection", 146 (event: PromiseRejectionEvent) => { 147 const message = 148 event.reason instanceof Error 149 ? event.reason.message 150 : String(event.reason); 151 const stack = 152 event.reason instanceof Error ? event.reason.stack : undefined; 153 154 sendPlayerEvent( 155 "unhandled-rejection", 156 `Unhandled Promise Rejection: ${message}`, 157 { 158 stack, 159 source: "browser-unhandled-rejection", 160 reason: event.reason instanceof Error ? undefined : event.reason, 161 }, 162 ); 163 }, 164 ); 165} 166 167// Report console errors and warnings 168const originalError = console.error; 169const originalWarn = console.warn; 170 171console.error = (...args: any[]) => { 172 const message = args.join(" "); 173 sendPlayerEvent("console-error", message, { 174 source: "console-error", 175 args: args, 176 }); 177 originalError(...args); 178}; 179 180console.warn = (...args: any[]) => { 181 const message = args.join(" "); 182 sendPlayerEvent("console-warning", message, { 183 source: "console-warning", 184 args: args, 185 }); 186 originalWarn(...args); 187};