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 everyflushInterval(or whenbufferSizeis reached).trackError()-- Records an'error'event withseverity: '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.
| Function | Description |
|---|---|
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();| Method | Description |
|---|---|
.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
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,
}));
}
}import { setupMonitoring } from '@/lib/monitoring';
setupMonitoring();
export default function RootLayout({ children }: { children: React.ReactNode }) {
return <html><body>{children}</body></html>;
}