Created
May 21, 2026 21:10
-
-
Save danthedaniel/52e121578c7c857236e264355b69650b to your computer and use it in GitHub Desktop.
Minimal Sentry-compatible client-side JS library for error reporting
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const CHROME_RE = | |
| /^\s*at (?:(.*?) ?\()?((?:file|https?|blob|chrome-extension|address|native|eval|webpack|<anonymous>|[-a-z]+:|.*bundle|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i; | |
| const GECKO_RE = | |
| /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:file|https?|blob|chrome|webpack|resource|moz-extension).*?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js))(?::(\d+))?(?::(\d+))?\s*$/i; | |
| /** | |
| * Parse one line of a stacktrace. | |
| */ | |
| function parseLine(line) { | |
| const chrome = CHROME_RE.exec(line); | |
| if (chrome) { | |
| return { | |
| filename: | |
| chrome[2] && chrome[2].indexOf("address at ") === 0 | |
| ? chrome[2].slice("address at ".length) | |
| : chrome[2] || "", | |
| function: chrome[1] || "?", | |
| lineno: chrome[3] ? Number(chrome[3]) : null, | |
| colno: chrome[4] ? Number(chrome[4]) : null, | |
| }; | |
| } | |
| const gecko = GECKO_RE.exec(line); | |
| if (gecko) { | |
| return { | |
| filename: gecko[3] || "", | |
| function: gecko[1] || "?", | |
| lineno: gecko[4] ? Number(gecko[4]) : null, | |
| colno: gecko[5] ? Number(gecko[5]) : null, | |
| }; | |
| } | |
| return null; | |
| } | |
| /** | |
| * Parse an `Error`'s stack trace into the Sentry exception format. | |
| * Supports Chromium-based browsers (Chrome, Edge, Brave, Opera) and Gecko (Firefox). | |
| */ | |
| function parseStackTrace(error) { | |
| if (!error.stack) { | |
| return { | |
| type: error.name || "Error", | |
| value: error.message || "No error message", | |
| stacktrace: { frames: [] }, | |
| }; | |
| } | |
| const frames = []; | |
| for (const line of error.stack.split("\n")) { | |
| const frame = parseLine(line); | |
| if (!frame) { | |
| continue; | |
| } | |
| if (!frame.function && frame.lineno) { | |
| frame.function = "?"; | |
| } | |
| frames.push(frame); | |
| } | |
| return { | |
| type: error.name || "Error", | |
| value: | |
| (error.message || "No error message").split("\n")[0] || | |
| "No error message", | |
| stacktrace: { frames: frames.reverse() }, | |
| }; | |
| } | |
| /** | |
| * Report an `Error` to the server-side error reporting endpoint. | |
| */ | |
| export function sendErrorReport(error) { | |
| const event = { | |
| timestamp: new Date().toISOString(), | |
| platform: "javascript", | |
| level: "error", | |
| exception: { values: [parseStackTrace(error)] }, | |
| tags: { | |
| url: window.location.pathname, | |
| }, | |
| contexts: { | |
| browser: { | |
| name: navigator.userAgent, | |
| }, | |
| screen: { | |
| width: window.screen.width, | |
| height: window.screen.height, | |
| }, | |
| }, | |
| }; | |
| const body = JSON.stringify(event); | |
| const success = navigator.sendBeacon("/error-reporting", body); | |
| if (!success) { | |
| console.error("Failed to log error to server"); | |
| } | |
| } | |
| let initialized = false; | |
| function initErrorReporting() { | |
| if (initialized) { | |
| return; | |
| } | |
| window.addEventListener("error", (event) => { | |
| if (event.error instanceof Error) { | |
| sendErrorReport(event.error); | |
| } else { | |
| console.warn("Can't log event:", event); | |
| } | |
| }); | |
| window.addEventListener("unhandledrejection", (event) => { | |
| if (event.reason instanceof Error) { | |
| sendErrorReport(event.reason); | |
| } else { | |
| console.warn("Can't log event:", event); | |
| } | |
| }); | |
| initialized = true; | |
| } | |
| initErrorReporting(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment