From a63653ee8207e3fe80f09fb94215509be3cba15c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bazyli=20Brz=C3=B3ska?= Date: Tue, 4 Jun 2024 19:08:39 -0700 Subject: [PATCH] fix: improvements to getCommonUrlForTracing --- src/2024/operationTracking.ts | 4 +- src/main.ts | 2 +- src/v2/defaultEventProcessor.ts | 45 +++++++------- ...orTracing.ts => getCommonUrlForTracing.ts} | 62 ++++++++++++++++--- src/v2/operation.ts | 4 +- src/v2/types.ts | 4 +- 6 files changed, 85 insertions(+), 36 deletions(-) rename src/v2/{sanitizeUrlForTracing.ts => getCommonUrlForTracing.ts} (55%) diff --git a/src/2024/operationTracking.ts b/src/2024/operationTracking.ts index 593fdbf..3134eb0 100644 --- a/src/2024/operationTracking.ts +++ b/src/2024/operationTracking.ts @@ -2,7 +2,7 @@ import { type Subscription, Observable, share } from 'rxjs' import type { AnyPerformanceEntry, PerformanceEntryType } from './globalTypes' -import { sanitizeUrlForTracing } from '../v2/sanitizeUrlForTracing' +import { getCommonUrlForTracing } from '../v2/getCommonUrlForTracing' import type { Operation, OperationSpanMetadata, @@ -36,7 +36,7 @@ function extractEntryMetadata( switch (entry.entryType) { case 'resource': { - ;({ commonUrl: commonName, query } = sanitizeUrlForTracing(entry.name)) + ;({ commonUrl: commonName, query } = getCommonUrlForTracing(entry.name)) const resourceTiming = entry as PerformanceResourceTiming extraMetadata.initiatorType = resourceTiming.initiatorType extraMetadata.transferSize = resourceTiming.transferSize diff --git a/src/main.ts b/src/main.ts index 3839ea2..f8607ab 100644 --- a/src/main.ts +++ b/src/main.ts @@ -40,7 +40,7 @@ export { export * from './v2/constants' export * from './v2/defaultEventProcessor' export * from './v2/element' +export * from './v2/getCommonUrlForTracing' export * from './v2/hooks' export * from './v2/operation' -export * from './v2/sanitizeUrlForTracing' export type * from './v2/types' diff --git a/src/v2/defaultEventProcessor.ts b/src/v2/defaultEventProcessor.ts index 4f02a91..0829e40 100644 --- a/src/v2/defaultEventProcessor.ts +++ b/src/v2/defaultEventProcessor.ts @@ -1,4 +1,4 @@ -import { sanitizeUrlForTracing } from './sanitizeUrlForTracing' +import { getCommonUrlForTracing } from './getCommonUrlForTracing' import { type Event, type EventProcessor, @@ -25,30 +25,33 @@ export const defaultEventProcessor: EventProcessor = ( let commonName = entry.name let status: EventStatus = 'ok' - if (entry.entryType === 'resource') { - const { commonUrl, query } = sanitizeUrlForTracing(entry.name) + if (entry.entryType === 'resource' || entry.entryType === 'navigation') { + const { commonUrl, query, hash } = getCommonUrlForTracing(entry.name) commonName = commonUrl metadata.resourceQuery = query + metadata.resourceHash = hash - const resource = - metadata.resource && - typeof metadata.resource === 'object' && - metadata.resource - const resourceType = - resource && typeof resource.type === 'string' && resource.type - const statusCode = - resource && typeof resource.status === 'number' && resource.status + if (entry.entryType === 'resource') { + const resource = + metadata.resource && + typeof metadata.resource === 'object' && + metadata.resource + const resourceType = + resource && typeof resource.type === 'string' && resource.type + const statusCode = + resource && typeof resource.status === 'number' && resource.status - if (resourceType && resourceType !== 'xhr' && resourceType !== 'fetch') { - kind = 'asset' - } - // eslint-disable-next-line no-magic-numbers - if (statusCode && statusCode >= 400) { - status = 'error' - } - const resourceTiming = entry as PerformanceResourceTiming - if (resourceTiming.initiatorType === 'iframe') { - kind = 'iframe' + if (resourceType && resourceType !== 'xhr' && resourceType !== 'fetch') { + kind = 'asset' + } + // eslint-disable-next-line no-magic-numbers + if (statusCode && statusCode >= 400) { + status = 'error' + } + const resourceTiming = entry as PerformanceResourceTiming + if (resourceTiming.initiatorType === 'iframe') { + kind = 'iframe' + } } } else if (entry.entryType !== 'mark' && entry.entryType !== 'measure') { commonName = `${entry.entryType}${ diff --git a/src/v2/sanitizeUrlForTracing.ts b/src/v2/getCommonUrlForTracing.ts similarity index 55% rename from src/v2/sanitizeUrlForTracing.ts rename to src/v2/getCommonUrlForTracing.ts index 0e7d874..ccc1cb2 100644 --- a/src/v2/sanitizeUrlForTracing.ts +++ b/src/v2/getCommonUrlForTracing.ts @@ -1,11 +1,39 @@ -export function sanitizeUrlForTracing(url: string): { +const COMMON_EXTENSIONS = [ + '.html', + '.js', + '.css', + '.png', + '.jpg', + '.jpeg', + '.gif', + '.svg', + '.webp', + '.woff', + '.woff2', + '.ttf', + '.eot', + '.otf', + '.ico', +] + +export function getCommonUrlForTracing( + url: string, + commonExtensions = COMMON_EXTENSIONS, +): { commonUrl: string query: Record + hash: string } { + let commonUrl = url + let hash = '' + const hashIndex = url.indexOf('#') + if (hashIndex >= 0) { + commonUrl = url.slice(0, hashIndex) + hash = url.slice(hashIndex) + } // Extract query string into a separate variable const queryStringIndex = url.indexOf('?') const query: Record = {} - let commonUrl = url if (queryStringIndex >= 0) { // Split the URL to get the query string part commonUrl = url.slice(0, queryStringIndex) @@ -35,19 +63,35 @@ export function sanitizeUrlForTracing(url: string): { }) } - // Remove URL scheme - // const urlWithoutScheme = commonUrl.replace(/(^\w+:|^)\/\//, ''); - // Replace numeric parts of the ID with $ID - let sanitizedUrl = commonUrl.replace(/\/\d+/g, '/$id') - // replace UUIDs as well: - sanitizedUrl = sanitizedUrl.replace( + // if the URL ends with a common extension, replace file name with $file: + const urlParts = commonUrl.split('/') + const lastPart = urlParts.at(-1)! + const extensionIndex = lastPart.lastIndexOf('.') + const extension = + extensionIndex >= 0 ? lastPart.slice(extensionIndex) : undefined + if (extension && commonExtensions.includes(extension)) { + urlParts[urlParts.length - 1] = '$file' + commonUrl = urlParts.join('/') + } + + // replace UUIDs: + commonUrl = commonUrl.replace( // eslint-disable-next-line unicorn/better-regex /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g, '$uuid', ) + // replace 32-character or longer hex strings: + commonUrl = commonUrl.replace( + // eslint-disable-next-line unicorn/better-regex + /[0-9a-f]{32,}/g, + '$hex', + ) + // Replace numeric parts of the ID with $id + commonUrl = commonUrl.replace(/\d{2,}/g, '$d') return { - commonUrl: sanitizedUrl, + commonUrl, query, + hash, } } diff --git a/src/v2/operation.ts b/src/v2/operation.ts index 0b10574..431618d 100644 --- a/src/v2/operation.ts +++ b/src/v2/operation.ts @@ -611,7 +611,7 @@ export class Operation implements PerformanceEntryLike { name: this.name, startTime: this.startTime, duration: this.duration, - metadata: this.metadata, + metadata: { ...this.metadata }, event: { commonName: this.name, kind: OPERATION_ENTRY_TYPE, @@ -630,7 +630,7 @@ export class Operation implements PerformanceEntryLike { name: this.name, startTime: this.startTime, duration: this.durationTillInteractive, - metadata: this.metadata, + metadata: { ...this.metadata }, event: { commonName: this.name, kind: OPERATION_INTERACTIVE_ENTRY_TYPE, diff --git a/src/v2/types.ts b/src/v2/types.ts index ba5edcf..0738449 100644 --- a/src/v2/types.ts +++ b/src/v2/types.ts @@ -211,10 +211,12 @@ export interface Metadata { | 'DELETE' | 'PATCH' | 'TRACE' - | undefined + | 'OPTIONS' + | 'CONNECT' status?: number | undefined } resourceQuery?: Record + resourceHash?: string // renders add this metadata: visibleState?: VisibleStates | string