rilaykit
Monitoring

Monitoring Overview

Track form and workflow performance, errors, and user behavior with RilayKit's monitoring system.

RilayKit's opt-in monitoring system centers on a RilayMonitor that buffers events and dispatches them to one or more adapters.

If you never call initializeMonitoring, no events are collected and there is zero runtime overhead.

Quick Setup

1. Initialize the global monitor

Call initializeMonitoring once at startup.

import { initializeMonitoring, getGlobalMonitor, ConsoleAdapter } from '@rilaykit/core';

initializeMonitoring({
  enabled: true,
  sampleRate: 1.0,
  flushInterval: 30000,
}, {
  appName: 'my-app',
  environment: 'production',
});

2. Attach an adapter

Adapters determine where events go. Attach as many as you need.

const monitor = getGlobalMonitor();
monitor?.addAdapter(new ConsoleAdapter('info'));

3. Track events

Workflow analytics pick up the global monitor automatically. You can also track manually.

monitor?.track('form_submission', 'checkout', { coupon: 'SUMMER' });

The RilayMonitor Class

class RilayMonitor {
  constructor(config: MonitoringConfig, context?: Partial<MonitoringContext>)

  addAdapter(adapter: MonitoringAdapter): void
  removeAdapter(adapterName: string): void

  track(
    type: MonitoringEventType,
    source: string,
    data: Record<string, any>,
    metrics?: PerformanceMetrics,
    severity?: 'low' | 'medium' | 'high' | 'critical',
  ): void
  trackError(error: Error, source: string, context?: any): void

  getProfiler(): PerformanceProfiler
  updateContext(updates: Partial<MonitoringContext>): void

  flush(): Promise<void>
  destroy(): Promise<void>
}
  • track() -- Records an event ('form_submission', 'form_validation', 'workflow_navigation', 'error', ...). Events buffer and flush to all adapters every flushInterval (or when bufferSize is reached).
  • trackError() -- Records an 'error' event with severity: 'high' and the stack trace.
  • flush() -- Sends all buffered events immediately.
  • destroy() -- Flushes, then tears down internal timers.

Global Monitoring

Three functions manage a global singleton so you never pass the monitor around.

FunctionDescription
initializeMonitoring(config, context?)Creates and stores a global RilayMonitor (destroys any previous one).
getGlobalMonitor()Returns the global monitor, or null if not initialized.
destroyGlobalMonitoring()Awaits destroy() on the global monitor and clears the reference.

PerformanceProfiler

Each monitor exposes a PerformanceProfiler via getProfiler() for high-resolution timing.

const profiler = monitor.getProfiler();

profiler.start('data-fetch');
const data = await fetchData();
profiler.end('data-fetch');

// Or use marks and measures
profiler.mark('render-start');
profiler.mark('render-end');
profiler.measure('full-render', 'render-start', 'render-end');

const all = profiler.getAllMetrics();
MethodDescription
.start(label, metadata?)Starts a timer.
.end(label)Ends the timer; returns the PerformanceMetrics (or null if never started).
.mark(name)Places a named timestamp mark.
.measure(name, startMark, endMark?)Returns the duration between two marks.
.getMetrics(label)Metrics for one label, or null.
.getAllMetrics()All recorded metrics, keyed by label.
.clear(label?)Clears one label, or everything.

Integration with Forms

useFormMonitoring (from @rilaykit/forms/react) returns trackers wired to the global monitor. Call them from your own form UI; each is a no-op when no global monitor is active.

import { useFormMonitoring } from '@rilaykit/forms/react';

const {
  trackFormRender,
  trackFormValidation,
  trackFormSubmission,
  trackFieldChange,
  startPerformanceTracking,
  endPerformanceTracking,
} = useFormMonitoring({ formConfig });

Integration with Workflows

Workflow tracking is automatic: WorkflowProvider reports to the global monitor with no extra setup. Tracked events include step navigation and skips, step completion time, condition-evaluation timing, and overall completion. Abandonment is surfaced via the analytics.onWorkflowAbandon callback only — it does not reach the monitoring adapters.

Every workflow error path (step-transition failures, onAfterValidation throws, submission throws, persistence save/load/remove failures) reaches both analytics.onError and the monitoring adapters. A validation error that merely blocks Next is not an error event.

Complete Example

lib/monitoring.ts
import {
  initializeMonitoring,
  getGlobalMonitor,
  RemoteAdapter,
  DevelopmentAdapter,
} from '@rilaykit/core';

export function setupMonitoring() {
  const isDev = process.env.NODE_ENV === 'development';

  initializeMonitoring({
    enabled: true,
    sampleRate: isDev ? 1.0 : 0.25,
    flushInterval: isDev ? 5000 : 30000,
  }, {
    appName: 'my-saas',
    environment: isDev ? 'development' : 'production',
  });

  const monitor = getGlobalMonitor();
  if (!monitor) return;

  if (isDev) {
    monitor.addAdapter(new DevelopmentAdapter());
  } else {
    monitor.addAdapter(new RemoteAdapter({
      endpoint: 'https://analytics.example.com/events',
      apiKey: process.env.MONITORING_API_KEY!,
      batchSize: 50,
      retryAttempts: 3,
    }));
  }
}
app/layout.tsx
import { setupMonitoring } from '@/lib/monitoring';

setupMonitoring();

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return <html><body>{children}</body></html>;
}

On this page