Skip to content

Add OpenTelemetry Browser Analytics #22

Description

@visoedhwa

Add OpenTelemetry Browser Analytics

Summary

Add client-side OpenTelemetry instrumentation to the Open Data Portal (Docusaurus site) to collect anonymous usage analytics (page views, navigation timing, web vitals, errors).

Background

The browser OTel SDK (@opentelemetry/sdk-logs + @opentelemetry/browser-instrumentation) batches log records and POSTs them to /v1/logs, which nginx reverse-proxies to the in-cluster OpenTelemetry Collector (opentelemetry-collector.observability.svc:4318).

Tasks

1. Install OpenTelemetry dependencies

Add to package.json:

@opentelemetry/api-logs
@opentelemetry/sdk-logs
@opentelemetry/exporter-logs-otlp-http
@opentelemetry/resources
@opentelemetry/semantic-conventions
@opentelemetry/instrumentation
@opentelemetry/browser-instrumentation

2. Create analytics module (src/analytics/)

Create the following files:

  • src/analytics/init.ts — Initialize LoggerProvider with:

    • resourceFromAttributes setting service.name = open-data and service.version from package.json
    • BatchLogRecordProcessor + OTLPLogExporter({ url: '/v1/logs' }) in production
    • SimpleLogRecordProcessor + ConsoleLogRecordExporter in development
    • Register browser instrumentations: NavigationInstrumentation, NavigationTimingInstrumentation, UserActionInstrumentation, WebVitalsInstrumentation, ErrorsInstrumentation
    • Export a logEvent(eventName, attributes) helper
  • src/analytics/useAnalytics.ts — React hook providing:

    • Session ID via sessionStorage + crypto.randomUUID()
    • trackEvent(name, attrs) — logs with session ID, user agent, screen resolution, referrer
    • trackPageView() — logs current URL
    • session_start event on mount
    • session_heartbeat every 60 s (only when tab is visible)
  • src/analytics/AnalyticsContext.tsx — React context + AnalyticsProvider wrapper + useTrackEvent hook

3. Integrate into Docusaurus

Docusaurus uses a clientModules config option for side-effect imports. Add a client module (e.g. src/analytics/clientModule.ts) that calls initAnalytics() on load, and register it in docusaurus.config.ts:

clientModules: ['./src/analytics/clientModule.ts'],

Wrap the root with AnalyticsProvider using a swizzled Root component or a Docusaurus plugin's Root wrapper.

4. Add nginx reverse-proxy for /v1/logs

In conf.d/default.conf, add a location block (before the SPA fallback):

# Analytics: proxy browser OTLP log requests to the in-cluster collector
location = /v1/logs {
  proxy_pass http://opentelemetry-collector.observability.svc:4318/v1/logs;
  proxy_http_version 1.1;
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header Content-Type $content_type;
  client_max_body_size 64k;
  access_log off;
}

5. Update Content-Security-Policy

Update the CSP connect-src directive in conf.d/default.conf to allow 'self' (already present — just verify /v1/logs is same-origin so no change is needed).

6. Track page navigation events

Add a trackPageView() call on route changes. In Docusaurus this can be done via the onRouteDidUpdate lifecycle in a client module:

export function onRouteDidUpdate({ location }) {
  logEvent('page_view', { url: location.pathname });
}

Acceptance Criteria

  • npm run build succeeds with no type errors
  • In dev mode, OTel log records appear in the browser console
  • In production, POST requests to /v1/logs are proxied to the collector (verify with curl or browser DevTools)
  • Browser instrumentations capture: navigation, navigation-timing, web-vitals, user-actions, errors
  • Session heartbeat fires every 60 s while page is visible
  • No analytics failure ever breaks page rendering (wrap in try/catch)
  • Helm chart requires no changes (nginx config is baked into the image)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions