From c45eecad1ac40fde747de9c14a3ac6ab79244653 Mon Sep 17 00:00:00 2001 From: "Yohan @ ScreenshotLabs" Date: Fri, 21 Jun 2024 20:59:12 +0200 Subject: [PATCH] feat: Added error tracking / logging (#220) --- apps/web/next.config.mjs | 3 +- apps/web/package.json | 2 ++ .../app/(routes)/root-layout-container.tsx | 8 +++++ apps/web/src/app/_components/Logger.tsx | 31 ++++++++++++++++ apps/web/src/pages/api/trpc/[trpc].ts | 35 ++++++++++++------- pnpm-lock.yaml | 21 +++++++++++ 6 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 apps/web/src/app/_components/Logger.tsx diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index 11250c1a..6c17e3da 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -3,6 +3,7 @@ * for Docker builds. */ await import("./env.mjs"); +import { withLogtail } from "@logtail/next"; /** @type {import("next").NextConfig} */ const config = { @@ -35,4 +36,4 @@ const config = { return config; }, }; -export default config; +export default withLogtail(config); diff --git a/apps/web/package.json b/apps/web/package.json index 60b4e381..220e2809 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -10,6 +10,8 @@ }, "dependencies": { "@headlessui/react": "1.7.10", + "@hotjar/browser": "^1.0.9", + "@logtail/next": "^0.1.5", "@radix-ui/react-collapsible": "^1.0.3", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-tabs": "^1.0.4", diff --git a/apps/web/src/app/(routes)/root-layout-container.tsx b/apps/web/src/app/(routes)/root-layout-container.tsx index 389b05d8..d161f8cc 100644 --- a/apps/web/src/app/(routes)/root-layout-container.tsx +++ b/apps/web/src/app/(routes)/root-layout-container.tsx @@ -1,5 +1,6 @@ "use client"; +import Hotjar from "@hotjar/browser"; import clsx from "clsx"; import { cn } from "design-system/src/lib/utils"; import { usePathname } from "next/navigation"; @@ -8,10 +9,16 @@ import React, { type PropsWithChildren } from "react"; import { api } from "~/utils/api"; import Header from "../_components/Header"; +import Logger from "../_components/Logger"; import MobilePlaceholder from "../_components/MobilePlaceholder"; import useCurrentChain from "../_hooks/useCurrentChain"; import Providers from "./providers"; +const siteId = 5032874; +const hotjarVersion = 6; + +Hotjar.init(siteId, hotjarVersion); + function RootLayoutContainer({ children }: PropsWithChildren) { const { targetChain } = useCurrentChain(); const pathname = usePathname(); @@ -35,6 +42,7 @@ function RootLayoutContainer({ children }: PropsWithChildren) { )} + ); } diff --git a/apps/web/src/app/_components/Logger.tsx b/apps/web/src/app/_components/Logger.tsx new file mode 100644 index 00000000..ea704a18 --- /dev/null +++ b/apps/web/src/app/_components/Logger.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { log } from "@logtail/next"; +import { useEffect } from "react"; + +export default function Logger() { + useEffect(() => { + function logError(event: ErrorEvent) { + console.log("ERROR DETECTED"); + log.error(event.message, { + column: event.colno, + line: event.lineno, + url: event.filename, + }); + } + function logUnhandledRejection(event: PromiseRejectionEvent) { + console.log("ERROR DETECTED"); + log.error("Unhandled Promise Rejection", { + reason: event.reason as string, + }); + } + + window.addEventListener("error", logError); + window.addEventListener("unhandledrejection", logUnhandledRejection); + return () => { + document.removeEventListener("error", logError); + }; + }, []); + + return null; +} diff --git a/apps/web/src/pages/api/trpc/[trpc].ts b/apps/web/src/pages/api/trpc/[trpc].ts index 9b2ff171..c43aa9c3 100644 --- a/apps/web/src/pages/api/trpc/[trpc].ts +++ b/apps/web/src/pages/api/trpc/[trpc].ts @@ -1,3 +1,5 @@ +import { log } from "@logtail/next"; +import { withLogtail } from "@logtail/next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { env } from "~/../env.mjs"; @@ -5,15 +7,24 @@ import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; // export API handler -export default createNextApiHandler({ - createContext: createTRPCContext, - onError: - env.NODE_ENV === "development" - ? ({ error, path }) => { - console.error( - `❌ tRPC failed on ${path ?? ""}: ${error.message}` - ); - } - : undefined, - router: appRouter, -}); +export default withLogtail( + createNextApiHandler({ + createContext: createTRPCContext, + onError: + env.NODE_ENV === "development" + ? ({ error, path }) => { + console.error( + `❌ tRPC failed on ${path ?? ""}: ${error.message}` + ); + } + : ({ error, path }) => { + log.error( + `❌ tRPC failed on ${path ?? ""}: ${error.message}` + ); + if (error.stack !== undefined) { + log.error(error.stack); + } + }, + router: appRouter, + }) +); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4f29be4..c41551cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,6 +30,12 @@ importers: '@headlessui/react': specifier: 1.7.10 version: 1.7.10(react-dom@18.2.0)(react@18.2.0) + '@hotjar/browser': + specifier: ^1.0.9 + version: 1.0.9 + '@logtail/next': + specifier: ^0.1.5 + version: 0.1.5(next@13.5.6) '@radix-ui/react-collapsible': specifier: ^1.0.3 version: 1.0.3(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0) @@ -2118,6 +2124,10 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@hotjar/browser@1.0.9: + resolution: {integrity: sha512-n9akDMod8BLGpYEQCrHwlYWWd63c1HlhUSXNIDfClZtKYXbUjIUOFlNZNNcUxgHTCsi4l2i+SWKsGsO0t93S8w==} + dev: false + /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -2252,6 +2262,16 @@ packages: '@lit-labs/ssr-dom-shim': 1.2.0 dev: false + /@logtail/next@0.1.5(next@13.5.6): + resolution: {integrity: sha512-J8m9jGdcGjYfKEbB1pdQj9iaBq9eNtHtqNcrHStjGI1LPiGREwk+HnnpM12yS6teiwlQepcXJDtQ8fk1QHg+jw==} + engines: {node: '>=15'} + peerDependencies: + next: ^12.1.4 || ^13 || ^14 + dependencies: + next: 13.5.6(@babel/core@7.24.4)(react-dom@18.2.0)(react@18.2.0) + whatwg-fetch: 3.6.20 + dev: false + /@metamask/eth-json-rpc-provider@1.0.1: resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==} engines: {node: '>=14.0.0'} @@ -9895,6 +9915,7 @@ packages: /rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true dependencies: glob: 7.2.3