From dd6758a13b75c012c6128d820acb36b4a13a17ee Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Thu, 28 May 2020 14:11:44 +0200 Subject: [PATCH 01/11] feat: Report LCP metric on pageload transactions Largest Contentful Paint (LCP) is an user-centric metric for measuring perceived load speed. It marks the point in the page load timeline when the page's main content has likely loaded. Reference: https://web.dev/lcp/#measure-lcp-in-javascript --- packages/apm/src/integrations/tracing.ts | 79 +++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/packages/apm/src/integrations/tracing.ts b/packages/apm/src/integrations/tracing.ts index d30758e2fb98..f2d49a45f968 100644 --- a/packages/apm/src/integrations/tracing.ts +++ b/packages/apm/src/integrations/tracing.ts @@ -14,6 +14,73 @@ import { Span as SpanClass } from '../span'; import { SpanStatus } from '../spanstatus'; import { Transaction } from '../transaction'; +/** Holds the latest LargestContentfulPaint value (it changes during page load). */ +let lcp: { [key: string]: any }; + +/** Force any pending LargestContentfulPaint records to be dispatched. */ +let forceLCP = () => { + /* No-op, replaced later if LCP API is available. */ +}; + +// Based on reference implementation from https://web.dev/lcp/#measure-lcp-in-javascript. +{ + // Use a try/catch instead of feature detecting `largest-contentful-paint` + // support, since some browsers throw when using the new `type` option. + // https://bugs.webkit.org/show_bug.cgi?id=209216 + try { + // Keep track of whether (and when) the page was first hidden, see: + // https://github.com/w3c/page-visibility/issues/29 + // NOTE: ideally this check would be performed in the document + // to avoid cases where the visibility state changes before this code runs. + let firstHiddenTime = document.visibilityState === 'hidden' ? 0 : Infinity; + document.addEventListener( + 'visibilitychange', + event => { + firstHiddenTime = Math.min(firstHiddenTime, event.timeStamp); + }, + { once: true }, + ); + + const updateLCP = (entry: PerformanceEntry) => { + // Only include an LCP entry if the page wasn't hidden prior to + // the entry being dispatched. This typically happens when a page is + // loaded in a background tab. + if (entry.startTime < firstHiddenTime) { + // NOTE: the `startTime` value is a getter that returns the entry's + // `renderTime` value, if available, or its `loadTime` value otherwise. + // The `renderTime` value may not be available if the element is an image + // that's loaded cross-origin without the `Timing-Allow-Origin` header. + lcp = { + // @ts-ignore + elementId: entry.id, + // @ts-ignore + elementSize: entry.size, + largestContentfulPaint: entry.startTime, + }; + } + }; + + // Create a PerformanceObserver that calls `updateLCP` for each entry. + const po = new PerformanceObserver(entryList => { + entryList.getEntries().forEach(updateLCP); + }); + + // Observe entries of type `largest-contentful-paint`, including buffered entries, + // i.e. entries that occurred before calling `observe()` below. + po.observe({ + buffered: true, + // @ts-ignore + type: 'largest-contentful-paint', + }); + + forceLCP = () => { + po.takeRecords().forEach(updateLCP); + }; + } catch (e) { + // Do nothing if the browser doesn't support this API. + } +} + /** * Options for Tracing integration */ @@ -450,7 +517,7 @@ export class Tracing implements Integration { } /** - * Finshes the current active transaction + * Finishes the current active transaction */ public static finishIdleTransaction(endTimestamp: number): void { const active = Tracing._activeTransaction; @@ -508,6 +575,16 @@ export class Tracing implements Integration { Tracing._log('[Tracing] Adding & adjusting spans using Performance API'); + // FIXME: depending on the 'op' directly is brittle. + if (transactionSpan.op === 'pageload') { + // Force any pending records to be dispatched. + forceLCP(); + if (lcp) { + // Set the last observed LCP score. + transactionSpan.setData('_sentry_extra_metrics', JSON.stringify({ lcp })); + } + } + const timeOrigin = Tracing._msToSec(performance.timeOrigin); // tslint:disable-next-line: completed-docs From 91948bb392b7dc47b97f9df0dd7ad15b1359baa6 Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Thu, 4 Jun 2020 17:38:03 +0200 Subject: [PATCH 02/11] ref: Move LCP tracking to static side of Tracing This allows us to optionally disable LCP based on config later. --- packages/apm/src/integrations/tracing.ts | 145 ++++++++++++----------- 1 file changed, 75 insertions(+), 70 deletions(-) diff --git a/packages/apm/src/integrations/tracing.ts b/packages/apm/src/integrations/tracing.ts index f2d49a45f968..07b1a3c8fcdf 100644 --- a/packages/apm/src/integrations/tracing.ts +++ b/packages/apm/src/integrations/tracing.ts @@ -14,73 +14,6 @@ import { Span as SpanClass } from '../span'; import { SpanStatus } from '../spanstatus'; import { Transaction } from '../transaction'; -/** Holds the latest LargestContentfulPaint value (it changes during page load). */ -let lcp: { [key: string]: any }; - -/** Force any pending LargestContentfulPaint records to be dispatched. */ -let forceLCP = () => { - /* No-op, replaced later if LCP API is available. */ -}; - -// Based on reference implementation from https://web.dev/lcp/#measure-lcp-in-javascript. -{ - // Use a try/catch instead of feature detecting `largest-contentful-paint` - // support, since some browsers throw when using the new `type` option. - // https://bugs.webkit.org/show_bug.cgi?id=209216 - try { - // Keep track of whether (and when) the page was first hidden, see: - // https://github.com/w3c/page-visibility/issues/29 - // NOTE: ideally this check would be performed in the document - // to avoid cases where the visibility state changes before this code runs. - let firstHiddenTime = document.visibilityState === 'hidden' ? 0 : Infinity; - document.addEventListener( - 'visibilitychange', - event => { - firstHiddenTime = Math.min(firstHiddenTime, event.timeStamp); - }, - { once: true }, - ); - - const updateLCP = (entry: PerformanceEntry) => { - // Only include an LCP entry if the page wasn't hidden prior to - // the entry being dispatched. This typically happens when a page is - // loaded in a background tab. - if (entry.startTime < firstHiddenTime) { - // NOTE: the `startTime` value is a getter that returns the entry's - // `renderTime` value, if available, or its `loadTime` value otherwise. - // The `renderTime` value may not be available if the element is an image - // that's loaded cross-origin without the `Timing-Allow-Origin` header. - lcp = { - // @ts-ignore - elementId: entry.id, - // @ts-ignore - elementSize: entry.size, - largestContentfulPaint: entry.startTime, - }; - } - }; - - // Create a PerformanceObserver that calls `updateLCP` for each entry. - const po = new PerformanceObserver(entryList => { - entryList.getEntries().forEach(updateLCP); - }); - - // Observe entries of type `largest-contentful-paint`, including buffered entries, - // i.e. entries that occurred before calling `observe()` below. - po.observe({ - buffered: true, - // @ts-ignore - type: 'largest-contentful-paint', - }); - - forceLCP = () => { - po.takeRecords().forEach(updateLCP); - }; - } catch (e) { - // Do nothing if the browser doesn't support this API. - } -} - /** * Options for Tracing integration */ @@ -222,6 +155,14 @@ export class Tracing implements Integration { private static _heartbeatCounter: number = 0; + /** Holds the latest LargestContentfulPaint value (it changes during page load). */ + private static _lcp: { [key: string]: any }; + + /** Force any pending LargestContentfulPaint records to be dispatched. */ + private static _forceLCP = () => { + /* No-op, replaced later if LCP API is available. */ + }; + /** * Constructor for Tracing * @@ -230,6 +171,7 @@ export class Tracing implements Integration { public constructor(_options?: Partial) { if (global.performance) { global.performance.mark('sentry-tracing-init'); + Tracing._trackLCP(); } const defaults = { debug: { @@ -578,10 +520,10 @@ export class Tracing implements Integration { // FIXME: depending on the 'op' directly is brittle. if (transactionSpan.op === 'pageload') { // Force any pending records to be dispatched. - forceLCP(); - if (lcp) { + Tracing._forceLCP(); + if (Tracing._lcp) { // Set the last observed LCP score. - transactionSpan.setData('_sentry_extra_metrics', JSON.stringify({ lcp })); + transactionSpan.setData('_sentry_extra_metrics', JSON.stringify({ lcp: Tracing._lcp })); } } @@ -709,6 +651,69 @@ export class Tracing implements Integration { // tslint:enable: no-unsafe-any } + /** + * Starts tracking the Largest Contentful Paint on the current page. + */ + private static _trackLCP(): void { + // Based on reference implementation from https://web.dev/lcp/#measure-lcp-in-javascript. + + // Use a try/catch instead of feature detecting `largest-contentful-paint` + // support, since some browsers throw when using the new `type` option. + // https://bugs.webkit.org/show_bug.cgi?id=209216 + try { + // Keep track of whether (and when) the page was first hidden, see: + // https://github.com/w3c/page-visibility/issues/29 + // NOTE: ideally this check would be performed in the document + // to avoid cases where the visibility state changes before this code runs. + let firstHiddenTime = document.visibilityState === 'hidden' ? 0 : Infinity; + document.addEventListener( + 'visibilitychange', + event => { + firstHiddenTime = Math.min(firstHiddenTime, event.timeStamp); + }, + { once: true }, + ); + + const updateLCP = (entry: PerformanceEntry) => { + // Only include an LCP entry if the page wasn't hidden prior to + // the entry being dispatched. This typically happens when a page is + // loaded in a background tab. + if (entry.startTime < firstHiddenTime) { + // NOTE: the `startTime` value is a getter that returns the entry's + // `renderTime` value, if available, or its `loadTime` value otherwise. + // The `renderTime` value may not be available if the element is an image + // that's loaded cross-origin without the `Timing-Allow-Origin` header. + Tracing._lcp = { + // @ts-ignore + elementId: entry.id, + // @ts-ignore + elementSize: entry.size, + largestContentfulPaint: entry.startTime, + }; + } + }; + + // Create a PerformanceObserver that calls `updateLCP` for each entry. + const po = new PerformanceObserver(entryList => { + entryList.getEntries().forEach(updateLCP); + }); + + // Observe entries of type `largest-contentful-paint`, including buffered entries, + // i.e. entries that occurred before calling `observe()` below. + po.observe({ + buffered: true, + // @ts-ignore + type: 'largest-contentful-paint', + }); + + Tracing._forceLCP = () => { + po.takeRecords().forEach(updateLCP); + }; + } catch (e) { + // Do nothing if the browser doesn't support this API. + } + } + /** * Sets the status of the current active transaction (if there is one) */ From 64ab997e494404d5950af41d149fe76391852eb3 Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Thu, 4 Jun 2020 17:39:35 +0200 Subject: [PATCH 03/11] feat: Increase default normalizeDepth This allows for proper serialization of objects as values in context.trace.data. --- packages/apm/src/integrations/tracing.ts | 2 +- packages/core/src/baseclient.ts | 2 +- packages/types/src/options.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/apm/src/integrations/tracing.ts b/packages/apm/src/integrations/tracing.ts index 07b1a3c8fcdf..9bf769af7b2b 100644 --- a/packages/apm/src/integrations/tracing.ts +++ b/packages/apm/src/integrations/tracing.ts @@ -523,7 +523,7 @@ export class Tracing implements Integration { Tracing._forceLCP(); if (Tracing._lcp) { // Set the last observed LCP score. - transactionSpan.setData('_sentry_extra_metrics', JSON.stringify({ lcp: Tracing._lcp })); + transactionSpan.setData('_sentry_extra_metrics', { lcp: Tracing._lcp }); } } diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 9dd23ec7213c..58fbde44bdbf 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -248,7 +248,7 @@ export abstract class BaseClient implement * @returns A new event with more information. */ protected _prepareEvent(event: Event, scope?: Scope, hint?: EventHint): PromiseLike { - const { normalizeDepth = 3 } = this.getOptions(); + const { normalizeDepth } = this.getOptions(); const prepared: Event = { ...event, event_id: event.event_id || (hint && hint.event_id ? hint.event_id : uuid4()), diff --git a/packages/types/src/options.ts b/packages/types/src/options.ts index fd23a2904582..1cc5d922c59f 100644 --- a/packages/types/src/options.ts +++ b/packages/types/src/options.ts @@ -101,9 +101,9 @@ export interface Options { * - `user` * - `contexts` * - `extra` - * Defaults to `3`. Set to `0` to disable. + * Defaults to `4`. Set to `0` to disable. */ - normalizeDepth?: number; + normalizeDepth?: number = 4; /** * A callback invoked during event submission, allowing to optionally modify From ec371dbdf55ef0253b3dd0fcacbd243d6836a414 Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Thu, 4 Jun 2020 18:48:16 +0200 Subject: [PATCH 04/11] Revert "feat: Increase default normalizeDepth" This reverts commit 19cdf22f2316f5b22045b62009de2687b2f1e8c3. This is too aggressive, would increase the side of breadcrumbs --- packages/apm/src/integrations/tracing.ts | 2 +- packages/core/src/baseclient.ts | 2 +- packages/types/src/options.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/apm/src/integrations/tracing.ts b/packages/apm/src/integrations/tracing.ts index 9bf769af7b2b..07b1a3c8fcdf 100644 --- a/packages/apm/src/integrations/tracing.ts +++ b/packages/apm/src/integrations/tracing.ts @@ -523,7 +523,7 @@ export class Tracing implements Integration { Tracing._forceLCP(); if (Tracing._lcp) { // Set the last observed LCP score. - transactionSpan.setData('_sentry_extra_metrics', { lcp: Tracing._lcp }); + transactionSpan.setData('_sentry_extra_metrics', JSON.stringify({ lcp: Tracing._lcp })); } } diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 58fbde44bdbf..9dd23ec7213c 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -248,7 +248,7 @@ export abstract class BaseClient implement * @returns A new event with more information. */ protected _prepareEvent(event: Event, scope?: Scope, hint?: EventHint): PromiseLike { - const { normalizeDepth } = this.getOptions(); + const { normalizeDepth = 3 } = this.getOptions(); const prepared: Event = { ...event, event_id: event.event_id || (hint && hint.event_id ? hint.event_id : uuid4()), diff --git a/packages/types/src/options.ts b/packages/types/src/options.ts index 1cc5d922c59f..fd23a2904582 100644 --- a/packages/types/src/options.ts +++ b/packages/types/src/options.ts @@ -101,9 +101,9 @@ export interface Options { * - `user` * - `contexts` * - `extra` - * Defaults to `4`. Set to `0` to disable. + * Defaults to `3`. Set to `0` to disable. */ - normalizeDepth?: number = 4; + normalizeDepth?: number; /** * A callback invoked during event submission, allowing to optionally modify From 19a6723694664f61f0b4bd8b75a3c331765f1b19 Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Thu, 4 Jun 2020 19:07:19 +0200 Subject: [PATCH 05/11] feat: Remove normalization of contexts All of the context types documented in [1] are flat objects. The new contexts.trace is the only key under contexts that may have nested objects under contexts.trace.data. That value holds the data for the Transaction Span and matches spans[].data for invidual Spans. At the moment one of the data fields is heavily limited, while the other is ignored by normalization. [1]: https://develop.sentry.dev/sdk/event-payloads/contexts/ This is the simplest to implement fix to #2646, thus what I'm doing here perhaps temporarily. Fixes #2646 --- packages/apm/src/integrations/tracing.ts | 2 +- packages/core/src/baseclient.ts | 4 ---- packages/core/test/lib/base.test.ts | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/apm/src/integrations/tracing.ts b/packages/apm/src/integrations/tracing.ts index 07b1a3c8fcdf..9bf769af7b2b 100644 --- a/packages/apm/src/integrations/tracing.ts +++ b/packages/apm/src/integrations/tracing.ts @@ -523,7 +523,7 @@ export class Tracing implements Integration { Tracing._forceLCP(); if (Tracing._lcp) { // Set the last observed LCP score. - transactionSpan.setData('_sentry_extra_metrics', JSON.stringify({ lcp: Tracing._lcp })); + transactionSpan.setData('_sentry_extra_metrics', { lcp: Tracing._lcp }); } } diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 9dd23ec7213c..eb4f87582909 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -289,7 +289,6 @@ export abstract class BaseClient implement * Normalized keys: * - `breadcrumbs.data` * - `user` - * - `contexts` * - `extra` * @param event Event * @returns Normalized event @@ -313,9 +312,6 @@ export abstract class BaseClient implement ...(event.user && { user: normalize(event.user, depth), }), - ...(event.contexts && { - contexts: normalize(event.contexts, depth), - }), ...(event.extra && { extra: normalize(event.extra, depth), }), diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index dfc85dbf987d..1b02701b61d4 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -467,7 +467,7 @@ describe('BaseClient', () => { }); expect(TestBackend.instance!.event!).toEqual({ breadcrumbs: [normalizedBreadcrumb, normalizedBreadcrumb, normalizedBreadcrumb], - contexts: normalizedObject, + contexts: fourLevelsObject, event_id: '42', extra: normalizedObject, timestamp: 2020, @@ -512,7 +512,7 @@ describe('BaseClient', () => { }); expect(TestBackend.instance!.event!).toEqual({ breadcrumbs: [normalizedBreadcrumb, normalizedBreadcrumb, normalizedBreadcrumb], - contexts: normalizedObject, + contexts: fourLevelsObject, event_id: '42', extra: normalizedObject, timestamp: 2020, From eace0c2c7c1ba4f389135010856cc96382be977b Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Thu, 4 Jun 2020 20:06:39 +0200 Subject: [PATCH 06/11] misc: CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff276499f8d1..62171ae9ca53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - [react] feat: Add @sentry/react package (#2631) - [browser] Change XHR instrumentation order to handle `onreadystatechange` breadcrumbs correctly (#2643) - [apm] fix: Re-add TraceContext for all events (#2656) +- [browser] feat: Report LCP metric on pageload transactions (#2624) ## 5.16.1 From 9d114cc0732d3cf8d34bbe2d8eda5402f2efd5dc Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Fri, 5 Jun 2020 17:56:58 +0200 Subject: [PATCH 07/11] Revert "feat: Remove normalization of contexts" This reverts commit 88c210c0f21cff2b28df6405df4a957bb237f776. --- packages/apm/src/integrations/tracing.ts | 2 +- packages/core/src/baseclient.ts | 4 ++++ packages/core/test/lib/base.test.ts | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/apm/src/integrations/tracing.ts b/packages/apm/src/integrations/tracing.ts index 9bf769af7b2b..07b1a3c8fcdf 100644 --- a/packages/apm/src/integrations/tracing.ts +++ b/packages/apm/src/integrations/tracing.ts @@ -523,7 +523,7 @@ export class Tracing implements Integration { Tracing._forceLCP(); if (Tracing._lcp) { // Set the last observed LCP score. - transactionSpan.setData('_sentry_extra_metrics', { lcp: Tracing._lcp }); + transactionSpan.setData('_sentry_extra_metrics', JSON.stringify({ lcp: Tracing._lcp })); } } diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index eb4f87582909..9dd23ec7213c 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -289,6 +289,7 @@ export abstract class BaseClient implement * Normalized keys: * - `breadcrumbs.data` * - `user` + * - `contexts` * - `extra` * @param event Event * @returns Normalized event @@ -312,6 +313,9 @@ export abstract class BaseClient implement ...(event.user && { user: normalize(event.user, depth), }), + ...(event.contexts && { + contexts: normalize(event.contexts, depth), + }), ...(event.extra && { extra: normalize(event.extra, depth), }), diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 1b02701b61d4..dfc85dbf987d 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -467,7 +467,7 @@ describe('BaseClient', () => { }); expect(TestBackend.instance!.event!).toEqual({ breadcrumbs: [normalizedBreadcrumb, normalizedBreadcrumb, normalizedBreadcrumb], - contexts: fourLevelsObject, + contexts: normalizedObject, event_id: '42', extra: normalizedObject, timestamp: 2020, @@ -512,7 +512,7 @@ describe('BaseClient', () => { }); expect(TestBackend.instance!.event!).toEqual({ breadcrumbs: [normalizedBreadcrumb, normalizedBreadcrumb, normalizedBreadcrumb], - contexts: fourLevelsObject, + contexts: normalizedObject, event_id: '42', extra: normalizedObject, timestamp: 2020, From 66d2329d120917b284539bc58c1e5f80aded578f Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Fri, 5 Jun 2020 18:07:28 +0200 Subject: [PATCH 08/11] misc: Update type definition Tracing._lcp can be undefined if no LCP is ever reported. --- packages/apm/src/integrations/tracing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apm/src/integrations/tracing.ts b/packages/apm/src/integrations/tracing.ts index 07b1a3c8fcdf..d19b9f9c2ed2 100644 --- a/packages/apm/src/integrations/tracing.ts +++ b/packages/apm/src/integrations/tracing.ts @@ -156,7 +156,7 @@ export class Tracing implements Integration { private static _heartbeatCounter: number = 0; /** Holds the latest LargestContentfulPaint value (it changes during page load). */ - private static _lcp: { [key: string]: any }; + private static _lcp?: { [key: string]: any }; /** Force any pending LargestContentfulPaint records to be dispatched. */ private static _forceLCP = () => { From 3f9e9215842437e9921a68d42bf8c273818681af Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Fri, 5 Jun 2020 18:09:13 +0200 Subject: [PATCH 09/11] misc: Rename keys leading up to LCP metric The idea is to align with the name used in web-vitals (LCP in uppercase, not lcp) and to constrain the custom container in data to metrics related to Web Vitals. Do not double encode as JSON, assume that normalization is solved elsewhere. --- packages/apm/src/integrations/tracing.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/apm/src/integrations/tracing.ts b/packages/apm/src/integrations/tracing.ts index d19b9f9c2ed2..a3e6b3484fd1 100644 --- a/packages/apm/src/integrations/tracing.ts +++ b/packages/apm/src/integrations/tracing.ts @@ -523,7 +523,7 @@ export class Tracing implements Integration { Tracing._forceLCP(); if (Tracing._lcp) { // Set the last observed LCP score. - transactionSpan.setData('_sentry_extra_metrics', JSON.stringify({ lcp: Tracing._lcp })); + transactionSpan.setData('_sentry_web_vitals', { LCP: Tracing._lcp }); } } @@ -688,7 +688,7 @@ export class Tracing implements Integration { elementId: entry.id, // @ts-ignore elementSize: entry.size, - largestContentfulPaint: entry.startTime, + value: entry.startTime, }; } }; From 0fd278d3ed3cbfe9fea03b416e76ff712ea8607c Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Mon, 8 Jun 2020 12:00:46 +0200 Subject: [PATCH 10/11] fix: Include elementId and elementSize only when available --- packages/apm/src/integrations/tracing.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/apm/src/integrations/tracing.ts b/packages/apm/src/integrations/tracing.ts index a3e6b3484fd1..a6ce61fe4192 100644 --- a/packages/apm/src/integrations/tracing.ts +++ b/packages/apm/src/integrations/tracing.ts @@ -685,9 +685,9 @@ export class Tracing implements Integration { // that's loaded cross-origin without the `Timing-Allow-Origin` header. Tracing._lcp = { // @ts-ignore - elementId: entry.id, + ...(entry.id && { elementId: entry.id }), // @ts-ignore - elementSize: entry.size, + ...(entry.size && { elementSize: entry.size }), value: entry.startTime, }; } From c98d92410ed292a9c819c6f121b05437858202da Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Mon, 8 Jun 2020 14:06:05 +0200 Subject: [PATCH 11/11] Update CHANGELOG.md Co-authored-by: Daniel Griesser --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62171ae9ca53..f16702869225 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - [react] feat: Add @sentry/react package (#2631) - [browser] Change XHR instrumentation order to handle `onreadystatechange` breadcrumbs correctly (#2643) - [apm] fix: Re-add TraceContext for all events (#2656) -- [browser] feat: Report LCP metric on pageload transactions (#2624) +- [apm] feat: Report LCP metric on pageload transactions (#2624) ## 5.16.1