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

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions