From c306ef1f744cef9d12887cae8680552bc9f19302 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 17 Dec 2024 11:38:19 +0100 Subject: [PATCH] Add Sentry integration (#434) * Add Sentry integration * Use env var for DSN * Move to `optionalDependencies` * Update docs --- docs/pages/configuration/sentry.mdx | 41 +++++++++++ docs/sidebar.ts | 56 --------------- docs/zudoku.config.tsx | 1 + packages/zudoku/package.json | 3 +- packages/zudoku/src/app/entry.client.tsx | 6 ++ packages/zudoku/src/app/sentry.ts | 24 +++++++ packages/zudoku/src/vite/config.ts | 5 +- packages/zudoku/vite.config.ts | 1 + pnpm-lock.yaml | 87 ++++++++++++++++++++++-- 9 files changed, 160 insertions(+), 64 deletions(-) create mode 100644 docs/pages/configuration/sentry.mdx delete mode 100644 docs/sidebar.ts create mode 100644 packages/zudoku/src/app/sentry.ts diff --git a/docs/pages/configuration/sentry.mdx b/docs/pages/configuration/sentry.mdx new file mode 100644 index 00000000..5c125dfa --- /dev/null +++ b/docs/pages/configuration/sentry.mdx @@ -0,0 +1,41 @@ +--- +sidebar_icon: bug +--- + +# Sentry + +Sentry is a popular error tracking tool that helps you monitor and fix crashes in real time. It provides you with detailed error reports, so you can quickly identify and resolve issues before they affect your users. + +## Enable Sentry + +1. To enable it you have to first install the optional dependency `@sentry/react`. + +```bash +npm install --save @sentry/react +``` + +2. And then set the `SENTRY_DSN` environment variable in your Zudoku project. + +```bash + +SENTRY_DSN=https://your-sentry-dsn +``` + +## Release management + +However this does not handle release management for you. For that you can [create a custom `vite.config.ts`](./vite-config) and use the `@sentry/vite-plugin` plugin. + +```ts +import { sentryVitePlugin } from "@sentry/vite-plugin"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [ + sentryVitePlugin({ + authToken: "your-token", + org: "your-org", + project: "your-project", + }), + ], +}); +``` diff --git a/docs/sidebar.ts b/docs/sidebar.ts deleted file mode 100644 index ffe81a1d..00000000 --- a/docs/sidebar.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Sidebar } from "zudoku"; - -export const sidebar: Sidebar = { - docs: [ - { - type: "category", - label: "Getting started", - icon: "sparkles", - items: ["introduction", "app-quickstart", "html-quickstart"], - }, - { - type: "category", - label: "Configuration", - icon: "cog", - link: "configuration/overview", - items: [ - "configuration/api-reference", - "configuration/navigation", - "configuration/search", - "configuration/authentication", - "configuration/vite-config", - ], - }, - { - type: "category", - label: "Markdown", - icon: "book-open-text", - link: "markdown/overview", - items: ["markdown/mdx", "markdown/admonitions", "markdown/code-blocks"], - }, - { - type: "category", - label: "Guide", - icon: "monitor-check", - items: ["environment-variables", "custom-pages", "using-multiple-apis"], - }, - { - type: "category", - label: "Deployment", - icon: "cloud-upload", - link: "deployment", - items: [ - "deploy/cloudflare-pages", - "deploy/github-pages", - "deploy/vercel", - "deploy/direct-upload", - ], - }, - { - type: "category", - label: "Extending", - icon: "blocks", - items: ["custom-plugins", "api-keys"], - }, - ], -}; diff --git a/docs/zudoku.config.tsx b/docs/zudoku.config.tsx index c0442fc8..6ca3faa3 100644 --- a/docs/zudoku.config.tsx +++ b/docs/zudoku.config.tsx @@ -50,6 +50,7 @@ const config: ZudokuConfig = { "configuration/navigation", "configuration/search", "configuration/authentication", + "configuration/sentry", "configuration/vite-config", ], }, diff --git a/packages/zudoku/package.json b/packages/zudoku/package.json index 956333cc..47c6e00f 100644 --- a/packages/zudoku/package.json +++ b/packages/zudoku/package.json @@ -280,6 +280,7 @@ }, "optionalDependencies": { "@clerk/clerk-js": "5.11.0", - "@inkeep/widgets": "^0.2.289" + "@inkeep/widgets": "^0.2.289", + "@sentry/react": "^8.45.1" } } diff --git a/packages/zudoku/src/app/entry.client.tsx b/packages/zudoku/src/app/entry.client.tsx index cb469a09..25265b34 100644 --- a/packages/zudoku/src/app/entry.client.tsx +++ b/packages/zudoku/src/app/entry.client.tsx @@ -14,6 +14,12 @@ import { getRoutesByConfig } from "./main.js"; const routes = getRoutesByConfig(config); const root = document.getElementById("root")!; +if (process.env.SENTRY_DSN) { + void import("./sentry.js").then((mod) => { + mod.initSentry({ dsn: process.env.SENTRY_DSN as string }); + }); +} + if (root.childElementCount > 0) { void hydrate(routes); } else { diff --git a/packages/zudoku/src/app/sentry.ts b/packages/zudoku/src/app/sentry.ts new file mode 100644 index 00000000..f440eaab --- /dev/null +++ b/packages/zudoku/src/app/sentry.ts @@ -0,0 +1,24 @@ +import * as Sentry from "@sentry/react"; +import { useEffect } from "react"; +import { + createRoutesFromChildren, + matchRoutes, + useLocation, + useNavigationType, +} from "react-router-dom"; + +export const initSentry = ({ dsn }: { dsn: string }) => { + Sentry.init({ + dsn, + integrations: [ + Sentry.reactRouterV6BrowserTracingIntegration({ + useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + }), + Sentry.replayIntegration(), + ], + }); +}; diff --git a/packages/zudoku/src/vite/config.ts b/packages/zudoku/src/vite/config.ts index 0c669624..0ad8e440 100644 --- a/packages/zudoku/src/vite/config.ts +++ b/packages/zudoku/src/vite/config.ts @@ -171,6 +171,9 @@ export async function getViteConfig( worker: { format: "es", }, + define: { + "process.env.SENTRY_DSN": JSON.stringify(process.env.SENTRY_DSN), + }, ssr: { target: "node", noExternal: [], @@ -228,7 +231,7 @@ export async function getViteConfig( ? getAppServerEntryPath() : getAppClientEntryPath(), ], - include: ["react-dom/client"], + include: ["react-dom/client", "@sentry/react"], exclude: [ // Vite does not like optimizing the worker dependency "zudoku/openapi-worker", diff --git a/packages/zudoku/vite.config.ts b/packages/zudoku/vite.config.ts index 5df3dc4f..cee4845f 100644 --- a/packages/zudoku/vite.config.ts +++ b/packages/zudoku/vite.config.ts @@ -55,6 +55,7 @@ export default defineConfig({ "react-dom", "lucide-react", /@radix-ui/, + "@sentry/react", // Optional Modules (i.e. auth providers) are external as we don't // want to bundle these in the library. Users will install these diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac167d4b..1a9e9ccb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -652,6 +652,9 @@ importers: '@inkeep/widgets': specifier: ^0.2.289 version: 0.2.289(@internationalized/date@3.5.5)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2) + '@sentry/react': + specifier: ^8.45.1 + version: 8.45.1(react@18.3.1) devDependencies: '@graphql-codegen/cli': specifier: 5.0.3 @@ -3811,10 +3814,34 @@ packages: '@rushstack/eslint-patch@1.10.4': resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} + '@sentry-internal/browser-utils@8.45.1': + resolution: {integrity: sha512-sZwtP3zAzDsjUS7WkMW5VGbvSl7hGKTMc8gAJbpEsrybMxllIP13zzMRwpeFF11RnnvbrZ/FtAeX58Mvj0jahA==} + engines: {node: '>=14.18'} + + '@sentry-internal/feedback@8.45.1': + resolution: {integrity: sha512-zCKptzki4SLnG+s8je8dgnppOKFjiiO4GVBc4fh7uL8zjNPBnxW8wK4SrPfAEKVYaHUzkKc5vixwUqcpmfLLGw==} + engines: {node: '>=14.18'} + + '@sentry-internal/replay-canvas@8.45.1': + resolution: {integrity: sha512-qiPg6XwOwkiMMe/8Qf3EhXCqkSlSnWLlorYngIbdkV2klbWjd7vKnqkFJF4PnaS0g7kkZr7nh+MdzpyLyuj2Mw==} + engines: {node: '>=14.18'} + + '@sentry-internal/replay@8.45.1': + resolution: {integrity: sha512-cOA9CodNSR9+hmICDaGIDUvWiwxQxeMHk/esbjB8uAW8HG4CYTG3CTYTZmlmou7DuysfMd4JNuFmDFBj+YU5/A==} + engines: {node: '>=14.18'} + + '@sentry/browser@8.45.1': + resolution: {integrity: sha512-/KvYhQSRg8m9kotG8h9FrfXCWRlebrvdfXKjj1oE9SyZ2LmR8Ze9AcEw1qzsBsa1F1D/a5FQbUJahSoLBkaQPA==} + engines: {node: '>=14.18'} + '@sentry/core@8.34.0': resolution: {integrity: sha512-adrXCTK/zsg5pJ67lgtZqdqHvyx6etMjQW3P82NgWdj83c8fb+zH+K79Z47pD4zQjX0ou2Ws5nwwi4wJbz4bfA==} engines: {node: '>=14.18'} + '@sentry/core@8.45.1': + resolution: {integrity: sha512-1fGmkr0paZshh38mD29c4CfkRkgFoYDaAGyDLoGYfTbEph/lU8RHB2HWzN93McqNdMEhl1DRRyqIasUZoPlqSA==} + engines: {node: '>=14.18'} + '@sentry/node@8.34.0': resolution: {integrity: sha512-Q7BPp7Y8yCcwD620xoziWSOuPi/PCIdttkczvB0BGzBRYh2s702h+qNusRijRpVNZmzmYOo9m1x7Y1O/b8/v2A==} engines: {node: '>=14.18'} @@ -3829,6 +3856,12 @@ packages: '@opentelemetry/sdk-trace-base': ^1.26.0 '@opentelemetry/semantic-conventions': ^1.27.0 + '@sentry/react@8.45.1': + resolution: {integrity: sha512-ooMR2Lt4YSc5CMJklBKiL/mb+uidcZMpflxUvVUbtWMif+PqTUkfPRyICv6vs7muxK9i84Rr4iCkyZ4ns4H27A==} + engines: {node: '>=14.18'} + peerDependencies: + react: ^16.14.0 || 17.x || 18.x || 19.x + '@sentry/types@8.34.0': resolution: {integrity: sha512-zLRc60CzohGCo6zNsNeQ9JF3SiEeRE4aDCP9fDDdIVCOKovS+mn1rtSip0qd0Vp2fidOu0+2yY0ALCz1A3PJSQ==} engines: {node: '>=14.18'} @@ -13333,11 +13366,45 @@ snapshots: '@rushstack/eslint-patch@1.10.4': {} + '@sentry-internal/browser-utils@8.45.1': + dependencies: + '@sentry/core': 8.45.1 + optional: true + + '@sentry-internal/feedback@8.45.1': + dependencies: + '@sentry/core': 8.45.1 + optional: true + + '@sentry-internal/replay-canvas@8.45.1': + dependencies: + '@sentry-internal/replay': 8.45.1 + '@sentry/core': 8.45.1 + optional: true + + '@sentry-internal/replay@8.45.1': + dependencies: + '@sentry-internal/browser-utils': 8.45.1 + '@sentry/core': 8.45.1 + optional: true + + '@sentry/browser@8.45.1': + dependencies: + '@sentry-internal/browser-utils': 8.45.1 + '@sentry-internal/feedback': 8.45.1 + '@sentry-internal/replay': 8.45.1 + '@sentry-internal/replay-canvas': 8.45.1 + '@sentry/core': 8.45.1 + optional: true + '@sentry/core@8.34.0': dependencies: '@sentry/types': 8.34.0 '@sentry/utils': 8.34.0 + '@sentry/core@8.45.1': + optional: true + '@sentry/node@8.34.0': dependencies: '@opentelemetry/api': 1.9.0 @@ -13389,6 +13456,14 @@ snapshots: '@sentry/types': 8.34.0 '@sentry/utils': 8.34.0 + '@sentry/react@8.45.1(react@18.3.1)': + dependencies: + '@sentry/browser': 8.45.1 + '@sentry/core': 8.45.1 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optional: true + '@sentry/types@8.34.0': {} '@sentry/utils@8.34.0': @@ -16252,7 +16327,7 @@ snapshots: '@typescript-eslint/parser': 8.13.0(eslint@8.57.0)(typescript@5.7.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.0) eslint-plugin-react: 7.37.2(eslint@8.57.0) @@ -16275,12 +16350,12 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 4.3.7 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.1 get-tsconfig: 4.8.1 @@ -16292,14 +16367,14 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.13.0(eslint@8.57.0)(typescript@5.7.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -16314,7 +16389,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.13.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3