--- title: "Monitoring and Error Tracking in Makerkit" status: "published" label: "How Monitoring Works" order: 0 description: "Set up error tracking and performance monitoring in your Next.js Supabase SaaS app with Sentry, PostHog, or SigNoz." --- {% sequence title="Steps to configure monitoring" description="Learn how to configure monitoring in the Next.js Supabase Starter Kit." %} [Understanding the monitoring architecture](#understanding-the-monitoring-architecture) [Supported monitoring providers](#supported-monitoring-providers) [Configuring your monitoring provider](#configuring-your-monitoring-provider) [What gets monitored automatically](#what-gets-monitored-automatically) [Manually capturing exceptions](#manually-capturing-exceptions) [Identifying users in error reports](#identifying-users-in-error-reports) {% /sequence %} ## Understanding the Monitoring Architecture Makerkit's monitoring system uses a **provider-based architecture** that lets you swap monitoring services without changing your application code. The system lives in the `@kit/monitoring` package and handles: - **Error tracking**: Capture client-side and server-side exceptions - **Performance monitoring**: Track server response times via OpenTelemetry instrumentation - **User identification**: Associate errors with specific users for debugging The architecture follows a registry pattern. When you set `NEXT_PUBLIC_MONITORING_PROVIDER`, Makerkit loads the appropriate service implementation at runtime: ``` MonitoringProvider (React context) │ ▼ Registry lookup │ ▼ ┌───────┴───────┐ │ sentry │ │ posthog │ │ signoz │ └───────────────┘ ``` This means your components interact with a consistent `MonitoringService` interface regardless of which provider you choose. ## Supported Monitoring Providers Makerkit provides first-class support for these monitoring providers: | Provider | Error Tracking | Performance | Self-Hostable | Notes | |----------|---------------|-------------|---------------|-------| | [Sentry](/docs/next-supabase-turbo/monitoring/sentry) | Yes | Yes | Yes | Built-in, recommended for most apps | | [PostHog](/docs/next-supabase-turbo/monitoring/posthog) | Yes | No | Yes | Plugin, doubles as analytics | | [SigNoz](/docs/next-supabase-turbo/monitoring/signoz) | Yes | Yes | Yes | Plugin, OpenTelemetry-native | **Sentry** is included out of the box. PostHog and SigNoz require installing plugins via the Makerkit CLI. {% alert type="default" title="Custom providers" %} You can add support for any monitoring service by implementing the `MonitoringService` interface and registering it in the provider registry. See [Adding a custom monitoring provider](#adding-a-custom-monitoring-provider) below. {% /alert %} ## Configuring Your Monitoring Provider Set these environment variables to enable monitoring: ```bash title=".env.local" # Required: Choose your provider (sentry, posthog, or signoz) NEXT_PUBLIC_MONITORING_PROVIDER=sentry # Provider-specific configuration # See the individual provider docs for required variables ``` The `NEXT_PUBLIC_MONITORING_PROVIDER` variable determines which service handles your errors. Leave it empty to disable monitoring entirely (errors still log to console in development). ## What Gets Monitored Automatically Once configured, Makerkit captures errors without additional code: ### Client-side exceptions The `MonitoringProvider` component wraps your app and captures uncaught exceptions in React components. This includes: - Runtime errors in components - Unhandled promise rejections - Errors thrown during rendering ### Server-side exceptions Next.js 15+ includes an instrumentation hook that captures server errors automatically. Makerkit hooks into this via `instrumentation.ts`: ```typescript title="apps/web/instrumentation.ts" import { type Instrumentation } from 'next'; export const onRequestError: Instrumentation.onRequestError = async ( err, request, context, ) => { const { getServerMonitoringService } = await import('@kit/monitoring/server'); const service = await getServerMonitoringService(); await service.ready(); await service.captureException( err as Error, {}, { path: request.path, headers: request.headers, method: request.method, routePath: context.routePath, }, ); }; ``` This captures errors from Server Components, Server Actions, Route Handlers, and Middleware. ## Manually Capturing Exceptions For expected errors (like validation failures or API errors), capture them explicitly: ### In Server Actions or Route Handlers ```typescript import { getServerMonitoringService } from '@kit/monitoring/server'; export async function createProject(data: FormData) { try { // ... your logic } catch (error) { const monitoring = await getServerMonitoringService(); await monitoring.ready(); monitoring.captureException(error, { action: 'createProject', userId: user.id, }); throw error; // Re-throw or handle as needed } } ``` ### In React Components Use the `useMonitoring` hook for client-side error capture: ```tsx 'use client'; import { useMonitoring } from '@kit/monitoring/hooks'; export function DataLoader() { const monitoring = useMonitoring(); async function loadData() { try { const response = await fetch('/api/data'); if (!response.ok) { throw new Error(`Failed to load data: ${response.status}`); } return response.json(); } catch (error) { monitoring.captureException(error, { component: 'DataLoader', }); throw error; } } // ... } ``` ### The `useCaptureException` Hook For error boundaries or components that receive errors as props: ```tsx 'use client'; import { useCaptureException } from '@kit/monitoring/hooks'; export function ErrorDisplay({ error }: { error: Error }) { // Automatically captures the error when the component mounts useCaptureException(error); return (

Something went wrong

{error.message}

); } ``` ## Identifying Users in Error Reports Associate errors with users to debug issues faster. Makerkit's monitoring providers support user identification: ```typescript const monitoring = useMonitoring(); // After user signs in monitoring.identifyUser({ id: user.id, email: user.email, // Additional fields depend on your provider }); ``` Makerkit automatically identifies users when they sign in if you've configured the analytics/events system. The `user.signedIn` event triggers user identification in both analytics and monitoring. ## Adding a Custom Monitoring Provider To add a provider not included in Makerkit: ### 1. Implement the MonitoringService interface ```typescript title="packages/monitoring/my-provider/src/my-provider.service.ts" import { MonitoringService } from '@kit/monitoring-core'; export class MyProviderMonitoringService implements MonitoringService { private readyPromise: Promise; private readyResolver?: () => void; constructor() { this.readyPromise = new Promise((resolve) => { this.readyResolver = resolve; }); this.initialize(); } async ready() { return this.readyPromise; } captureException(error: Error, extra?: Record) { // Send to your monitoring service myProviderSDK.captureException(error, { extra }); } captureEvent(event: string, extra?: Record) { myProviderSDK.captureEvent(event, extra); } identifyUser(user: { id: string }) { myProviderSDK.setUser(user); } private initialize() { // Initialize your SDK myProviderSDK.init({ dsn: process.env.MY_PROVIDER_DSN }); this.readyResolver?.(); } } ``` ### 2. Register the provider Add your provider to the monitoring registries: ```typescript title="packages/monitoring/api/src/get-monitoring-provider.ts" const MONITORING_PROVIDERS = [ 'sentry', 'my-provider', // Add your provider '', ] as const; ``` ```typescript title="packages/monitoring/api/src/services/get-server-monitoring-service.ts" serverMonitoringRegistry.register('my-provider', async () => { const { MyProviderMonitoringService } = await import('@kit/my-provider'); return new MyProviderMonitoringService(); }); ``` ```typescript title="packages/monitoring/api/src/components/provider.tsx" monitoringProviderRegistry.register('my-provider', async () => { const { MyProviderProvider } = await import('@kit/my-provider/provider'); return { default: function MyProviderWrapper({ children }: React.PropsWithChildren) { return {children}; }, }; }); ``` {% alert type="default" title="Telegram notifications" %} We wrote a tutorial showing how to add Telegram notifications for error monitoring: [Send SaaS errors to Telegram](/blog/tutorials/telegram-saas-error-monitoring). {% /alert %} ## Best Practices ### Do capture context with errors ```typescript // Good: Includes debugging context monitoring.captureException(error, { userId: user.id, accountId: account.id, action: 'updateBillingPlan', planId: newPlanId, }); // Less useful: No context monitoring.captureException(error); ``` ### Don't capture expected validation errors ```typescript // Avoid: This clutters your error dashboard if (!isValidEmail(email)) { monitoring.captureException(new Error('Invalid email')); return { error: 'Invalid email' }; } // Better: Only capture unexpected failures try { await sendEmail(email); } catch (error) { monitoring.captureException(error, { extra: { email: maskEmail(email) }, }); } ``` ## Next Steps Choose a monitoring provider and follow its setup guide: - [Configure Sentry](/docs/next-supabase-turbo/monitoring/sentry) (recommended for most apps) - [Configure PostHog](/docs/next-supabase-turbo/monitoring/posthog) (if you already use PostHog for analytics) - [Configure SigNoz](/docs/next-supabase-turbo/monitoring/signoz) (self-hosted, OpenTelemetry-native)