Custom Events

Track application-specific events beyond the default page loads, vitals, and errors. Custom events help you understand business metrics, user behavior, and feature adoption.

Types of Events

1. User Actions

Track clicks, form submissions, feature usage:

// Track button click
rum.trackEvent('button_click', {
  button_id: 'signup-cta',
  location: 'homepage-hero'
});

// Track form submission
rum.trackEvent('form_submit', {
  form_name: 'contact',
  fields_filled: 5,
  validation_errors: 0
});

// Track feature usage
rum.trackEvent('feature_used', {
  feature: 'export_pdf',
  user_tier: 'pro'
});

2. Business Metrics

Track conversions, revenue, cart actions:

// Track purchase
rum.trackEvent('purchase', {
  order_id: 'ORD-12345',
  total: 49.99,
  currency: 'USD',
  items: 3
});

// Track add to cart
rum.trackEvent('add_to_cart', {
  product_id: 'SKU-789',
  product_name: 'Widget Pro',
  price: 29.99,
  quantity: 1
});

// Track signup
rum.trackEvent('signup', {
  method: 'email',
  plan: 'free',
  referrer: 'google_ads'
});

3. Performance Milestones

Track custom timing metrics:

// Track API response time
const start = performance.now();
await fetch('/api/data');
const duration = performance.now() - start;

rum.trackTiming('api_call', duration, {
  endpoint: '/api/data',
  method: 'GET'
});

// Track feature render time
rum.trackTiming('dashboard_render', 245, {
  widgets: 12,
  data_points: 5000
});

Auto-Tracking

Click Tracking

Automatically track all clicks:

const rum = new StatusRadarRUM({
  apiKey: 'your-api-key',
  appId: 'your-app-id',
  endpoint: 'https://rum.statusradar.dev/v1/ingest',

  trackInteractions: true,  // Enable auto-tracking
  trackClicks: {
    enabled: true,
    captureText: true,       // Include button/link text
    captureSelector: true,   // Include CSS selector
    ignoreSelectors: ['.ad', '.analytics']  // Ignore these
  }
});

Navigation Tracking

Track SPA route changes:

// React Router
import { useLocation } from 'react-router-dom';

function App() {
  const location = useLocation();

  useEffect(() => {
    rum.trackPageView({
      path: location.pathname,
      search: location.search,
      referrer: document.referrer
    });
  }, [location]);
}

// Manual tracking
window.addEventListener('popstate', () => {
  rum.trackPageView({
    path: window.location.pathname
  });
});

Form Tracking

Track form interactions:

rum.trackForms({
  enabled: true,
  captureValues: false,  // Don't capture input values (privacy)
  trackAbandonment: true,
  events: ['submit', 'abandon', 'error']
});

Event Properties

Standard Properties

Every event includes:

  • event_type: Type of event
  • timestamp: When it occurred
  • session_id: Current session ID
  • page_url: Current page
  • user_agent: Browser info

Custom Properties

Add your own metadata:

rum.trackEvent('video_play', {
  video_id: 'intro-2024',
  duration: 120,
  quality: '1080p',
  autoplay: false
});

User Properties

Associate events with user data:

// Set user context
rum.setUser({
  id: 'user-12345',
  email: '[email protected]',
  name: 'John Doe',
  plan: 'pro',
  signup_date: '2024-01-15'
});

// All subsequent events include user context
rum.trackEvent('feature_used', {
  feature: 'export'
});
// ^ Will include user_id, plan, etc.

Event Filtering

Client-Side Filtering

Filter events before sending:

rum.configure({
  beforeSend: (event) => {
    // Don't send events from internal IPs
    if (event.ip_address?.startsWith('192.168')) {
      return null;  // Drop event
    }

    // Add extra context
    event.properties.environment = 'production';
    event.properties.version = '1.2.3';

    return event;  // Send modified event
  }
});

Server-Side Filtering

Configure in Dashboard:

  1. Dashboard β†’ Observability β†’ Apps β†’ Settings
  2. Event Filters section
  3. Add filter rules:
    • Drop events by type
    • Drop events by property
    • Sample specific event types

Event Aggregation

Dashboard Metrics

View aggregated events:

  • Events β†’ Timeline: Events over time
  • Events β†’ Top Events: Most frequent events
  • Events β†’ Properties: Event property distribution

Custom Dashboards

Create charts from event data:

-- In dashboard query builder
SELECT
  event_type,
  COUNT(*) as count,
  AVG(properties.duration) as avg_duration
FROM events
WHERE app_id = 'your-app-id'
  AND timestamp > NOW() - INTERVAL '24 hours'
GROUP BY event_type
ORDER BY count DESC

Performance Considerations

Batch Events

Events are automatically batched and sent every 10 seconds:

rum.configure({
  batchSize: 50,          // Send when 50 events collected
  batchInterval: 10000,   // Or every 10 seconds
  maxQueueSize: 1000      // Drop old events if queue full
});

Sampling

Sample high-volume events:

// Track only 10% of clicks
rum.trackEvent('click', data, {
  sampleRate: 0.1
});

// Or configure per event type
rum.configure({
  sampleRates: {
    'click': 0.1,
    'page_view': 1.0,
    'error': 1.0
  }
});

Event Validation

Schema Validation

Define expected event schema:

rum.defineEvent('purchase', {
  properties: {
    order_id: { type: 'string', required: true },
    total: { type: 'number', required: true },
    currency: { type: 'string', required: true },
    items: { type: 'number', required: false }
  }
});

// This will warn if properties don't match schema
rum.trackEvent('purchase', {
  order_id: 'ORD-123',
  total: '49.99'  // ⚠️  Warning: total should be number
});

Type Safety (TypeScript)

interface PurchaseEvent {
  order_id: string;
  total: number;
  currency: string;
  items?: number;
}

rum.trackEvent<PurchaseEvent>('purchase', {
  order_id: 'ORD-123',
  total: 49.99,
  currency: 'USD',
  items: 3
});

Event Debugging

Console Logging

Enable debug mode:

rum.configure({
  debug: true  // Log all events to console
});

// Or manually log events
rum.on('event', (event) => {
  console.log('RUM Event:', event);
});

Network Inspection

Check network tab for:

  • POST to rum.statusradar.dev/v1/ingest
  • Status 202 (Accepted)
  • Response: {"status":"ok","received":50}

Dashboard Validation

Real-time event stream:

  1. Dashboard β†’ Observability β†’ Apps β†’ [Your App]
  2. Events tab
  3. Enable "Live Stream"
  4. See events as they arrive

Advanced Patterns

Event Hierarchy

Group related events:

// User journey
rum.trackEvent('checkout.started');
rum.trackEvent('checkout.shipping_entered');
rum.trackEvent('checkout.payment_entered');
rum.trackEvent('checkout.completed');

// Query in dashboard with prefix
events.where(event_type LIKE 'checkout.%')

Event Sequences

Track multi-step flows:

const flowId = generateId();

rum.trackEvent('signup.started', { flow_id: flowId });
rum.trackEvent('signup.email_entered', { flow_id: flowId });
rum.trackEvent('signup.password_entered', { flow_id: flowId });
rum.trackEvent('signup.completed', { flow_id: flowId });

// Analyze funnel conversion in dashboard

Error Context

Add context to errors:

rum.on('error', (error) => {
  rum.trackEvent('error_context', {
    error_message: error.message,
    user_action: 'submitting_form',
    form_data: { fields: 5, validation_passed: false }
  });
});

Quota Management

Events count towards monthly quota. Optimize with:

  1. Sample high-volume events (clicks, mouse moves)
  2. Filter redundant events (duplicate page views)
  3. Batch events before sending
  4. Monitor quota in dashboard

See: Quota Management

Best Practices

DO βœ…

  • Use descriptive event names: checkout_completed not event123
  • Include relevant context in properties
  • Set user context for personalized analytics
  • Sample high-frequency events
  • Validate event schemas in development

DON'T ❌

  • Send PII (emails, phone numbers) in events
  • Track every single click (use sampling)
  • Send large payloads (>100KB per event)
  • Hardcode sensitive data in event properties
  • Forget to test event tracking in staging

API Reference

// Track custom event
rum.trackEvent(eventType: string, properties?: object, options?: object)

// Track timing
rum.trackTiming(name: string, duration: number, properties?: object)

// Track page view
rum.trackPageView(properties?: object)

// Set user context
rum.setUser(userProperties: object)

// Update user properties
rum.updateUser(properties: object)

// Clear user context
rum.clearUser()

// Listen to events
rum.on('event', callback)
rum.on('error', callback)
rum.on('pageview', callback)

Next Steps