Analytics Plugin

Built-in event tracking and request monitoring for SonicJS. Store custom events, query them via API, and view request metrics in the admin dashboard.


Overview

The Analytics plugin is included with SonicJS core and enabled by default. It provides two complementary capabilities:

πŸ“Š

Custom Event Tracking

Track named events via HTTP API β€” single or batch. Stored in the document repository and queryable by event name, category, user, session, or date range.

πŸ–₯️

Request Monitoring Dashboard

Admin dashboard at /admin/analytics showing last-24h request totals, unique IPs, average response time, error count, top URLs, and recent activity β€” sourced from system_logs.

Not currently implemented: automatic page view recording, session duration / bounce rate calculation, real-time active-user counts, reports UI, data export, and per-plugin configuration options.


Custom Events

Event Structure

Events are stored as analytics_event documents in the document repository. The fields you can track and filter on:

TrackEventInput

interface TrackEventInput {
  event: string                        // required β€” event name
  category?: string                    // default: 'user-activity'
  properties?: Record<string, unknown> // arbitrary metadata
  userId?: string
  sessionId?: string
  path?: string
  // ipAddress and userAgent are captured automatically from the request
}

Queryable Fields

FieldIndexed columnNotes
eventq_evt_eventExact match
categoryq_evt_categoryExact match
userIdq_evt_user_idExact match
sessionIdq_evt_session_idExact match
pathq_evt_pathExact match

API Reference

Track Event

No authentication required to write events.

Track a single event.

Request Body:

  • event (required) β€” event name string
  • category β€” event category (default: user-activity)
  • properties β€” optional metadata object
  • path β€” page URL path
  • sessionId β€” session identifier
  • userId β€” user identifier (overridden by authenticated user if logged in)

Track Single Event

curl -X POST 'https://your-app.workers.dev/api/events' \
  -H 'Content-Type: application/json' \
  -d '{
    "event": "button_click",
    "category": "ui",
    "properties": { "button": "signup", "variant": "hero" },
    "path": "/pricing"
  }'

Response

{
  "success": true,
  "eventId": "doc_abc123"
}

Track Batch Events

Send an array of up to 100 events in one request. Each event follows the same shape as a single event. Events are written sequentially β€” not in a single DB transaction.

Batch Track

curl -X POST 'https://your-app.workers.dev/api/events' \
  -H 'Content-Type: application/json' \
  -d '[
    { "event": "page_view", "path": "/home" },
    { "event": "page_view", "path": "/about" },
    { "event": "click", "category": "nav", "properties": { "target": "pricing" } }
  ]'

Response

{
  "success": true,
  "eventIds": ["doc_abc", "doc_def", "doc_ghi"],
  "count": 3
}

Query Events

Admin auth required.

Query Parameters:

  • event β€” filter by event name
  • category β€” filter by category
  • userId β€” filter by user ID
  • sessionId β€” filter by session ID
  • startDate β€” Unix timestamp (seconds)
  • endDate β€” Unix timestamp (seconds)
  • limit β€” max results (default: 50)
  • offset β€” pagination offset (default: 0)

Query Events

curl -X GET 'https://your-app.workers.dev/api/events?category=ui&limit=50' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Response

{
  "events": [
    {
      "id": "doc_abc123",
      "createdAt": 1700000000,
      "event": "button_click",
      "category": "ui",
      "path": "/pricing"
    }
  ],
  "total": 142
}

Event Stats

Admin auth required.

Returns total event count, unique user count, unique session count, and top 20 events by frequency.

Query Parameters:

  • startDate β€” Unix timestamp (seconds)
  • endDate β€” Unix timestamp (seconds)

Event Stats

curl -X GET 'https://your-app.workers.dev/api/events/stats' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Response

{
  "totalEvents": 5420,
  "uniqueUsers": 312,
  "uniqueSessions": 891,
  "topEvents": [
    { "event": "page_view", "count": 3100 },
    { "event": "button_click", "count": 980 }
  ]
}

Admin Dashboard

Path: /admin/analytics β€” requires admin role.

Displays last-24-hour metrics sourced from the system_logs table:

MetricSource
Total RequestsCOUNT(*) where category = 'api'
Unique VisitorsCOUNT(DISTINCT ip_address) where category = 'api'
Avg Response TimeAVG(duration) where category = 'api'
ErrorsCOUNT(*) where level IN ('error', 'fatal')
Top PagesTop 10 URLs by request count
Recent ActivityLast 20 API requests

Note: The dashboard reads from system_logs, not the analytics_event documents stored by POST /api/events. If system_logs is empty or doesn't exist yet, all metrics show zero β€” this is expected on a fresh install.

There are no sub-pages for reports, real-time monitoring, or settings. The only admin analytics URL is /admin/analytics.


Hooks Integration

The plugin registers four hooks at boot:

HookPriorityWhat it does
request:start1Generates an in-memory session ID (data.analytics.sessionId). Not persisted to DB.
request:end1Logs request duration to console.debug. No DB write.
user:login8Attempts to call context.services?.analyticsService?.trackEvent(...). Currently a no-op β€” analyticsService is not registered in the service context.
content:view8Same as user:login β€” no-op until analyticsService is wired up.

To track login or content-view events today, use POST /api/events directly from your handler instead of relying on hooks.


Data Storage

Events tracked via POST /api/events are stored as documents of type analytics_event in the unified documents table β€” not in separate analytics_pageviews or analytics_events tables (those don't exist).

Five generated columns index the queryable fields:

Generated columnMaps to
q_evt_eventjson_extract(data, '$.event')
q_evt_categoryjson_extract(data, '$.category')
q_evt_user_idjson_extract(data, '$.userId')
q_evt_session_idjson_extract(data, '$.sessionId')
q_evt_pathjson_extract(data, '$.path')

Each event is stored as a published document (is_published = 1, maxVersionsPerRoot = 1) under tenant default.


Next Steps

Was this page helpful?