@9apes/rum-sdk
v0.1.1
Published
9apes Real User Monitoring SDK helps you track the real time performance, errors, and analytics of your application's users.
Downloads
228
Readme
9APES RUM SDK
9apes Real User Monitoring SDK helps you track the real time performance, errors, and analytics of your application's users.
Installation
npm install @9apes/rum-sdk
# or
pnpm add @9apes/rum-sdkUsage
import { initWatchdog } from '@9apes/rum-sdk';
initWatchdog({
apiKey: 'your-api-key',
projectId: 'your-project-id',
organizationId: 'your-organization-id',
enableProfiling: false, // optional
userId: 'user-123' // optional
});Configuration
Required Fields
{
apiKey: string; // Your API key
projectId: string; // Your project ID
organizationId: string;
}Optional Configuration
{
// User identification
userId?: string;
// Batch configuration
batchSize?: number; // Default: 25
flushInterval?: number; // Default: 15000 (15 seconds)
// Feature toggles (all true by default)
enableWebVitals?: boolean;
enableErrorTracking?: boolean;
enablePerformanceTracking?: boolean;
enableBehaviorTracking?: boolean;
enableBusinessMetrics?: boolean;
enableSessionRecording?: boolean;
// Privacy settings
enablePrivacyMode?: boolean; // Default: true
maskAllInputs?: boolean; // Default: true
sensitiveSelectors?: string[]; // CSS selectors to mask
// Session recording
maxRecordingTime?: number; // Default: 1800000 (30 min)
segmentDuration?: number; // Default: 30000 (30 sec)
maxSegmentSize?: number; // Default: 500 events
// Error handling
maxStackTraceLength?: number; // Default: 1000
maxErrorMessageLength?: number; // Default: 500
sendCriticalErrorsImmediately?: boolean; // Default: true
criticalErrorDelay?: number; // Default: 0
// Debug
debug?: boolean; // Default: false
}API Reference
Core Functions
initWatchdog(config: WatchdogConfig): void
Initialize Watchdog with your configuration.
initWatchdog({
apiKey: 'your-api-key',
projectId: 'your-project',
});destroyWatchdog(): void
Cleanup and destroy the Watchdog instance.
destroyWatchdog();Tracking Functions
trackFeatureUsage(featureName: string, context?: object): void
Track feature usage for product analytics.
trackFeatureUsage('export-pdf', {
format: 'a4',
pages: 10
});trackConversion(event: string, value?: number, metadata?: object): void
Track conversion events.
trackConversion('purchase', 99.99, {
productId: '123',
quantity: 2
});addCustomBreadcrumb(message: string, data?: object): void
Add debugging breadcrumbs for error context.
addCustomBreadcrumb('User clicked checkout', {
cartItems: 3,
total: 149.99
});Session Control
pauseRecording(): void
Pause session recording (useful for sensitive screens).
pauseRecording();resumeRecording(): void
Resume session recording.
resumeRecording();updateUserId(userId: string): void
Update the user ID after authentication.
updateUserId('user-456');Utilities
forceFlush(): void
Immediately flush all buffered events.
forceFlush();getAnalyticsStatus(): object
Get current tracking status.
const status = getAnalyticsStatus();
console.log(status);
// {
// isRecording: true,
// sessionId: '...',
// bufferSize: 5,
// collectors: ['WebVitalsCollector', 'ErrorCollector', ...]
// }Architecture
Modular Design
Watchdog uses a modular architecture with clear separation of concerns:
┌─────────────────────────────────────────────────────┐
│ Public API │
└────────────────────┬────────────────────────────────┘
│
┌────────────────────▼────────────────────────────────┐
│ WatchdogCore │
│ ┌─────────────────────────────────────────────┐ │
│ │ Event Pipeline │ │
│ │ Collectors → EventBus → Middleware → Queue │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
│ │ │
┌────▼───┐ ┌────▼────┐ ┌───▼────┐
│Collect-│ │Middle- │ │Trans- │
│ors │ │ware │ │port │
└────────┘ └─────────┘ └────────┘Collectors
Each collector is independent and focuses on a specific tracking concern:
- WebVitalsCollector: Core Web Vitals (CLS, INP, FCP, LCP, TTFB)
- ErrorCollector: JavaScript errors, resource errors, promise rejections
- PerformanceCollector: Navigation timing, resource timing, memory usage
- BehaviorCollector: Clicks, scrolls, form interactions, rage clicks
- NetworkCollector: Fetch/XHR tracking
- SessionRecorder: rrweb-based session replay
Design Patterns
Observer Pattern (EventBus)
Collectors emit events without knowing who consumes them:
// Collector emits
eventBus.emit('event:collected', event);
// Core subscribes
eventBus.subscribe('event:collected', (event) => {
// Process event
});Strategy Pattern
Severity Classification
const classifier = new SeverityClassifier();
const severity = classifier.classify({
type: 'javascript',
error: new Error('Chunk load failed')
});
// Returns: 'critical'Transport Selection
// Automatically selects best available transport:
// 1. Fetch (priority: 100)
// 2. Beacon (priority: 50)
// 3. XHR (priority: 10)
await transportManager.send(payload);Middleware System
Watchdog includes a powerful middleware system with three hooks:
Built-in Middleware
- BreadcrumbEnricher: Attaches breadcrumbs to error events
- SessionEnricher: Adds session-level metrics
- ViewportEnricher: Adds device category and derived viewport data
- ConnectionEnricher: Adds network quality classification
Custom Middleware
Register your own middleware to enrich or transform events:
import { registerMiddleware } from '@9apes/rum-sdk';
registerMiddleware({
name: 'my-enricher',
// Filter events before collection
onPreCollect(event) {
if (event.type === 'internal-debug') {
return null; // Filter out
}
return event;
},
// Enrich events after collection
onPostCollect(event) {
return {
...event,
customField: 'value',
timestamp: Date.now()
};
},
// Transform before sending
onPreSend(batch) {
return {
...batch,
metadata: {
buildVersion: '1.2.3'
}
};
}
});ClickHouse Schema
Watchdog uses a flat, ClickHouse-optimized schema for efficient analytics.
Event Metadata (All Events)
CREATE TABLE events (
-- Event identification
event_id String, -- UUIDv7 (time-ordered)
event_type LowCardinality(String),
event_category LowCardinality(String),
timestamp DateTime64(3),
-- Session and user
session_id LowCardinality(String),
user_id Nullable(String),
-- Page context
page_url String,
page_path String,
page_title Nullable(String),
referrer Nullable(String),
-- Device and browser
user_agent LowCardinality(String),
viewport_width UInt16,
viewport_height UInt16,
screen_width UInt16,
screen_height UInt16,
device_pixel_ratio Float32,
-- Platform
platform LowCardinality(String),
language LowCardinality(String),
timezone LowCardinality(String),
-- Network
connection_type Nullable(LowCardinality(String)),
connection_downlink Nullable(Float32),
connection_rtt Nullable(UInt16),
-- Event-specific fields (nullable)
-- See specific event types below
-- Backend enrichment (add these in your pipeline)
country LowCardinality(String),
city Nullable(String),
-- ... other enrichment fields
) ENGINE = MergeTree()
ORDER BY (event_category, event_type, timestamp);Error Events
-- Additional fields for error events
error_message String,
error_stack Nullable(String),
error_severity LowCardinality(String), -- 'low' | 'medium' | 'high' | 'critical'
error_filename Nullable(String),
error_line Nullable(UInt32),
error_column Nullable(UInt32),
breadcrumbs_json String, -- JSON array
resource_url Nullable(String),
resource_type Nullable(LowCardinality(String)),
http_status Nullable(UInt16),
http_status_text Nullable(String)Web Vital Events
-- Additional fields for web vital events
metric_name LowCardinality(String), -- 'CLS' | 'INP' | 'FCP' | 'LCP' | 'TTFB'
metric_value Float64,
metric_rating LowCardinality(String), -- 'good' | 'needs-improvement' | 'poor'
metric_delta Float64,
metric_id StringSee CLICKHOUSE_SCHEMA.md for complete schema documentation.
Examples
React Integration
import { useEffect } from 'react';
import { initWatchdog, updateUserId } from '@9apes/rum-sdk';
function App() {
useEffect(() => {
initWatchdog({
apiKey: process.env.REACT_APP_WATCHDOG_API_KEY!,
projectId: 'my-react-app',
endpoint: 'https://analytics.example.com/events'
});
}, []);
const handleLogin = (user) => {
updateUserId(user.id);
};
return <YourApp />;
}Next.js Integration
// pages/_app.tsx
import { useEffect } from 'react';
import type { AppProps } from 'next/app';
import { initWatchdog } from '@9apes/rum-sdk';
function MyApp({ Component, pageProps }: AppProps) {
useEffect(() => {
initWatchdog({
apiKey: process.env.NEXT_PUBLIC_WATCHDOG_API_KEY!,
projectId: 'my-nextjs-app',
endpoint: '/api/analytics'
});
}, []);
return <Component {...pageProps} />;
}
export default MyApp;Vue Integration
// main.ts
import { createApp } from 'vue';
import { initWatchdog } from '@9apes/rum-sdk';
import App from './App.vue';
initWatchdog({
apiKey: import.meta.env.VITE_WATCHDOG_API_KEY,
projectId: 'my-vue-app',
endpoint: 'https://analytics.example.com/events'
});
createApp(App).mount('#app');Adding Custom Collectors
Adding a new collector is straightforward:
// collectors/BfCacheCollector.ts
import { BaseCollector } from '@9apes/rum-sdk/collectors/BaseCollector';
import type { EventBus } from '@9apes/rum-sdk/core/EventBus';
export class BfCacheCollector extends BaseCollector {
constructor(eventBus: EventBus, sessionId: string, userId: string | null) {
super('BfCacheCollector', eventBus, sessionId, userId);
}
initialize(): void {
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
const baseMetadata = this.getBaseMetadata();
this.emit('event:collected', {
...baseMetadata,
event_type: 'bfcache_restore',
event_category: 'performance'
});
}
});
this.isInitialized = true;
}
destroy(): void {
// Cleanup
this.isInitialized = false;
}
}License
MIT
Support
For issues and questions, please open an issue on GitHub.
