From fe8ad6de0f38eacc896efd9275538f2583f7ecc7 Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 7 May 2021 16:40:51 -0400 Subject: [PATCH 1/4] stop tracking ttfb provided by web-vitals library --- packages/tracing/src/browser/metrics.ts | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index 1611c25472bb..d78a2a842e0a 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -9,7 +9,6 @@ import { msToSec } from '../utils'; import { getCLS } from './web-vitals/getCLS'; import { getFID } from './web-vitals/getFID'; import { getLCP, LargestContentfulPaint } from './web-vitals/getLCP'; -import { getTTFB } from './web-vitals/getTTFB'; import { getFirstHidden } from './web-vitals/lib/getFirstHidden'; import { NavigatorDeviceMemory, NavigatorNetworkInformation } from './web-vitals/types'; @@ -31,7 +30,6 @@ export class MetricsInstrumentation { this._trackCLS(); this._trackLCP(); this._trackFID(); - this._trackTTFB(); } } @@ -282,24 +280,6 @@ export class MetricsInstrumentation { this._measurements['mark.fid'] = { value: timeOrigin + startTime }; }); } - - /** Starts tracking the Time to First Byte on the current page. */ - private _trackTTFB(): void { - getTTFB(metric => { - const entry = metric.entries.pop(); - - if (!entry) { - return; - } - - logger.log('[Measurements] Adding TTFB'); - this._measurements['ttfb'] = { value: metric.value }; - - // Capture the time spent making the request and receiving the first byte of the response - const requestTime = metric.value - ((metric.entries[0] ?? entry) as PerformanceNavigationTiming).requestStart; - this._measurements['ttfb.requestTime'] = { value: requestTime }; - }); - } } /** Instrument navigation entries */ From 80bfd965c0101222ceb80700f652821e9a9d2fda Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 7 May 2021 16:41:17 -0400 Subject: [PATCH 2/4] track TTFB metrics using span/transaction information --- packages/tracing/src/browser/metrics.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index d78a2a842e0a..5710af403107 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -60,6 +60,8 @@ export class MetricsInstrumentation { let entryScriptStartTimestamp: number | undefined; let tracingInitMarkStartTime: number | undefined; + let responseStartTimestamp: number | undefined; + let requestStartTimestamp: number | undefined; global.performance .getEntries() @@ -73,9 +75,12 @@ export class MetricsInstrumentation { } switch (entry.entryType) { - case 'navigation': + case 'navigation': { addNavigationSpans(transaction, entry, timeOrigin); + responseStartTimestamp = timeOrigin + msToSec(entry.responseStart as number); + requestStartTimestamp = timeOrigin + msToSec(entry.requestStart as number); break; + } case 'mark': case 'paint': case 'measure': { @@ -137,7 +142,19 @@ export class MetricsInstrumentation { const timeOrigin = msToSec(browserPerformanceTimeOrigin); - ['fcp', 'fp', 'lcp', 'ttfb'].forEach(name => { + // Generate TTFB (Time to First Byte), which measured as the time between the beginning of the transaction and the + // start of the response in milliseconds + if (typeof responseStartTimestamp === 'number') { + this._measurements['ttfb'] = { value: (responseStartTimestamp - transaction.startTimestamp) * 1000 }; + + if (typeof requestStartTimestamp === 'number' && requestStartTimestamp <= responseStartTimestamp) { + // Capture the time spent making the request and receiving the first byte of the response. + // This is the time between the start of the request and the start of the response in milliseconds. + this._measurements['ttfb.requestTime'] = { value: (responseStartTimestamp - requestStartTimestamp) * 1000 }; + } + } + + ['fcp', 'fp', 'lcp'].forEach(name => { if (!this._measurements[name] || timeOrigin >= transaction.startTimestamp) { return; } From 133f9beeb470d2062a9c1fad808c593621e3ab92 Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 7 May 2021 16:54:58 -0400 Subject: [PATCH 3/4] delete getTTFB.ts --- .../tracing/src/browser/web-vitals/getTTFB.ts | 70 ------------------- 1 file changed, 70 deletions(-) delete mode 100644 packages/tracing/src/browser/web-vitals/getTTFB.ts diff --git a/packages/tracing/src/browser/web-vitals/getTTFB.ts b/packages/tracing/src/browser/web-vitals/getTTFB.ts deleted file mode 100644 index 63fda201caf5..000000000000 --- a/packages/tracing/src/browser/web-vitals/getTTFB.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { getGlobalObject } from '@sentry/utils'; - -import { initMetric } from './lib/initMetric'; -import { NavigationTimingPolyfillEntry, ReportHandler } from './types'; - -const global = getGlobalObject(); - -const afterLoad = (callback: () => void): void => { - if (document.readyState === 'complete') { - // Queue a task so the callback runs after `loadEventEnd`. - setTimeout(callback, 0); - } else { - // Use `pageshow` so the callback runs after `loadEventEnd`. - addEventListener('pageshow', callback); - } -}; - -const getNavigationEntryFromPerformanceTiming = (): NavigationTimingPolyfillEntry => { - // Really annoying that TypeScript errors when using `PerformanceTiming`. - // eslint-disable-next-line deprecation/deprecation - const timing = global.performance.timing; - - const navigationEntry: { [key: string]: number | string } = { - entryType: 'navigation', - startTime: 0, - }; - - for (const key in timing) { - if (key !== 'navigationStart' && key !== 'toJSON') { - navigationEntry[key] = Math.max((timing[key as keyof PerformanceTiming] as number) - timing.navigationStart, 0); - } - } - return navigationEntry as NavigationTimingPolyfillEntry; -}; - -export const getTTFB = (onReport: ReportHandler): void => { - const metric = initMetric('TTFB'); - - afterLoad(() => { - try { - // Use the NavigationTiming L2 entry if available. - const navigationEntry = - global.performance.getEntriesByType('navigation')[0] || getNavigationEntryFromPerformanceTiming(); - - metric.value = metric.delta = (navigationEntry as PerformanceNavigationTiming).responseStart; - - metric.entries = [navigationEntry]; - - onReport(metric); - } catch (error) { - // Do nothing. - } - }); -}; From 2ec9a880559a4f731ffa5cb79488ddf83a32fbc4 Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 7 May 2021 16:55:41 -0400 Subject: [PATCH 4/4] retain ttfb logging --- packages/tracing/src/browser/metrics.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index 5710af403107..9764101e7a04 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -145,6 +145,7 @@ export class MetricsInstrumentation { // Generate TTFB (Time to First Byte), which measured as the time between the beginning of the transaction and the // start of the response in milliseconds if (typeof responseStartTimestamp === 'number') { + logger.log('[Measurements] Adding TTFB'); this._measurements['ttfb'] = { value: (responseStartTimestamp - transaction.startTimestamp) * 1000 }; if (typeof requestStartTimestamp === 'number' && requestStartTimestamp <= responseStartTimestamp) {