-
Notifications
You must be signed in to change notification settings - Fork 1
EMBR-10483: Switch to a buffered POST beacon for JS errors #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ee33a18
e96123c
717485a
b59394c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| { | ||
| "name": "@speedcurve/lux", | ||
| "version": "4.4.3", | ||
| "version": "4.5.0", | ||
| "config": { | ||
| "snippetVersion": "2.0.0" | ||
| }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| import type { ConfigObject } from "./config"; | ||
| import { postJson } from "./transport"; | ||
|
|
||
| type ErrorBeaconContext = { | ||
| customerId: string; | ||
| pageId: string; | ||
| sessionId: string; | ||
| scriptVersion: string; | ||
| hostname: string; | ||
| pathname: string; | ||
| pageLabel: string; | ||
| connectionType: string | undefined; | ||
| deliveryType: string | undefined; | ||
| navigationType: number | undefined; | ||
| deviceMemory: number | undefined; | ||
| flags: number; | ||
| customData: Record<string, unknown>; | ||
| }; | ||
|
|
||
| type BufferedError = { | ||
| errorTime: number; | ||
| filename: string; | ||
| lineno: number; | ||
| colno: number; | ||
| message: string; | ||
| }; | ||
|
|
||
| // This is the same value as MAX_ERRORS_PER_BEACON in speedcurve-rum-beacons-edge-service | ||
| const MAX_ERRORS_PER_BEACON = 64; | ||
|
|
||
| let buffer: BufferedError[] = []; | ||
| let pendingContext: ErrorBeaconContext | null = null; | ||
| let pendingConfig: ConfigObject | null = null; | ||
| let flushTimer: ReturnType<typeof setTimeout> | null = null; | ||
|
|
||
| export function queueErrorBeacon( | ||
| config: ConfigObject, | ||
| error: ErrorEvent, | ||
| errorTime: number, | ||
| context: ErrorBeaconContext, | ||
| ): void { | ||
| // If the page ID has changed since the last error (e.g. due to a SPA navigation), or if the | ||
| // buffer is full, flush the current beacon before adding the new error. | ||
| const pageChanged = pendingContext && pendingContext.pageId !== context.pageId; | ||
| const bufferFull = buffer.length >= MAX_ERRORS_PER_BEACON; | ||
|
|
||
| if (pageChanged || bufferFull) { | ||
| flushErrorBeacon(); | ||
| } | ||
|
|
||
| pendingContext = context; | ||
| pendingConfig = config; | ||
| buffer.push({ | ||
| errorTime, | ||
| filename: error.filename, | ||
| lineno: error.lineno, | ||
| colno: error.colno, | ||
| message: error.message, | ||
| }); | ||
|
|
||
| if (flushTimer === null) { | ||
| flushTimer = setTimeout(flushErrorBeacon, config.errorBeaconDelay); | ||
| } | ||
| } | ||
|
|
||
| function flushErrorBeacon(): void { | ||
| if (flushTimer !== null) { | ||
| clearTimeout(flushTimer); | ||
| flushTimer = null; | ||
| } | ||
|
|
||
| if (buffer.length === 0 || !pendingContext || !pendingConfig) { | ||
| return; | ||
| } | ||
|
|
||
| postJson( | ||
| pendingConfig.errorBeaconUrl, | ||
| JSON.stringify(Object.assign({}, pendingContext, { errors: buffer })), | ||
| ); | ||
|
|
||
| buffer = []; | ||
| pendingContext = null; | ||
| pendingConfig = null; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,12 +16,12 @@ export const timing = performance.timing || { | |
| // Older PerformanceTiming implementations allow for arbitrary keys to exist on the timing object | ||
| export type PerfTimingKey = keyof Omit<PerformanceTiming, "toJSON">; | ||
|
|
||
| export function navigationType() { | ||
| export function navigationType(): number | undefined { | ||
| if (performance.navigation && typeof performance.navigation.type !== "undefined") { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably a separate PR but just noting this has been deprecated: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigation/type
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jpmunz yeah there's quite a bit of old code in this SDK. Some of it is intentional to support old browsers, but I am definitely leaning towards just removing it these days. I don't think anyone will mind if IE11 doesn't have a navigation type :) |
||
| return performance.navigation.type; | ||
| } | ||
|
|
||
| return ""; | ||
| return undefined; | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| const sendBeaconFallback = (url: string, data: string) => { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extracted this from beacon.ts since it's now shared between two beacons. |
||
| const xhr = new XMLHttpRequest(); | ||
| xhr.open("POST", url, true); | ||
| xhr.setRequestHeader("content-type", "application/json"); | ||
| xhr.send(data); | ||
|
|
||
| return true; | ||
| }; | ||
|
|
||
| const sendBeaconImpl = | ||
| "sendBeacon" in navigator ? navigator.sendBeacon.bind(navigator) : sendBeaconFallback; | ||
|
|
||
| export function postJson(url: string, data: string): boolean { | ||
| return sendBeaconImpl(url, data); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Originally the only change here was an explicit return type, but I felt the need to refactor the function body as well.
navigator.connectionis only everslow-2g,2g,3g, or4g. The SDK turns these into "friendly" values by upper-casing them, with the exception being "Slow 2G". The previous logic was overly complicated for such a simple transform.