diff --git a/components/HitCounter/HitCounter.tsx b/components/HitCounter/HitCounter.tsx index 3009c7d5..ffe66529 100644 --- a/components/HitCounter/HitCounter.tsx +++ b/components/HitCounter/HitCounter.tsx @@ -14,22 +14,26 @@ const HitCounter = ({ slug, className }: HitCounterProps) => { revalidateOnFocus: false, }); - // show spinning loading indicator if data isn't fetched yet - if (!data) { - return ; - } + try { + // show spinning loading indicator if data isn't fetched yet + if (!data) { + return ; + } - // fail secretly - if (error) { - return; - } + // fail secretly + if (error) { + return null; + } - // we have data! - return ( - - {data.hits.toLocaleString("en-US")} - - ); + // we have data! + return ( + + {data.hits.toLocaleString("en-US")} + + ); + } catch (error) { + return null; + } }; export default HitCounter; diff --git a/components/VNC/VNC.tsx b/components/VNC/VNC.tsx index 2c2a929b..46347ec1 100644 --- a/components/VNC/VNC.tsx +++ b/components/VNC/VNC.tsx @@ -31,7 +31,7 @@ const VNC = ({ server }: VNCProps) => { terminalRef.current.textContent = `${message}${ anyKey ? "\n\nPress the Any key or refresh the page to continue." : "" }`; - } catch (e) {} // eslint-disable-line no-empty + } catch (error) {} // eslint-disable-line no-empty }; // hides the console and show the screen when VM connects @@ -44,7 +44,7 @@ const VNC = ({ server }: VNCProps) => { const disconnectVM = () => { try { rfbRef.current.disconnect(); - } catch (e) {} // eslint-disable-line no-empty + } catch (error) {} // eslint-disable-line no-empty }; // prepare for possible navigation away from this page, and disconnect if/when it happens diff --git a/lib/sentry.ts b/lib/sentry.ts new file mode 100644 index 00000000..71a72a5a --- /dev/null +++ b/lib/sentry.ts @@ -0,0 +1,31 @@ +import * as Sentry from "@sentry/node"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import * as Tracing from "@sentry/tracing"; + +// https://docs.sentry.io/platforms/node/configuration/options/ +Sentry.init({ + dsn: process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN || "", + environment: process.env.NODE_ENV || process.env.VERCEL_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV || "", + tracesSampleRate: 1.0, +}); + +export const logServerError = async (error: string | Error) => { + try { + // log error to sentry + Sentry.captureException(error); + // give it 2 seconds to finish sending: + // https://docs.sentry.io/platforms/node/configuration/draining/ + await Sentry.flush(2000); + } catch (sentryError) { + // cue inception bong + console.error("Encountered an error logging an error... We are doomed.", sentryError); + } + + // also log the error normally to the Vercel console; will get picked up by log drain + console.error(error); + + // we really don't want to return *any* error from logging an error, so just keep it on the dl + return true; +}; + +export default Sentry; diff --git a/package.json b/package.json index 46bdc118..1e23766e 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@octokit/graphql": "^4.8.0", "@primer/octicons": "^16.3.1", "@sentry/node": "^6.17.7", + "@sentry/tracing": "^6.17.7", "classnames": "^2.3.1", "copy-to-clipboard": "^3.3.1", "critters": "^0.0.16", @@ -91,7 +92,7 @@ "@typescript-eslint/eslint-plugin": "^5.11.0", "@typescript-eslint/parser": "^5.11.0", "cross-env": "^7.0.3", - "eslint": "~8.8.0", + "eslint": "~8.9.0", "eslint-config-next": "12.0.10", "eslint-config-prettier": "~8.3.0", "eslint-plugin-import": "~2.25.4", diff --git a/pages/api/contact.ts b/pages/api/contact.ts index 91ad5dd4..41b556ef 100644 --- a/pages/api/contact.ts +++ b/pages/api/contact.ts @@ -1,13 +1,8 @@ -import * as Sentry from "@sentry/node"; import fetch from "node-fetch"; import queryString from "query-string"; +import { logServerError } from "../../lib/sentry"; import type { NextApiRequest, NextApiResponse } from "next"; -Sentry.init({ - dsn: process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN || "", - environment: process.env.NODE_ENV || process.env.VERCEL_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV || "", -}); - // fallback to dummy secret for testing: https://docs.hcaptcha.com/#integration-testing-test-keys const HCAPTCHA_SITE_KEY = process.env.HCAPTCHA_SITE_KEY || process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY || "10000000-ffff-ffff-ffff-000000000001"; @@ -56,15 +51,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { // success! let the client know return res.status(200).json({ success: true }); } catch (error) { - console.error(error); - + // extract just the error message to send back to client const message = error instanceof Error ? error.message : "UNKNOWN_EXCEPTION"; - // don't log PEBCAK errors to sentry + // log errors (except PEBCAK) to console and sentry if (!message.startsWith("USER_")) { - // log error to sentry, give it 2 seconds to finish sending - Sentry.captureException(error); - await Sentry.flush(2000); + await logServerError(error); } // 500 Internal Server Error diff --git a/pages/api/hits.ts b/pages/api/hits.ts index f0887666..eab5dfb0 100644 --- a/pages/api/hits.ts +++ b/pages/api/hits.ts @@ -1,14 +1,9 @@ -import * as Sentry from "@sentry/node"; import pRetry from "p-retry"; import faunadb from "faunadb"; import { getAllNotes } from "../../lib/parse-notes"; +import { logServerError } from "../../lib/sentry"; import type { NextApiRequest, NextApiResponse } from "next"; -Sentry.init({ - dsn: process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN || "", - environment: process.env.NODE_ENV || process.env.VERCEL_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV || "", -}); - type PageStats = { slug: string; hits: number; @@ -57,14 +52,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { return res.status(200).json(siteStats); } } catch (error) { - console.error(error); - - // log error to sentry, give it 2 seconds to finish sending - Sentry.captureException(error); - await Sentry.flush(2000); - + // extract just the error message to send back to client const message = error instanceof Error ? error.message : "Unknown error."; + // log full error to console and sentry + await logServerError(error); + // 500 Internal Server Error return res.status(500).json({ success: false, message }); } diff --git a/yarn.lock b/yarn.lock index 28390cf6..4d720163 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1035,14 +1035,14 @@ resolved "https://registry.yarnpkg.com/@corex/deepmerge/-/deepmerge-2.6.148.tgz#8fa825d53ffd1cbcafce1b6a830eefd3dcc09dd5" integrity sha512-6QMz0/2h5C3ua51iAnXMPWFbb1QOU1UvSM4bKBw5mzdT+WtLgjbETBBIQZ+Sh9WvEcGwlAt/DEdRpIC3XlDBMA== -"@eslint/eslintrc@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.5.tgz#33f1b838dbf1f923bfa517e008362b78ddbbf318" - integrity sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ== +"@eslint/eslintrc@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.1.0.tgz#583d12dbec5d4f22f333f9669f7d0b7c7815b4d3" + integrity sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.2.0" + espree "^9.3.1" globals "^13.9.0" ignore "^4.0.6" import-fresh "^3.2.1" @@ -1359,7 +1359,7 @@ lru_map "^0.3.3" tslib "^1.9.3" -"@sentry/tracing@6.17.7": +"@sentry/tracing@6.17.7", "@sentry/tracing@^6.17.7": version "6.17.7" resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.17.7.tgz#f4536683b29bb3ac7ddda5ca49494731cec6b619" integrity sha512-QzIDHOjjdi/0LTdrK2LTC27YEOODI473KD8KmMJ+r9PmjDeIjNzz4hJlPwQSnXR3Mu/8foxGJGXsAt3LNmKzlQ== @@ -2801,10 +2801,10 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.0.tgz#c1f6ea30ac583031f203d65c73e723b01298f153" - integrity sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg== +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -2821,17 +2821,17 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz#6fbb166a6798ee5991358bc2daa1ba76cc1254a1" - integrity sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ== +eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@~8.8.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.8.0.tgz#9762b49abad0cb4952539ffdb0a046392e571a2d" - integrity sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ== +eslint@~8.9.0: + version "8.9.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.9.0.tgz#a2a8227a99599adc4342fd9b854cb8d8d6412fdb" + integrity sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q== dependencies: - "@eslint/eslintrc" "^1.0.5" + "@eslint/eslintrc" "^1.1.0" "@humanwhocodes/config-array" "^0.9.2" ajv "^6.10.0" chalk "^4.0.0" @@ -2839,10 +2839,10 @@ eslint@~8.8.0: debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.0" + eslint-scope "^7.1.1" eslint-utils "^3.0.0" - eslint-visitor-keys "^3.2.0" - espree "^9.3.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -2867,14 +2867,14 @@ eslint@~8.8.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^9.2.0, espree@^9.3.0: - version "9.3.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.0.tgz#c1240d79183b72aaee6ccfa5a90bc9111df085a8" - integrity sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ== +espree@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" + integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== dependencies: acorn "^8.7.0" acorn-jsx "^5.3.1" - eslint-visitor-keys "^3.1.0" + eslint-visitor-keys "^3.3.0" esprima@^4.0.0: version "4.0.1"