monitoring

Catch errors in the browser and on the server, and see which user hit them and what they did first.

Analytics & errorsClient ServerBuilt onSentry

Every export of kitr/monitoring, kitr/monitoring/provider, kitr/monitoring/server, read from the published type definitions. The guide shows them in use.

kitr/monitoring

consoleReporter

function consoleReporter(): Reporter;

Prints reports. The default reporter in development.

createMonitoring

function createMonitoring(config?: MonitoringConfig): Monitoring;

defaultShouldReport

function defaultShouldReport(error: unknown): boolean;

Default filter: kitr errors that are client mistakes (4xx) are not incidents.

devtoolsReporter

function devtoolsReporter(hub: {
  log(channel: string, label: string, data?: unknown, level?: 'debug' | 'info' | 'warn' | 'error'): void;
}): Reporter;

Mirrors reports into the kitr devtools (channel "errors"). Added automatically in development.

installGlobalHandlers

function installGlobalHandlers(monitoring: Monitoring): () => void;

Window error and unhandled-rejection handlers. Returns a disposer.

memoryReporter

function memoryReporter(): Reporter & {
  exceptions: Array<{
    error: unknown;
    context?: CaptureContext;
  }>;
  messages: string[];
  user: MonitoringUser | null;
};

Keeps reports in memory, for tests.

sentry

function sentry(Sentry: SentryLike, options: {
  dsn: string;
  environment?: string;
  release?: string;
  tracesSampleRate?: number;
  [key: string]: unknown;
}): Reporter;

Sentry reporter. Pass the SDK module so the right one (react, browser, node) is bundled for the environment: sentry(Sentry, { dsn }).

defineConfig

const defineConfig: <const C extends MonitoringConfig>(config: C) => C;
interface Breadcrumb {
  category?: string;
  message: string;
  level?: Severity;
  data?: Record<string, unknown>;
  timestamp?: number;
}

CaptureContext

interface CaptureContext {
  tags?: Record<string, string>;
  extra?: Record<string, unknown>;
  level?: Severity;
  /** Where it came from: 'error-boundary' | 'router' | 'server' | 'window' | 'action'. */
  source?: string;
}

Monitoring

interface Monitoring {
  captureException(error: unknown, context?: CaptureContext): void;
  captureMessage(message: string, context?: CaptureContext): void;
  setUser(user: MonitoringUser | null): void;
  addBreadcrumb(crumb: Breadcrumb): void;
  setTag(key: string, value: string): void;
  /** Run `fn`, report and rethrow on failure. */
  wrap<T>(fn: () => T, context?: CaptureContext): T;
  flush(timeoutMs?: number): Promise<void>;
  readonly reporters: readonly Reporter[];
}

MonitoringConfig

interface MonitoringConfig {
  reporters?: Reporter[];
  /** Return false to drop an error (expected 4xx, cancelled requests). Default drops kitr errors with status < 500. */
  shouldReport?: (error: unknown, context?: CaptureContext) => boolean;
  /** Capture `window.onerror` and unhandled rejections on the client. Default true. */
  globalHandlers?: boolean;
  /** Release / environment tags applied to every report. */
  tags?: Record<string, string>;
  /** Log to the console as well. Default: development. */
  debug?: boolean;
}

MonitoringUser

interface MonitoringUser {
  id: string;
  email?: string;
  username?: string;
  [key: string]: unknown;
}

Reporter

interface Reporter {
  name: string;
  init?(): void | Promise<void>;
  captureException(error: unknown, context?: CaptureContext): void;
  captureMessage?(message: string, context?: CaptureContext): void;
  setUser?(user: MonitoringUser | null): void;
  addBreadcrumb?(crumb: Breadcrumb): void;
  setTag?(key: string, value: string): void;
  flush?(timeoutMs?: number): Promise<boolean>;
}

What every destination implements.

SentryLike

interface SentryLike {
  init?(options: Record<string, unknown>): unknown;
  captureException(error: unknown, context?: Record<string, unknown>): unknown;
  captureMessage(message: string, context?: Record<string, unknown>): unknown;
  setUser(user: Record<string, unknown> | null): void;
  addBreadcrumb(crumb: Record<string, unknown>): void;
  setTag(key: string, value: string): void;
  flush?(timeout?: number): Promise<boolean>;
}

The subset of @sentry/react / @sentry/browser / @sentry/node these reporters call.

Severity

type Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug';

kitr/monitoring/provider

MonitoringErrorBoundary

class MonitoringErrorBoundary extends Component<ErrorBoundaryProps, {
  error: unknown;
}> {
  state: {
    error: unknown;
  };
  static getDerivedStateFromError(error: unknown): {
    error: unknown;
  };
  private report;
  componentDidCatch(error: unknown, info: ErrorInfo): void;
  reset: () => void;
  render(): ReactNode;
}

Reports render errors, then shows fallback (or nothing, so the router's error component takes over).

monitoringProvider

const monitoringProvider: import("@kitr/core").KitrProvider;

Binds monitoring from config/monitoring.ts, installs window handlers on the client, and wraps the tree in an error boundary that reports before TanStack Router's error component renders.

ErrorBoundaryProps

interface ErrorBoundaryProps {
  monitoring: Monitoring;
  fallback?: ReactNode | ((error: unknown, reset: () => void) => ReactNode);
  children: ReactNode;
}

kitr/monitoring/server

reportErrors

function reportErrors(monitoring: Monitoring): (event: {
  req: {
    method: string;
    url: string;
  };
  context?: Record<string, any>;
}, next: () => Promise<unknown> | unknown) => Promise<unknown>;

h3 middleware that reports thrown errors (after the request context exists), then rethrows so kitr's error handler still produces the response. server.use(reportErrors(monitoring)).