From 07ff9a7f8e40bccbca5f255d12ecd96831d7078a Mon Sep 17 00:00:00 2001 From: giancarlo Date: Mon, 15 Apr 2024 14:14:08 +0800 Subject: [PATCH] Introduce error boundary mechanism and exception capture with Baselime and Sentry Deleted the ErrorBoundary component from the makerkit package and introduced new exception capture mechanisms for Baselime and Sentry monitoring providers. The code now captures all exceptions thrown within components and sends them to the configured monitoring provider, which in turn logs the errors for debugging purposes. Updated packages and environment variables accordingly to support this feature. --- apps/web/.env.production | 6 ++- .../home/[account]/billing/error.tsx | 15 ++++--- apps/web/app/error.tsx | 21 ++++++--- apps/web/instrumentation.ts | 10 ++++- apps/web/public/locales/en/common.json | 1 + packages/features/accounts/package.json | 1 + .../account-danger-zone.tsx | 2 +- packages/features/team-accounts/package.json | 1 + .../settings/team-account-danger-zone.tsx | 2 +- packages/monitoring/baselime/package.json | 7 ++- .../baselime/src/capture-exception.ts | 4 ++ .../baselime/src/components/provider.tsx | 22 ++++++++++ .../src/hooks/use-capture-exception.ts | 12 +++++ packages/monitoring/baselime/src/index.ts | 32 +------------- .../baselime/src/instrumentation.ts | 31 +++++++++++++ packages/monitoring/package.json | 12 ++++- packages/monitoring/sentry/package.json | 1 + .../sentry/src/capture-exception.ts | 5 +++ packages/monitoring/sentry/src/index.ts | 37 +--------------- .../monitoring/sentry/src/instrumentation.ts | 36 +++++++++++++++ packages/monitoring/src/capture-exception.ts | 44 +++++++++++++++++++ .../src/components/error-boundary.tsx | 39 ++++++++++++++++ packages/monitoring/src/components/index.ts | 1 + packages/monitoring/src/hooks/index.ts | 1 + .../src/hooks/use-capture-exception.ts | 11 +++++ packages/monitoring/src/index.ts | 2 +- packages/monitoring/src/instrumentation.ts | 32 +++++++------- .../src/monitoring-providers.enum.ts | 4 ++ packages/ui/package.json | 1 - packages/ui/src/makerkit/error-boundary.tsx | 26 ----------- pnpm-lock.yaml | 39 ++++++++++++++++ 31 files changed, 327 insertions(+), 131 deletions(-) create mode 100644 packages/monitoring/baselime/src/capture-exception.ts create mode 100644 packages/monitoring/baselime/src/components/provider.tsx create mode 100644 packages/monitoring/baselime/src/hooks/use-capture-exception.ts create mode 100644 packages/monitoring/baselime/src/instrumentation.ts create mode 100644 packages/monitoring/sentry/src/capture-exception.ts create mode 100644 packages/monitoring/sentry/src/instrumentation.ts create mode 100644 packages/monitoring/src/capture-exception.ts create mode 100644 packages/monitoring/src/components/error-boundary.tsx create mode 100644 packages/monitoring/src/components/index.ts create mode 100644 packages/monitoring/src/hooks/index.ts create mode 100644 packages/monitoring/src/hooks/use-capture-exception.ts create mode 100644 packages/monitoring/src/monitoring-providers.enum.ts delete mode 100644 packages/ui/src/makerkit/error-boundary.tsx diff --git a/apps/web/.env.production b/apps/web/.env.production index 6c50e0284..41b5ebb3d 100644 --- a/apps/web/.env.production +++ b/apps/web/.env.production @@ -44,4 +44,8 @@ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= NEXT_PUBLIC_ENABLE_ACCOUNT_DELETION=true NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING=true NEXT_PUBLIC_ENABLE_ORGANIZATION_DELETION=true -NEXT_PUBLIC_ENABLE_ORGANIZATION_BILLING=true \ No newline at end of file +NEXT_PUBLIC_ENABLE_ORGANIZATION_BILLING=true + +# MONITORING +MONITORING_PROVIDER= +MONITORING_INSTRUMENTATION_ENABLED=false diff --git a/apps/web/app/(dashboard)/home/[account]/billing/error.tsx b/apps/web/app/(dashboard)/home/[account]/billing/error.tsx index 509bbdb22..5eb160524 100644 --- a/apps/web/app/(dashboard)/home/[account]/billing/error.tsx +++ b/apps/web/app/(dashboard)/home/[account]/billing/error.tsx @@ -1,16 +1,21 @@ 'use client'; -import { useRouter } from 'next/navigation'; - import { ExclamationTriangleIcon } from '@radix-ui/react-icons'; +import { useCaptureException } from '@kit/monitoring/hooks'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; import { Button } from '@kit/ui/button'; import { PageBody, PageHeader } from '@kit/ui/page'; import { Trans } from '@kit/ui/trans'; -export default function BillingErrorPage() { - const router = useRouter(); +export default function BillingErrorPage({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useCaptureException(error); return ( <> @@ -34,7 +39,7 @@ export default function BillingErrorPage() {
-
diff --git a/apps/web/app/error.tsx b/apps/web/app/error.tsx index a3e84ac15..57ea2a3d5 100644 --- a/apps/web/app/error.tsx +++ b/apps/web/app/error.tsx @@ -4,13 +4,22 @@ import Link from 'next/link'; import { ArrowLeft } from 'lucide-react'; +import { useCaptureException } from '@kit/monitoring/hooks'; import { Button } from '@kit/ui/button'; import { Heading } from '@kit/ui/heading'; import { Trans } from '@kit/ui/trans'; import { SiteHeader } from '~/(marketing)/_components/site-header'; -const ErrorPage = () => { +const ErrorPage = ({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) => { + useCaptureException(error); + return (
@@ -39,13 +48,11 @@ const ErrorPage = () => {
- - - + +
diff --git a/apps/web/instrumentation.ts b/apps/web/instrumentation.ts index cc4b7aeec..9c4237a0e 100644 --- a/apps/web/instrumentation.ts +++ b/apps/web/instrumentation.ts @@ -2,11 +2,17 @@ * This file is used to register monitoring instrumentation * for your Next.js application. */ + +const RUNTIME = process.env.NEXT_RUNTIME; + +const ENABLE_INSTRUMENTATION = + process.env.MONITORING_INSTRUMENTATION_ENABLED === 'true'; + export async function register() { // only run in nodejs runtime - if (process.env.NEXT_RUNTIME === 'nodejs') { + if (RUNTIME === 'nodejs' && ENABLE_INSTRUMENTATION) { const { registerMonitoringInstrumentation } = await import( - '@kit/monitoring' + '@kit/monitoring/instrumentation' ); // Register monitoring instrumentation based on the diff --git a/apps/web/public/locales/en/common.json b/apps/web/public/locales/en/common.json index d0c75538f..bcf564bb9 100644 --- a/apps/web/public/locales/en/common.json +++ b/apps/web/public/locales/en/common.json @@ -21,6 +21,7 @@ "clear": "Clear", "notFound": "Not Found", "backToHomePage": "Back to Home Page", + "goBack": "Go Back", "genericServerError": "Sorry, something went wrong.", "genericServerErrorHeading": "Sorry, something went wrong while processing your request. Please contact us if the issue persists.", "pageNotFound": "Sorry, this page does not exist.", diff --git a/packages/features/accounts/package.json b/packages/features/accounts/package.json index 298b5e8be..f79eb62fa 100644 --- a/packages/features/accounts/package.json +++ b/packages/features/accounts/package.json @@ -23,6 +23,7 @@ "@kit/email-templates": "workspace:^", "@kit/eslint-config": "workspace:*", "@kit/mailers": "workspace:^", + "@kit/monitoring": "workspace:^", "@kit/prettier-config": "workspace:*", "@kit/shared": "workspace:^", "@kit/supabase": "workspace:^", diff --git a/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx b/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx index f8a0192d8..3c5177a4e 100644 --- a/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx +++ b/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx @@ -6,6 +6,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { ExclamationTriangleIcon } from '@radix-ui/react-icons'; import { useForm } from 'react-hook-form'; +import { ErrorBoundary } from '@kit/monitoring/components'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; import { AlertDialog, @@ -17,7 +18,6 @@ import { AlertDialogTrigger, } from '@kit/ui/alert-dialog'; import { Button } from '@kit/ui/button'; -import { ErrorBoundary } from '@kit/ui/error-boundary'; import { Form, FormControl, FormItem, FormLabel } from '@kit/ui/form'; import { Input } from '@kit/ui/input'; import { Trans } from '@kit/ui/trans'; diff --git a/packages/features/team-accounts/package.json b/packages/features/team-accounts/package.json index 53b7ea95f..c8118b0ca 100644 --- a/packages/features/team-accounts/package.json +++ b/packages/features/team-accounts/package.json @@ -22,6 +22,7 @@ "@kit/email-templates": "workspace:^", "@kit/eslint-config": "workspace:*", "@kit/mailers": "workspace:^", + "@kit/monitoring": "workspace:*", "@kit/prettier-config": "workspace:*", "@kit/shared": "workspace:^", "@kit/supabase": "workspace:^", diff --git a/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx b/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx index d036c9f33..f4c8fcee4 100644 --- a/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx +++ b/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx @@ -6,6 +6,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; +import { ErrorBoundary } from '@kit/monitoring/components'; import { useUser } from '@kit/supabase/hooks/use-user'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; import { @@ -19,7 +20,6 @@ import { AlertDialogTrigger, } from '@kit/ui/alert-dialog'; import { Button } from '@kit/ui/button'; -import { ErrorBoundary } from '@kit/ui/error-boundary'; import { Form, FormControl, diff --git a/packages/monitoring/baselime/package.json b/packages/monitoring/baselime/package.json index c581fa704..06ce9abc3 100644 --- a/packages/monitoring/baselime/package.json +++ b/packages/monitoring/baselime/package.json @@ -10,10 +10,13 @@ }, "prettier": "@kit/prettier-config", "exports": { - ".": "./src/index.ts" + ".": "./src/index.ts", + "./instrumentation": "./src/instrumentation.ts", + "./provider": "./src/components/provider.tsx" }, "dependencies": { - "@baselime/node-opentelemetry": "^0.5.8" + "@baselime/node-opentelemetry": "^0.5.8", + "@baselime/react-rum": "^0.2.9" }, "devDependencies": { "@kit/eslint-config": "workspace:*", diff --git a/packages/monitoring/baselime/src/capture-exception.ts b/packages/monitoring/baselime/src/capture-exception.ts new file mode 100644 index 000000000..c9e1114dc --- /dev/null +++ b/packages/monitoring/baselime/src/capture-exception.ts @@ -0,0 +1,4 @@ +export function captureException(error: Error) { + console.info(`No yet defined...`); + return console.error(`Caught exception: ${JSON.stringify(error)}`); +} diff --git a/packages/monitoring/baselime/src/components/provider.tsx b/packages/monitoring/baselime/src/components/provider.tsx new file mode 100644 index 000000000..a78ff5d39 --- /dev/null +++ b/packages/monitoring/baselime/src/components/provider.tsx @@ -0,0 +1,22 @@ +import { BaselimeRum } from '@baselime/react-rum'; + +export function BaselineProvider({ + children, + apiKey, + enableWebVitals, + ErrorPage, +}: React.PropsWithChildren<{ + apiKey: string; + enableWebVitals?: boolean; + ErrorPage?: React.ReactElement; +}>) { + return ( + + {children} + + ); +} diff --git a/packages/monitoring/baselime/src/hooks/use-capture-exception.ts b/packages/monitoring/baselime/src/hooks/use-capture-exception.ts new file mode 100644 index 000000000..29bacd09d --- /dev/null +++ b/packages/monitoring/baselime/src/hooks/use-capture-exception.ts @@ -0,0 +1,12 @@ +import { useCallback } from 'react'; + +import { useBaselimeRum } from '@baselime/react-rum'; + +export function useCaptureException() { + const { captureException } = useBaselimeRum(); + + return useCallback( + (error: Error) => captureException(error), + [captureException], + ); +} diff --git a/packages/monitoring/baselime/src/index.ts b/packages/monitoring/baselime/src/index.ts index 590437486..c258b27a8 100644 --- a/packages/monitoring/baselime/src/index.ts +++ b/packages/monitoring/baselime/src/index.ts @@ -1,31 +1 @@ -const INSTRUMENTATION_SERVICE_NAME = process.env.INSTRUMENTATION_SERVICE_NAME; - -if (!INSTRUMENTATION_SERVICE_NAME) { - throw new Error(` - You have set the Baselime instrumentation provider, but have not set the INSTRUMENTATION_SERVICE_NAME environment variable. Please set the INSTRUMENTATION_SERVICE_NAME environment variable. - `); -} - -/** - * @name registerBaselimeInstrumentation - * @description This file is used to register Baselime instrumentation for your Next.js application. - * - * Please set the MONITORING_INSTRUMENTATION_PROVIDER environment variable to 'baselime' to register Baselime instrumentation. - */ -export async function registerBaselimeInstrumentation() { - const { BaselimeSDK, BetterHttpInstrumentation, VercelPlugin } = await import( - '@baselime/node-opentelemetry' - ); - - const sdk = new BaselimeSDK({ - serverless: true, - service: INSTRUMENTATION_SERVICE_NAME, - instrumentations: [ - new BetterHttpInstrumentation({ - plugins: [new VercelPlugin()], - }), - ], - }); - - sdk.start(); -} +export * from './capture-exception'; diff --git a/packages/monitoring/baselime/src/instrumentation.ts b/packages/monitoring/baselime/src/instrumentation.ts new file mode 100644 index 000000000..590437486 --- /dev/null +++ b/packages/monitoring/baselime/src/instrumentation.ts @@ -0,0 +1,31 @@ +const INSTRUMENTATION_SERVICE_NAME = process.env.INSTRUMENTATION_SERVICE_NAME; + +if (!INSTRUMENTATION_SERVICE_NAME) { + throw new Error(` + You have set the Baselime instrumentation provider, but have not set the INSTRUMENTATION_SERVICE_NAME environment variable. Please set the INSTRUMENTATION_SERVICE_NAME environment variable. + `); +} + +/** + * @name registerBaselimeInstrumentation + * @description This file is used to register Baselime instrumentation for your Next.js application. + * + * Please set the MONITORING_INSTRUMENTATION_PROVIDER environment variable to 'baselime' to register Baselime instrumentation. + */ +export async function registerBaselimeInstrumentation() { + const { BaselimeSDK, BetterHttpInstrumentation, VercelPlugin } = await import( + '@baselime/node-opentelemetry' + ); + + const sdk = new BaselimeSDK({ + serverless: true, + service: INSTRUMENTATION_SERVICE_NAME, + instrumentations: [ + new BetterHttpInstrumentation({ + plugins: [new VercelPlugin()], + }), + ], + }); + + sdk.start(); +} diff --git a/packages/monitoring/package.json b/packages/monitoring/package.json index f94331b89..987655f66 100644 --- a/packages/monitoring/package.json +++ b/packages/monitoring/package.json @@ -11,7 +11,10 @@ }, "prettier": "@kit/prettier-config", "exports": { - ".": "./src/index.ts" + ".": "./src/index.ts", + "./instrumentation": "./src/instrumentation.ts", + "./hooks": "./src/hooks/index.ts", + "./components": "./src/components/index.ts" }, "devDependencies": { "@kit/baselime": "workspace:*", @@ -19,7 +22,12 @@ "@kit/prettier-config": "workspace:*", "@kit/sentry": "workspace:*", "@kit/tailwind-config": "workspace:*", - "@kit/tsconfig": "workspace:*" + "@kit/tsconfig": "workspace:*", + "@types/react": "^18.2.77", + "react": "18.2.0" + }, + "peerDependencies": { + "react": "^18.2.0" }, "eslintConfig": { "root": true, diff --git a/packages/monitoring/sentry/package.json b/packages/monitoring/sentry/package.json index cb61b722b..04ee4ac9e 100644 --- a/packages/monitoring/sentry/package.json +++ b/packages/monitoring/sentry/package.json @@ -11,6 +11,7 @@ "prettier": "@kit/prettier-config", "exports": { ".": "./src/index.ts", + "./instrumentation": "./src/instrumentation.ts", "./config/client": "./src/config/sentry.client.config.ts", "./config/server": "./src/config/sentry.server.config.ts", "./config/edge": "./src/config/sentry.server.edge.ts" diff --git a/packages/monitoring/sentry/src/capture-exception.ts b/packages/monitoring/sentry/src/capture-exception.ts new file mode 100644 index 000000000..5342fa459 --- /dev/null +++ b/packages/monitoring/sentry/src/capture-exception.ts @@ -0,0 +1,5 @@ +import * as Sentry from '@sentry/nextjs'; + +export function captureException(error: Error & { digest?: string }) { + return Sentry.captureException(error); +} diff --git a/packages/monitoring/sentry/src/index.ts b/packages/monitoring/sentry/src/index.ts index d2b87772c..7000d7f04 100644 --- a/packages/monitoring/sentry/src/index.ts +++ b/packages/monitoring/sentry/src/index.ts @@ -1,36 +1 @@ -const INSTRUMENTATION_SERVICE_NAME = process.env.INSTRUMENTATION_SERVICE_NAME; - -if (!INSTRUMENTATION_SERVICE_NAME) { - throw new Error(` - You have set the Sentry instrumentation provider, but have not set the INSTRUMENTATION_SERVICE_NAME environment variable. Please set the INSTRUMENTATION_SERVICE_NAME environment variable. - `); -} - -/** - * @name registerSentryInstrumentation - * @description This file is used to register Sentry instrumentation for your Next.js application. - * - * Please set the MONITORING_INSTRUMENTATION_PROVIDER environment variable to 'sentry' to register Sentry instrumentation. - */ -export async function registerSentryInstrumentation() { - const { Resource } = await import('@opentelemetry/resources'); - const { NodeSDK } = await import('@opentelemetry/sdk-node'); - - const { SEMRESATTRS_SERVICE_NAME } = await import( - '@opentelemetry/semantic-conventions' - ); - - const { SentrySpanProcessor, SentryPropagator } = await import( - '@sentry/opentelemetry-node' - ); - - const sdk = new NodeSDK({ - resource: new Resource({ - [SEMRESATTRS_SERVICE_NAME]: INSTRUMENTATION_SERVICE_NAME, - }), - spanProcessor: new SentrySpanProcessor(), - textMapPropagator: new SentryPropagator(), - }); - - sdk.start(); -} +export { captureException } from './capture-exception'; diff --git a/packages/monitoring/sentry/src/instrumentation.ts b/packages/monitoring/sentry/src/instrumentation.ts new file mode 100644 index 000000000..d2b87772c --- /dev/null +++ b/packages/monitoring/sentry/src/instrumentation.ts @@ -0,0 +1,36 @@ +const INSTRUMENTATION_SERVICE_NAME = process.env.INSTRUMENTATION_SERVICE_NAME; + +if (!INSTRUMENTATION_SERVICE_NAME) { + throw new Error(` + You have set the Sentry instrumentation provider, but have not set the INSTRUMENTATION_SERVICE_NAME environment variable. Please set the INSTRUMENTATION_SERVICE_NAME environment variable. + `); +} + +/** + * @name registerSentryInstrumentation + * @description This file is used to register Sentry instrumentation for your Next.js application. + * + * Please set the MONITORING_INSTRUMENTATION_PROVIDER environment variable to 'sentry' to register Sentry instrumentation. + */ +export async function registerSentryInstrumentation() { + const { Resource } = await import('@opentelemetry/resources'); + const { NodeSDK } = await import('@opentelemetry/sdk-node'); + + const { SEMRESATTRS_SERVICE_NAME } = await import( + '@opentelemetry/semantic-conventions' + ); + + const { SentrySpanProcessor, SentryPropagator } = await import( + '@sentry/opentelemetry-node' + ); + + const sdk = new NodeSDK({ + resource: new Resource({ + [SEMRESATTRS_SERVICE_NAME]: INSTRUMENTATION_SERVICE_NAME, + }), + spanProcessor: new SentrySpanProcessor(), + textMapPropagator: new SentryPropagator(), + }); + + sdk.start(); +} diff --git a/packages/monitoring/src/capture-exception.ts b/packages/monitoring/src/capture-exception.ts new file mode 100644 index 000000000..f99873bfa --- /dev/null +++ b/packages/monitoring/src/capture-exception.ts @@ -0,0 +1,44 @@ +import { InstrumentationProvider } from './monitoring-providers.enum'; + +/** + * @name MONITORING_PROVIDER + * @description Register monitoring instrumentation based on the MONITORING_PROVIDER environment variable. + */ +const MONITORING_PROVIDER = process.env.MONITORING_PROVIDER as + | InstrumentationProvider + | undefined; + +/** + * @name captureException + * @description Capture an exception and send it to the monitoring provider defined. + * @param error + */ +export async function captureException(error: Error) { + if (!MONITORING_PROVIDER) { + console.info( + `No instrumentation provider specified. Logging to console...`, + ); + + return console.error(`Caught exception: ${JSON.stringify(error)}`); + } + + switch (MONITORING_PROVIDER) { + case InstrumentationProvider.Baselime: { + const { captureException } = await import('@kit/baselime'); + + return captureException(error); + } + + case InstrumentationProvider.Sentry: { + const { captureException } = await import('@kit/sentry'); + + return captureException(error); + } + + default: { + throw new Error( + `Please set the MONITORING_PROVIDER environment variable to register the monitoring instrumentation provider.`, + ); + } + } +} diff --git a/packages/monitoring/src/components/error-boundary.tsx b/packages/monitoring/src/components/error-boundary.tsx new file mode 100644 index 000000000..6a5b4f265 --- /dev/null +++ b/packages/monitoring/src/components/error-boundary.tsx @@ -0,0 +1,39 @@ +import type { ErrorInfo, ReactNode } from 'react'; +import { Component } from 'react'; + +import { captureException } from '../capture-exception'; + +interface Props { + onError?: (error: Error, info: ErrorInfo) => void; + fallback: ReactNode; + children: ReactNode; +} + +export class ErrorBoundary extends Component { + readonly state = { hasError: false, error: null }; + + constructor(props: Props) { + super(props); + } + + static getDerivedStateFromError(error: unknown) { + return { + hasError: true, + error, + }; + } + + async componentDidCatch(error: Error, info: ErrorInfo) { + this.props.onError?.(error, info); + + await captureException(error); + } + + render() { + if (this.state.hasError) { + return this.props.fallback; + } + + return this.props.children; + } +} diff --git a/packages/monitoring/src/components/index.ts b/packages/monitoring/src/components/index.ts new file mode 100644 index 000000000..39d945d40 --- /dev/null +++ b/packages/monitoring/src/components/index.ts @@ -0,0 +1 @@ +export * from './error-boundary'; diff --git a/packages/monitoring/src/hooks/index.ts b/packages/monitoring/src/hooks/index.ts new file mode 100644 index 000000000..bb2203ca8 --- /dev/null +++ b/packages/monitoring/src/hooks/index.ts @@ -0,0 +1 @@ +export * from './use-capture-exception'; diff --git a/packages/monitoring/src/hooks/use-capture-exception.ts b/packages/monitoring/src/hooks/use-capture-exception.ts new file mode 100644 index 000000000..68e780c7b --- /dev/null +++ b/packages/monitoring/src/hooks/use-capture-exception.ts @@ -0,0 +1,11 @@ +import { useEffect } from 'react'; + +import { captureException } from '../capture-exception'; + +export function useCaptureException(error: Error) { + useEffect(() => { + void captureException(error); + }, [error]); + + return null; +} diff --git a/packages/monitoring/src/index.ts b/packages/monitoring/src/index.ts index c75c68adc..c258b27a8 100644 --- a/packages/monitoring/src/index.ts +++ b/packages/monitoring/src/index.ts @@ -1 +1 @@ -export * from './instrumentation'; +export * from './capture-exception'; diff --git a/packages/monitoring/src/instrumentation.ts b/packages/monitoring/src/instrumentation.ts index c8a722d9e..de4c9b5e4 100644 --- a/packages/monitoring/src/instrumentation.ts +++ b/packages/monitoring/src/instrumentation.ts @@ -1,44 +1,46 @@ -enum InstrumentationProvider { - Baselime = 'baselime', - Sentry = 'sentry', -} +import { InstrumentationProvider } from './monitoring-providers.enum'; /** - * @name DEFAULT_INSTRUMENTATION_PROVIDER - * @description Register monitoring instrumentation based on the MONITORING_INSTRUMENTATION_PROVIDER environment variable. + * @name MONITORING_PROVIDER + * @description Register monitoring instrumentation based on the MONITORING_PROVIDER environment variable. */ -const DEFAULT_INSTRUMENTATION_PROVIDER = process.env - .MONITORING_INSTRUMENTATION_PROVIDER as InstrumentationProvider | undefined; +const MONITORING_PROVIDER = process.env.MONITORING_PROVIDER as + | InstrumentationProvider + | undefined; /** * @name registerMonitoringInstrumentation - * @description Register monitoring instrumentation based on the MONITORING_INSTRUMENTATION_PROVIDER environment variable. + * @description Register monitoring instrumentation based on the MONITORING_PROVIDER environment variable. * - * Please set the MONITORING_INSTRUMENTATION_PROVIDER environment variable to register the monitoring instrumentation provider. + * Please set the MONITORING_PROVIDER environment variable to register the monitoring instrumentation provider. */ export async function registerMonitoringInstrumentation() { - if (!DEFAULT_INSTRUMENTATION_PROVIDER) { + if (!MONITORING_PROVIDER) { console.info(`No instrumentation provider specified. Skipping...`); return; } - switch (DEFAULT_INSTRUMENTATION_PROVIDER) { + switch (MONITORING_PROVIDER) { case InstrumentationProvider.Baselime: { - const { registerBaselimeInstrumentation } = await import('@kit/baselime'); + const { registerBaselimeInstrumentation } = await import( + '@kit/baselime/instrumentation' + ); return registerBaselimeInstrumentation(); } case InstrumentationProvider.Sentry: { - const { registerSentryInstrumentation } = await import('@kit/sentry'); + const { registerSentryInstrumentation } = await import( + '@kit/sentry/instrumentation' + ); return registerSentryInstrumentation(); } default: throw new Error( - `Unknown instrumentation provider: ${DEFAULT_INSTRUMENTATION_PROVIDER as string}`, + `Unknown instrumentation provider: ${MONITORING_PROVIDER as string}`, ); } } diff --git a/packages/monitoring/src/monitoring-providers.enum.ts b/packages/monitoring/src/monitoring-providers.enum.ts new file mode 100644 index 000000000..030610031 --- /dev/null +++ b/packages/monitoring/src/monitoring-providers.enum.ts @@ -0,0 +1,4 @@ +export enum InstrumentationProvider { + Baselime = 'baselime', + Sentry = 'sentry', +} diff --git a/packages/ui/package.json b/packages/ui/package.json index e21558de9..482dc4a27 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -119,7 +119,6 @@ "./page": "./src/makerkit/page.tsx", "./image-uploader": "./src/makerkit/image-uploader.tsx", "./global-loader": "./src/makerkit/global-loader.tsx", - "./error-boundary": "./src/makerkit/error-boundary.tsx", "./auth-change-listener": "./src/makerkit/auth-change-listener.tsx", "./loading-overlay": "./src/makerkit/loading-overlay.tsx", "./profile-avatar": "./src/makerkit/profile-avatar.tsx", diff --git a/packages/ui/src/makerkit/error-boundary.tsx b/packages/ui/src/makerkit/error-boundary.tsx deleted file mode 100644 index 04aeaa64e..000000000 --- a/packages/ui/src/makerkit/error-boundary.tsx +++ /dev/null @@ -1,26 +0,0 @@ -'use client'; - -import type { ReactNode } from 'react'; -import { Component } from 'react'; - -export class ErrorBoundary extends Component<{ - fallback: ReactNode; - children: ReactNode; -}> { - readonly state = { hasError: false, error: null }; - - static getDerivedStateFromError(error: unknown) { - return { - hasError: true, - error, - }; - } - - render() { - if (this.state.hasError) { - return this.props.fallback; - } - - return this.props.children; - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c6483b16..503390801 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -554,6 +554,9 @@ importers: '@kit/mailers': specifier: workspace:^ version: link:../../mailers + '@kit/monitoring': + specifier: workspace:^ + version: link:../../monitoring '@kit/prettier-config': specifier: workspace:* version: link:../../../tooling/prettier @@ -759,6 +762,9 @@ importers: '@kit/mailers': specifier: workspace:^ version: link:../../mailers + '@kit/monitoring': + specifier: workspace:* + version: link:../../monitoring '@kit/prettier-config': specifier: workspace:* version: link:../../../tooling/prettier @@ -901,12 +907,21 @@ importers: '@kit/tsconfig': specifier: workspace:* version: link:../../tooling/typescript + '@types/react': + specifier: ^18.2.77 + version: 18.2.78 + react: + specifier: 18.2.0 + version: 18.2.0 packages/monitoring/baselime: dependencies: '@baselime/node-opentelemetry': specifier: ^0.5.8 version: 0.5.8(@trpc/server@10.45.2) + '@baselime/react-rum': + specifier: ^0.2.9 + version: 0.2.9(react@18.2.0) devDependencies: '@kit/eslint-config': specifier: workspace:* @@ -1584,6 +1599,17 @@ packages: - supports-color dev: false + /@baselime/react-rum@0.2.9(react@18.2.0): + resolution: {integrity: sha512-x5+eYsNCsasHD6jbfH02HmwA7i8A+3Ac3PS2tKwgFgoqi1EmIgktjAvdYFosTZe73VIQMal19peRXAu2/lGxAA==} + peerDependencies: + react: ^18.2.0 + dependencies: + js-cookie: 3.0.5 + react: 18.2.0 + react-error-boundary: 4.0.13(react@18.2.0) + web-vitals: 3.5.2 + dev: false + /@braintree/sanitize-url@6.0.4: resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==} dev: false @@ -11690,6 +11716,15 @@ packages: - webpack-cli dev: false + /react-error-boundary@4.0.13(react@18.2.0): + resolution: {integrity: sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==} + peerDependencies: + react: '>=16.13.1' + dependencies: + '@babel/runtime': 7.24.4 + react: 18.2.0 + dev: false + /react-hook-form@7.51.3(react@18.2.0): resolution: {integrity: sha512-cvJ/wbHdhYx8aviSWh28w9ImjmVsb5Y05n1+FW786vEZQJV5STNM0pW6ujS+oiBecb0ARBxJFyAnXj9+GHXACQ==} engines: {node: '>=12.22.0'} @@ -13296,6 +13331,10 @@ packages: engines: {node: '>= 8'} dev: true + /web-vitals@3.5.2: + resolution: {integrity: sha512-c0rhqNcHXRkY/ogGDJQxZ9Im9D19hDihbzSQJrsioex+KnFgmMzBiy57Z1EjkhX/+OjyBpclDCzz2ITtjokFmg==} + dev: false + /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}