--- status: "published" title: 'Creating a Custom Monitoring Provider' label: 'Custom Provider' description: 'Integrate LogRocket, Bugsnag, Datadog, or any monitoring service by implementing the MonitoringService interface.' order: 1 --- {% sequence title="How to create a custom monitoring provider" description="Add your preferred monitoring service to the kit." %} [Implement the MonitoringService interface](#implement-the-monitoringservice-interface) [Register the provider](#register-the-provider) [Configure for server-side](#server-side-configuration) {% /sequence %} The monitoring system uses a registry pattern that loads providers dynamically based on the `NEXT_PUBLIC_MONITORING_PROVIDER` environment variable. You can add support for LogRocket, Bugsnag, Datadog, or any other service. ## Implement the MonitoringService Interface Create a new package or add to the existing monitoring packages: ```typescript {% title="packages/monitoring/logrocket/src/logrocket-monitoring.service.ts" %} import LogRocket from 'logrocket'; import { MonitoringService } from '@kit/monitoring-core'; export class LogRocketMonitoringService implements MonitoringService { private readonly readyPromise: Promise; private readyResolver?: (value?: unknown) => void; constructor() { this.readyPromise = new Promise( (resolve) => (this.readyResolver = resolve), ); void this.initialize(); } async ready() { return this.readyPromise; } captureException(error: Error, extra?: Record) { LogRocket.captureException(error, { extra, }); } captureEvent(event: string, extra?: Record) { LogRocket.track(event, extra); } identifyUser(user: { id: string; email?: string; name?: string }) { LogRocket.identify(user.id, { email: user.email, name: user.name, }); } private async initialize() { const appId = process.env.NEXT_PUBLIC_LOGROCKET_APP_ID; if (!appId) { console.warn('LogRocket app ID not configured'); this.readyResolver?.(); return; } if (typeof window !== 'undefined') { LogRocket.init(appId); } this.readyResolver?.(); } } ``` ### Package Configuration Create the package structure: ```json {% title="packages/monitoring/logrocket/package.json" %} { "name": "@kit/logrocket", "version": "0.0.1", "private": true, "exports": { ".": "./src/index.ts" }, "dependencies": { "@kit/monitoring-core": "workspace:*", "logrocket": "^3.0.0" } } ``` ```typescript {% title="packages/monitoring/logrocket/src/index.ts" %} export { LogRocketMonitoringService } from './logrocket-monitoring.service'; ``` ## Register the Provider ### Client-Side Registration Update the monitoring provider registry: ```typescript {% title="packages/monitoring/api/src/components/provider.tsx" %} import { lazy } from 'react'; import { createRegistry } from '@kit/shared/registry'; import { MonitoringProvider as MonitoringProviderType, getMonitoringProvider, } from '../get-monitoring-provider'; type ProviderComponent = { default: React.ComponentType; }; const provider = getMonitoringProvider(); const Provider = provider ? lazy(() => monitoringProviderRegistry.get(provider)) : null; const monitoringProviderRegistry = createRegistry< ProviderComponent, NonNullable >(); // Existing Sentry registration monitoringProviderRegistry.register('sentry', async () => { const { SentryProvider } = await import('@kit/sentry/provider'); return { default: function SentryProviderWrapper({ children }) { return {children}; }, }; }); // Add LogRocket registration monitoringProviderRegistry.register('logrocket', async () => { const { LogRocketProvider } = await import('@kit/logrocket/provider'); return { default: function LogRocketProviderWrapper({ children }) { return {children}; }, }; }); ``` ### Add Provider Type Update the provider enum: ```typescript {% title="packages/monitoring/api/src/get-monitoring-provider.ts" %} import * as z from 'zod'; const MONITORING_PROVIDERS = [ 'sentry', 'logrocket', // Add your provider '', ] as const; export const MONITORING_PROVIDER = z .enum(MONITORING_PROVIDERS) .optional() .transform((value) => value || undefined); export type MonitoringProvider = z.output; export function getMonitoringProvider() { const result = MONITORING_PROVIDER.safeParse(process.env.NEXT_PUBLIC_MONITORING_PROVIDER); if (result.success) { return result.data; } return undefined; } ``` ### Create the Provider Component ```typescript {% title="packages/monitoring/logrocket/src/provider.tsx" %} import { MonitoringContext } from '@kit/monitoring-core'; import { LogRocketMonitoringService } from './logrocket-monitoring.service'; const logrocket = new LogRocketMonitoringService(); export function LogRocketProvider({ children }: React.PropsWithChildren) { return ( {children} ); } ``` ## Server-Side Configuration Register the provider for server-side error capture: ```typescript {% title="packages/monitoring/api/src/services/get-server-monitoring-service.ts" %} import { ConsoleMonitoringService, MonitoringService, } from '@kit/monitoring-core'; import { createRegistry } from '@kit/shared/registry'; import { MonitoringProvider, getMonitoringProvider, } from '../get-monitoring-provider'; const serverMonitoringRegistry = createRegistry< MonitoringService, NonNullable >(); // Existing Sentry registration serverMonitoringRegistry.register('sentry', async () => { const { SentryMonitoringService } = await import('@kit/sentry'); return new SentryMonitoringService(); }); // Add LogRocket registration serverMonitoringRegistry.register('logrocket', async () => { const { LogRocketMonitoringService } = await import('@kit/logrocket'); return new LogRocketMonitoringService(); }); export async function getServerMonitoringService() { const provider = getMonitoringProvider(); if (!provider) { return new ConsoleMonitoringService(); } return serverMonitoringRegistry.get(provider); } ``` ## Environment Variables Add your provider's configuration: ```bash {% title="apps/web/.env.local" %} # Enable LogRocket as the monitoring provider NEXT_PUBLIC_MONITORING_PROVIDER=logrocket # LogRocket configuration NEXT_PUBLIC_LOGROCKET_APP_ID=your-org/your-app ``` ## Example: Datadog Integration Here's a complete example for Datadog RUM: ```typescript {% title="packages/monitoring/datadog/src/datadog-monitoring.service.ts" %} import { datadogRum } from '@datadog/browser-rum'; import { MonitoringService } from '@kit/monitoring-core'; export class DatadogMonitoringService implements MonitoringService { private readonly readyPromise: Promise; private readyResolver?: (value?: unknown) => void; constructor() { this.readyPromise = new Promise( (resolve) => (this.readyResolver = resolve), ); void this.initialize(); } async ready() { return this.readyPromise; } captureException(error: Error, extra?: Record) { datadogRum.addError(error, { ...extra, }); } captureEvent(event: string, extra?: Record) { datadogRum.addAction(event, extra); } identifyUser(user: { id: string; email?: string; name?: string }) { datadogRum.setUser({ id: user.id, email: user.email, name: user.name, }); } private async initialize() { if (typeof window === 'undefined') { this.readyResolver?.(); return; } datadogRum.init({ applicationId: process.env.NEXT_PUBLIC_DATADOG_APP_ID!, clientToken: process.env.NEXT_PUBLIC_DATADOG_CLIENT_TOKEN!, site: process.env.NEXT_PUBLIC_DATADOG_SITE ?? 'datadoghq.com', service: process.env.NEXT_PUBLIC_DATADOG_SERVICE ?? 'my-saas', env: process.env.NEXT_PUBLIC_DATADOG_ENV ?? 'production', sessionSampleRate: 100, sessionReplaySampleRate: 20, trackUserInteractions: true, trackResources: true, trackLongTasks: true, }); this.readyResolver?.(); } } ``` ## Common Gotchas 1. **Browser-only initialization** - Check `typeof window !== 'undefined'` before accessing browser APIs. 2. **Ready state** - The `ready()` method must resolve after initialization completes. Server contexts call `await service.ready()` before capturing. 3. **Provider enum** - Remember to add your provider to the `MONITORING_PROVIDERS` array in `get-monitoring-provider.ts`. 4. **Lazy loading** - Providers are loaded lazily through the registry. Don't import the monitoring service directly in your main bundle. 5. **Server vs client** - Some providers (like LogRocket) are browser-only. Return a no-op or console fallback for server contexts. This monitoring system is part of the [Next.js Supabase SaaS Kit](/next-supabase-turbo). --- **Previous:** [Sentry Configuration ←](./sentry)