Skip to content

Commit 9aa8bb5

Browse files
committed
feat: Report Web Vitals metrics on pageload transactions
Web Vitals is an initiative by Google to provide unified guidance for quality signals that are essential to delivering a great user experience on the web. https://web.dev/vitals/ Instead of a manual implementation targeting the performance API directly, this commit adds metrics by using the web-vitals library. Using web-vitals allows us to easily report on all supported metrics at the cost of ~1kb in the minified @sentry/apm bundle. Core Web Vitals - Cumulative Layout Shift (CLS) - First Input Delay (FID) - Largest Contentful Paint (LCP) Other Web Vitals - First Contentful Paint (FCP) - Time to First Byte (TTFB)
1 parent fc53353 commit 9aa8bb5

File tree

3 files changed

+91
-3
lines changed

3 files changed

+91
-3
lines changed

packages/apm/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"@sentry/minimal": "5.16.1",
2222
"@sentry/types": "5.16.1",
2323
"@sentry/utils": "5.16.1",
24-
"tslib": "^1.9.3"
24+
"tslib": "^1.9.3",
25+
"web-vitals": "^0.2.2"
2526
},
2627
"devDependencies": {
2728
"@types/express": "^4.17.1",

packages/apm/src/integrations/tracing.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
timestampWithMs,
1111
} from '@sentry/utils';
1212

13+
import { getCLS, getFID, getLCP, getFCP, getTTFB } from 'web-vitals';
14+
1315
import { Span as SpanClass } from '../span';
1416
import { SpanStatus } from '../spanstatus';
1517
import { Transaction } from '../transaction';
@@ -155,6 +157,9 @@ export class Tracing implements Integration {
155157

156158
private static _heartbeatCounter: number = 0;
157159

160+
/** Holds Web Vitals metrics. */
161+
private static _web_vitals: { [key: string]: { [key: string]: any } } = {};
162+
158163
/**
159164
* Constructor for Tracing
160165
*
@@ -163,6 +168,40 @@ export class Tracing implements Integration {
163168
public constructor(_options?: Partial<TracingOptions>) {
164169
if (global.performance) {
165170
global.performance.mark('sentry-tracing-init');
171+
172+
// Core Web Vitals
173+
getCLS(({ name, entries, value }) => {
174+
Tracing._web_vitals[name] = {
175+
value,
176+
};
177+
});
178+
getFID(({ name, entries, value }) => {
179+
Tracing._web_vitals[name] = {
180+
value,
181+
};
182+
});
183+
getLCP(({ name, entries, value }) => {
184+
const lastEntry = entries[entries.length - 1];
185+
Tracing._web_vitals[name] = {
186+
// @ts-ignore
187+
elementId: lastEntry.id,
188+
// @ts-ignore
189+
elementSize: lastEntry.size,
190+
value,
191+
};
192+
}, true /* reportAllChanges, necessary to be able to send the last seen LCP before the pageload transaction is sent */);
193+
194+
// Other Web Vitals
195+
getFCP(({ name, entries, value }) => {
196+
Tracing._web_vitals[name] = {
197+
value,
198+
};
199+
});
200+
getTTFB(({ name, entries, value }) => {
201+
Tracing._web_vitals[name] = {
202+
value,
203+
};
204+
});
166205
}
167206
const defaults = {
168207
debug: {
@@ -447,7 +486,7 @@ export class Tracing implements Integration {
447486
}
448487

449488
/**
450-
* Finshes the current active transaction
489+
* Finishes the current active transaction
451490
*/
452491
public static finishIdleTransaction(endTimestamp: number): void {
453492
const active = Tracing._activeTransaction;
@@ -505,6 +544,16 @@ export class Tracing implements Integration {
505544

506545
Tracing._log('[Tracing] Adding & adjusting spans using Performance API');
507546

547+
// FIXME: depending on the 'op' directly is brittle.
548+
if (transactionSpan.op === 'pageload') {
549+
if (Tracing._web_vitals) {
550+
// Add the last observed Web Vitals metrics to the transaction. Note
551+
// that some of the metrics may not yet be final, but we at the moment
552+
// do not wait for them to become final.
553+
transactionSpan.setData('_sentry_web_vitals', Tracing._web_vitals);
554+
}
555+
}
556+
508557
const timeOrigin = Tracing._msToSec(performance.timeOrigin);
509558

510559
// tslint:disable-next-line: completed-docs

yarn.lock

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1690,11 +1690,32 @@ [email protected]:
16901690
version "0.8.2"
16911691
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
16921692

1693-
agent-base@4, agent-base@5, agent-base@6, agent-base@^4.3.0, agent-base@~4.2.0:
1693+
agent-base@4, agent-base@^4.3.0:
1694+
version "4.3.0"
1695+
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
1696+
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
1697+
dependencies:
1698+
es6-promisify "^5.0.0"
1699+
1700+
agent-base@5:
16941701
version "5.1.1"
16951702
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c"
16961703
integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==
16971704

1705+
agent-base@6:
1706+
version "6.0.0"
1707+
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.0.tgz#5d0101f19bbfaed39980b22ae866de153b93f09a"
1708+
integrity sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==
1709+
dependencies:
1710+
debug "4"
1711+
1712+
agent-base@~4.2.0:
1713+
version "4.2.1"
1714+
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
1715+
integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==
1716+
dependencies:
1717+
es6-promisify "^5.0.0"
1718+
16981719
agentkeepalive@^3.4.1:
16991720
version "3.5.2"
17001721
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67"
@@ -4361,6 +4382,18 @@ es-to-primitive@^1.1.1, es-to-primitive@^1.2.0:
43614382
is-date-object "^1.0.1"
43624383
is-symbol "^1.0.2"
43634384

4385+
es6-promise@^4.0.3:
4386+
version "4.2.8"
4387+
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
4388+
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
4389+
4390+
es6-promisify@^5.0.0:
4391+
version "5.0.0"
4392+
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
4393+
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
4394+
dependencies:
4395+
es6-promise "^4.0.3"
4396+
43644397
escape-html@~1.0.3:
43654398
version "1.0.3"
43664399
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@@ -11367,6 +11400,11 @@ wcwidth@^1.0.0, wcwidth@^1.0.1:
1136711400
dependencies:
1136811401
defaults "^1.0.3"
1136911402

11403+
web-vitals@^0.2.2:
11404+
version "0.2.2"
11405+
resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-0.2.2.tgz#365d361590bf1a707c484d1196bd3c69e65523e0"
11406+
integrity sha512-6xR6kxa70XXnSHV4sZMDXKPvcrUfl2xaNUN1ENedcDbvcinzlWgaDD5Hn5mAnfHfKZVlSHe2/XCXKweuRnfWqw==
11407+
1137011408
webidl-conversions@^4.0.2:
1137111409
version "4.0.2"
1137211410
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"

0 commit comments

Comments
 (0)