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
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:2. Create analytics module (
src/analytics/)Create the following files:
src/analytics/init.ts— InitializeLoggerProviderwith:resourceFromAttributessettingservice.name=open-dataandservice.versionfrompackage.jsonBatchLogRecordProcessor+OTLPLogExporter({ url: '/v1/logs' })in productionSimpleLogRecordProcessor+ConsoleLogRecordExporterin developmentNavigationInstrumentation,NavigationTimingInstrumentation,UserActionInstrumentation,WebVitalsInstrumentation,ErrorsInstrumentationlogEvent(eventName, attributes)helpersrc/analytics/useAnalytics.ts— React hook providing:sessionStorage+crypto.randomUUID()trackEvent(name, attrs)— logs with session ID, user agent, screen resolution, referrertrackPageView()— logs current URLsession_startevent on mountsession_heartbeatevery 60 s (only when tab is visible)src/analytics/AnalyticsContext.tsx— React context +AnalyticsProviderwrapper +useTrackEventhook3. Integrate into Docusaurus
Docusaurus uses a
clientModulesconfig option for side-effect imports. Add a client module (e.g.src/analytics/clientModule.ts) that callsinitAnalytics()on load, and register it indocusaurus.config.ts:Wrap the root with
AnalyticsProviderusing a swizzled Root component or a Docusaurus plugin'sRootwrapper.4. Add nginx reverse-proxy for
/v1/logsIn
conf.d/default.conf, add a location block (before the SPA fallback):5. Update Content-Security-Policy
Update the CSP
connect-srcdirective inconf.d/default.confto allow'self'(already present — just verify/v1/logsis 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 theonRouteDidUpdatelifecycle in a client module:Acceptance Criteria
npm run buildsucceeds with no type errors/v1/logsare proxied to the collector (verify withcurlor browser DevTools)