# Custom Monitoring Provider

> How to extend the repo's monitoring layer with your own provider.

*Canonical: https://makerkit.dev/docs/tanstack-drizzle/monitoring/custom-monitoring-provider*

---

No third-party provider is registered by default, but the monitoring layer is
extensible. A complete provider must be registered in **four** places: the provider enum,
the React provider component, the server service, and the instrumentation
registries (client + server).

## 1. Add the Provider Name

Update the provider enum:

```typescript {% title="packages/monitoring/api/src/get-monitoring-provider.ts" %}
const MONITORING_PROVIDERS = [
  'my-provider',
  '',
] as const;
```

## 2. Register the Client Provider Component

```typescript {% title="packages/monitoring/api/src/components/provider.tsx" %}
monitoringProviderRegistry.register('my-provider', async () => {
  const { MyProvider } = await import('@kit/my-monitoring/provider');

  return {
    default: function Wrapper({ children }: React.PropsWithChildren) {
      return <MyProvider>{children}</MyProvider>;
    },
  };
});
```

## 3. Register the Server Service

```typescript {% title="packages/monitoring/api/src/services/get-server-monitoring-service.ts" %}
serverMonitoringRegistry.register('my-provider', async () => {
  const { MyMonitoringService } = await import('@kit/my-monitoring');

  return new MyMonitoringService();
});
```

## 4. Register Server Instrumentation

The server registration must export `onRequestError` (an `Instrumentation`-style
request-error contract).
A route `errorComponent` relies on `error.digest` being set for any
server-originated error so it can skip re-capturing — that dedup only works if
every provider records server errors here, with the proper request context
(route, method, headers).

```typescript {% title="packages/monitoring/api/src/instrumentation.ts" %}
instrumentationRegistry.register('my-provider', async () => {
  const { initMyProviderServer, captureMyProviderRequestError } =
    await import('@kit/my-monitoring/server');

  return {
    register: () => {
      initMyProviderServer({
        environment: process.env.VITE_MY_PROVIDER_ENVIRONMENT,
      });
    },
    onRequestError: captureMyProviderRequestError,
  };
});
```

## 5. Register Client Instrumentation

The client instrumentation owns the global `window.onerror` and
`unhandledrejection` handlers. The provider must supply `init` and
`captureException`; the buffer-then-replay machinery is handled for you.

```typescript {% title="packages/monitoring/api/src/instrumentation-client.ts" %}
clientInstrumentationRegistry.register('my-provider', async () => {
  const { initMyProviderBrowser, captureMyProviderException } =
    await import('@kit/my-monitoring/client');

  return {
    init: () => {
      initMyProviderBrowser({
        environment: process.env.VITE_MY_PROVIDER_ENVIRONMENT,
      });
    },
    captureException: (error, mechanism) => {
      captureMyProviderException(error, { handled: false, type: mechanism });
    },
  };
});
```

{% alert type="warn" title="Disable provider-owned global handlers" %}
If your SDK installs its own `window.onerror` / `unhandledrejection` handlers
(e.g. Sentry's `GlobalHandlers` integration), disable them in your `init` so
they don't double-report alongside the kit's own handlers.
{% /alert %}

## 6. Configure the Environment

```bash
VITE_MONITORING_PROVIDER=my-provider
```

## Notes

- The repo is ESM. Use `import`, not `require`.
- All five registrations are required. If any is missing, the client/server
experience will diverge or errors will silently drop.
- Leaving the env var empty keeps the console fallback.
