@dash0/sdk-web
Version:
Dash0's Web SDK to collect telemetry from end-users' web browsers
73 lines (72 loc) • 2.85 kB
JavaScript
import { debug, nowNanos, win, wrap } from "../../utils";
import { vars } from "../../vars";
import { transmitPageViewEvent } from "./event";
let currentLocation = {};
let shouldIncludeHashChanges = false;
let shouldIncludeSearchChanges = false;
/**
* Tracks page transitions (virtual page views) as per this OTel spec:
* https://github.com/open-telemetry/semantic-conventions/pull/1910/files
*
* Notable difference: The full URL is transmitted as a signal attribute.
*/
export function startPageTransitionInstrumentation() {
if (!win || !win.history) {
debug("Browser does not support history API, skipping instrumentation");
return;
}
if (!vars.pageViewInstrumentation.trackVirtualPageViews) {
return;
}
shouldIncludeSearchChanges = vars.pageViewInstrumentation.includeParts?.includes("SEARCH") ?? false;
shouldIncludeHashChanges = vars.pageViewInstrumentation.includeParts?.includes("HASH") ?? false;
/**
* Not wrapping history.go, history.backward and history.forward here, because their call signatures don't receive
* url information. For these cases we can use the popstate and hashchange events.
*/
wrap(win.history, "replaceState", (original) => function (state, unused, url) {
onUrlChange(url ? String(url) : undefined, true);
return original.apply(this, [state, unused, url]);
});
wrap(win.history, "pushState", (original) => function (state, unused, url) {
onUrlChange(url ? String(url) : undefined);
return original.apply(this, [state, unused, url]);
});
win.addEventListener("hashchange", onHashChange);
win.addEventListener("popstate", onPopState);
try {
updateCurrentLocation(new URL(win.location.href));
}
catch { }
}
function onPopState() {
// popState event is fired after the location entry has been updated, so the location should already contain the new url.
onUrlChange(win?.location.href);
}
function onHashChange(event) {
onUrlChange(event.newURL);
}
function onUrlChange(url, replaced) {
if (!url)
return;
try {
const parsedUrl = new URL(url, win?.location.href);
if (isLocationChange(parsedUrl)) {
updateCurrentLocation(parsedUrl);
transmitPageViewEvent(nowNanos(), parsedUrl, true, Boolean(replaced));
}
}
catch (e) {
debug("Failed to handle url change", e);
}
}
function updateCurrentLocation(url) {
currentLocation.path = url.pathname;
currentLocation.search = url.search;
currentLocation.hash = url.hash;
}
function isLocationChange(newUrl) {
return (newUrl.pathname !== currentLocation.path ||
(shouldIncludeSearchChanges && newUrl.search !== currentLocation.search) ||
(shouldIncludeHashChanges && newUrl.hash !== currentLocation.hash));
}