You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replace Plausible Analytics with OpenTelemetry Browser Instrumentation
Plausible Analytics is currently used for usage tracking across all pages. This issue tracks replacing it with OpenTelemetry browser instrumentation, sending structured log events through our own observability stack instead of a third-party service.
Plausible is loaded via an inline <script> block + external utils.js on every page
Custom events are fired from js/page-setup.js via trackPlausible(): content_section, nav_click, asset_download, external_click
Plausible also auto-captures page views
Constraints
This is a static multi-page site (Gulp + jQuery). There is no JS bundler — Gulp only copies raw .js files to dist/. To use OTel's npm packages in the browser, we need to add a minimal bundling step.
2. Approach: Minimal esbuild Bundling Step
Add a single esbuild step to the Gulp pipeline that bundles only the OTel analytics module (js/analytics/init.js) into a self-contained IIFE file (dist/js/otel-analytics.js). The rest of the build pipeline stays unchanged — all other JS files continue to be copied as-is.
3. What to Instrument (User Analytics Events)
Automatic (from OTel browser instrumentations)
These come free from @opentelemetry/browser-instrumentation:
Export global window.otelAnalytics.logEvent(eventName, attributes) and window.otelAnalytics.trackEvent(eventName, attributes) (with session ID + common attrs baked in)
Session management: generate session_id (UUID) in sessionStorage
Common attributes: user_agent, screen_resolution, referrer
Session heartbeat: setInterval every 60s (skip if document.hidden)
Emit session_start and page_view on init
Phase 3 — Integrate into HTML pages and remove Plausible
Add <script src="js/otel-analytics.js"></script> to all 8 pages (before closing </body>, after jQuery but before page-setup.js)
Call window.otelAnalytics.init() — or auto-init on script load
Remove the Plausible inline <script> block (window.plausible = ...) from all pages
Remove the <script defer src="https://common.ltc.bcit.ca/js/utils.js"> tag from all pages
Phase 4 — Instrument existing jQuery event handlers in page-setup.js
Replace all trackPlausible() calls with OTel equivalents and remove the trackPlausible() function:
Remove Plausible inline script block and utils.js script tag
EDIT
.gitignore
Add dist/js/otel-analytics.js if not already covered
6. Risks & Considerations
Bundle size: The OTel JS SDK adds ~30-50 KB gzipped. Acceptable for a documentation site, but monitor.
No bundler today: Adding esbuild is the lightest-touch way to get node_modules imports into a browser-ready IIFE. It only touches the analytics module — all other JS stays as-is.
Privacy: No PII collected. All events are anonymous. Same posture as current Plausible setup.
Failure isolation: BatchLogRecordProcessor is fire-and-forget. If the collector is unreachable, logs silently drop — the site functions normally.
Multi-page vs SPA: Each page navigation is a full page load, so session_start must be deduplicated via sessionStorage (only emit if no existing session). The heartbeat timer resets on each page, which is acceptable.
External dependency removal: Removing Plausible eliminates the external utils.js script load from common.ltc.bcit.ca, reducing third-party dependencies.
7. Suggested Implementation Order
Install deps + add esbuild Gulp task (Phase 1)
Create js/analytics/init.js (Phase 2)
Add Nginx /v1/logs proxy (Phase 5) — can be done in parallel
Add <script> tags to all HTML pages (Phase 3)
Instrument page-setup.js with OTel calls (Phase 4)
Test locally with console exporter, then deploy to staging (Phase 6)
Replace Plausible Analytics with OpenTelemetry Browser Instrumentation
Plausible Analytics is currently used for usage tracking across all pages. This issue tracks replacing it with OpenTelemetry browser instrumentation, sending structured log events through our own observability stack instead of a third-party service.
Target pipeline: Browser (OTel JS SDK) → Nginx reverse-proxy (
/v1/logs) → OpenTelemetry Collector → Loki → Grafana1. Architecture Overview
Current state
<script>block + externalutils.json every pagejs/page-setup.jsviatrackPlausible():content_section,nav_click,asset_download,external_clickConstraints
This is a static multi-page site (Gulp + jQuery). There is no JS bundler — Gulp only copies raw
.jsfiles todist/. To use OTel's npm packages in the browser, we need to add a minimal bundling step.2. Approach: Minimal esbuild Bundling Step
Add a single esbuild step to the Gulp pipeline that bundles only the OTel analytics module (
js/analytics/init.js) into a self-contained IIFE file (dist/js/otel-analytics.js). The rest of the build pipeline stays unchanged — all other JS files continue to be copied as-is.3. What to Instrument (User Analytics Events)
Automatic (from OTel browser instrumentations)
These come free from
@opentelemetry/browser-instrumentation:Custom Events (replaces existing Plausible events + new)
page_viewurl,page_type,referrer,user_agent,screen_resolutioncontent_sectionpage_type,content_sectiontrackPlausible("content_section")nav_clickpage_type,nav_link,content_sectiontrackPlausible("nav_click")asset_downloadpage_type,asset_type,asset_nametrackPlausible("asset_download")external_clickpage_type,external_linktrackPlausible("external_click")view_togglepage_type,view_mode,element_indexsession_startsession_id,timestampsession_heartbeatsession_id,duration_secondscopy_codepage_type,snippet_language4. Implementation Steps
Phase 1 — Add OTel dependencies and esbuild bundling step
esbuildas a dev dependencybundleAnalyticsGulp task that runs esbuild to bundlejs/analytics/init.js→dist/js/otel-analytics.jsas an IIFEbundleAnalyticsinto the existingbuildandwatchtasksPhase 2 — Create the analytics module (
js/analytics/init.js)js/analytics/init.js:LoggerProviderwithBatchLogRecordProcessor(prod) /ConsoleLogRecordExporter(dev, detected viawindow.location.hostname === 'localhost')OTLPLogExporter({ url: '/v1/logs' })— relative URL, proxied by Nginxservice.name = 'conversion-guide',service.versionfrompackage.jsonwindow.otelAnalytics.logEvent(eventName, attributes)andwindow.otelAnalytics.trackEvent(eventName, attributes)(with session ID + common attrs baked in)session_id(UUID) insessionStorageuser_agent,screen_resolution,referrersetIntervalevery 60s (skip ifdocument.hidden)session_startandpage_viewon initPhase 3 — Integrate into HTML pages and remove Plausible
<script src="js/otel-analytics.js"></script>to all 8 pages (before closing</body>, after jQuery but beforepage-setup.js)window.otelAnalytics.init()— or auto-init on script load<script>block (window.plausible = ...) from all pages<script defer src="https://common.ltc.bcit.ca/js/utils.js">tag from all pagesPhase 4 — Instrument existing jQuery event handlers in
page-setup.jsReplace all
trackPlausible()calls with OTel equivalents and remove thetrackPlausible()function:page-setup.jstrackPlausible("content_section", ...)(line ~118, ~407)window.otelAnalytics.trackEvent("content_section", ...)trackPlausible("nav_click", ...)(line ~400)window.otelAnalytics.trackEvent("nav_click", ...)trackPlausible("asset_download", ...)(line ~415)window.otelAnalytics.trackEvent("asset_download", ...)trackPlausible("external_click", ...)(line ~424)window.otelAnalytics.trackEvent("external_click", ...).preview-button,.word-button, etc.)window.otelAnalytics.trackEvent("view_toggle", ...)trackPlausible()function definition frompage-setup.jswindow.plausiblefrompage-setup.jsPhase 5 — Add Nginx reverse-proxy for
/v1/logsconf.d/default.conf:Phase 6 — Verify & test
npm run build— confirmdist/js/otel-analytics.jsis generatednpm run watch— confirm analytics loads in browser console (dev mode logs to console)dist/output/v1/logspath)5. File Changes Summary
js/analytics/init.jsgulpfile.mjsbundleAnalyticstask using esbuild; wire intobuildandwatchpackage.jsonpages/*.html(all 8)<script src="js/otel-analytics.js">tagjs/page-setup.jstrackPlausible()calls with OTeltrackEvent(); removetrackPlausible()function; addview_toggletrackingconf.d/default.conflocation = /v1/logsreverse proxy blockpages/*.html(all 8)utils.jsscript tag.gitignoredist/js/otel-analytics.jsif not already covered6. Risks & Considerations
node_modulesimports into a browser-ready IIFE. It only touches the analytics module — all other JS stays as-is.BatchLogRecordProcessoris fire-and-forget. If the collector is unreachable, logs silently drop — the site functions normally.session_startmust be deduplicated viasessionStorage(only emit if no existing session). The heartbeat timer resets on each page, which is acceptable.utils.jsscript load fromcommon.ltc.bcit.ca, reducing third-party dependencies.7. Suggested Implementation Order
js/analytics/init.js(Phase 2)/v1/logsproxy (Phase 5) — can be done in parallel<script>tags to all HTML pages (Phase 3)page-setup.jswith OTel calls (Phase 4)