@@ -19,6 +19,7 @@ import {
1919 addInpInstrumentationHandler ,
2020 addLcpInstrumentationHandler ,
2121 addPerformanceInstrumentationHandler ,
22+ addTtfbInstrumentationHandler ,
2223} from '../instrument' ;
2324import { WINDOW } from '../types' ;
2425import { getVisibilityWatcher } from '../web-vitals/lib/getVisibilityWatcher' ;
@@ -30,6 +31,8 @@ import type {
3031import { _startChild , isMeasurementValue } from './utils' ;
3132
3233import { createSpanEnvelope } from '@sentry/core' ;
34+ import { getNavigationEntry } from '../web-vitals/lib/getNavigationEntry' ;
35+ import type { TTFBMetric } from '../web-vitals/types/ttfb' ;
3336
3437const MAX_INT_AS_BYTES = 2147483647 ;
3538
@@ -68,11 +71,13 @@ export function startTrackingWebVitals(): () => void {
6871 const fidCallback = _trackFID ( ) ;
6972 const clsCallback = _trackCLS ( ) ;
7073 const lcpCallback = _trackLCP ( ) ;
74+ const ttfbCallback = _trackTtfb ( ) ;
7175
7276 return ( ) : void => {
7377 fidCallback ( ) ;
7478 clsCallback ( ) ;
7579 lcpCallback ( ) ;
80+ ttfbCallback ( ) ;
7681 } ;
7782 }
7883
@@ -201,6 +206,18 @@ function _trackFID(): () => void {
201206 } ) ;
202207}
203208
209+ function _trackTtfb ( ) : ( ) => void {
210+ return addTtfbInstrumentationHandler ( ( { metric } ) => {
211+ const entry = metric . entries [ metric . entries . length - 1 ] ;
212+ if ( ! entry ) {
213+ return ;
214+ }
215+
216+ DEBUG_BUILD && logger . log ( '[Measurements] Adding TTFB' ) ;
217+ _measurements [ 'ttfb' ] = { value : metric . value , unit : 'millisecond' } ;
218+ } ) ;
219+ }
220+
204221const INP_ENTRY_MAP : Record < string , 'click' | 'hover' | 'drag' | 'press' > = {
205222 click : 'click' ,
206223 pointerdown : 'click' ,
@@ -308,9 +325,6 @@ export function addPerformanceEntries(transaction: Transaction): void {
308325
309326 const performanceEntries = performance . getEntries ( ) ;
310327
311- let responseStartTimestamp : number | undefined ;
312- let requestStartTimestamp : number | undefined ;
313-
314328 const { op, start_timestamp : transactionStartTime } = spanToJSON ( transaction ) ;
315329
316330 // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -326,8 +340,6 @@ export function addPerformanceEntries(transaction: Transaction): void {
326340 switch ( entry . entryType ) {
327341 case 'navigation' : {
328342 _addNavigationSpans ( transaction , entry , timeOrigin ) ;
329- responseStartTimestamp = timeOrigin + msToSec ( entry . responseStart ) ;
330- requestStartTimestamp = timeOrigin + msToSec ( entry . requestStart ) ;
331343 break ;
332344 }
333345 case 'mark' :
@@ -365,7 +377,7 @@ export function addPerformanceEntries(transaction: Transaction): void {
365377
366378 // Measurements are only available for pageload transactions
367379 if ( op === 'pageload' ) {
368- _addTtfbToMeasurements ( _measurements , responseStartTimestamp , requestStartTimestamp , transactionStartTime ) ;
380+ _addTtfbRequestTimeToMeasurements ( _measurements ) ;
369381
370382 [ 'fcp' , 'fp' , 'lcp' ] . forEach ( name => {
371383 if ( ! _measurements [ name ] || ! transactionStartTime || timeOrigin >= transactionStartTime ) {
@@ -657,40 +669,20 @@ function setResourceEntrySizeData(
657669}
658670
659671/**
660- * Add ttfb information to measurements
672+ * Add ttfb request time information to measurements.
661673 *
662- * Exported for tests
674+ * ttfb information is added via vendored web vitals library.
663675 */
664- export function _addTtfbToMeasurements (
665- _measurements : Measurements ,
666- responseStartTimestamp : number | undefined ,
667- requestStartTimestamp : number | undefined ,
668- transactionStartTime : number | undefined ,
669- ) : void {
670- // Generate TTFB (Time to First Byte), which measured as the time between the beginning of the transaction and the
671- // start of the response in milliseconds
672- if ( typeof responseStartTimestamp === 'number' && transactionStartTime ) {
673- DEBUG_BUILD && logger . log ( '[Measurements] Adding TTFB' ) ;
674- _measurements [ 'ttfb' ] = {
675- // As per https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStart,
676- // responseStart can be 0 if the request is coming straight from the cache.
677- // This might lead us to calculate a negative ttfb if we don't use Math.max here.
678- //
679- // This logic is the same as what is in the web-vitals library to calculate ttfb
680- // https://github.com/GoogleChrome/web-vitals/blob/2301de5015e82b09925238a228a0893635854587/src/onTTFB.ts#L92
681- // TODO(abhi): We should use the web-vitals library instead of this custom calculation.
682- value : Math . max ( responseStartTimestamp - transactionStartTime , 0 ) * 1000 ,
676+ function _addTtfbRequestTimeToMeasurements ( _measurements : Measurements ) : void {
677+ const navEntry = getNavigationEntry ( ) as TTFBMetric [ 'entries' ] [ number ] ;
678+ const { responseStart, requestStart } = navEntry ;
679+
680+ if ( requestStart <= responseStart ) {
681+ DEBUG_BUILD && logger . log ( '[Measurements] Adding TTFB Request Time' ) ;
682+ _measurements [ 'ttfb.requestTime' ] = {
683+ value : responseStart - requestStart ,
683684 unit : 'millisecond' ,
684685 } ;
685-
686- if ( typeof requestStartTimestamp === 'number' && requestStartTimestamp <= responseStartTimestamp ) {
687- // Capture the time spent making the request and receiving the first byte of the response.
688- // This is the time between the start of the request and the start of the response in milliseconds.
689- _measurements [ 'ttfb.requestTime' ] = {
690- value : ( responseStartTimestamp - requestStartTimestamp ) * 1000 ,
691- unit : 'millisecond' ,
692- } ;
693- }
694686 }
695687}
696688
0 commit comments