From 5e6c48581ecb003c7eb7bafcd8a10388591695b3 Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 9 Oct 2020 16:04:22 -0400 Subject: [PATCH 01/13] feat(rum): Capture browser navigator information --- packages/tracing/src/browser/metrics.ts | 46 +++++++++++++++++++ .../tracing/src/browser/web-vitals/types.ts | 39 ++++++++++++++++ packages/tracing/src/transaction.ts | 11 ++++- 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index bcef09afc79e..58ace69dd28f 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -11,12 +11,23 @@ import { getFID } from './web-vitals/getFID'; import { getLCP } from './web-vitals/getLCP'; import { getTTFB } from './web-vitals/getTTFB'; import { getFirstHidden } from './web-vitals/lib/getFirstHidden'; +import { NavigatorDeviceMemory, NavigatorNetworkInformation } from './web-vitals/types'; const global = getGlobalObject(); +type FooNavigator = Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory; + +type BrowserContext = { + effectiveConnectionType?: string; + deviceMemory?: number; + // number of CPUs + hardwareConcurrency?: number; +}; + /** Class tracking metrics */ export class MetricsInstrumentation { private _measurements: Measurements = {}; + private _browserContext: BrowserContext = {}; private _performanceCursor: number = 0; @@ -30,6 +41,7 @@ export class MetricsInstrumentation { this._trackLCP(); this._trackFID(); this._trackTTFB(); + this._trackNavigator(); } } @@ -131,6 +143,8 @@ export class MetricsInstrumentation { // Measurements are only available for pageload transactions if (transaction.op === 'pageload') { + this._trackNavigator(); + transaction.setContexts({ browser: this._browserContext }); transaction.setMeasurements(this._measurements); } } @@ -149,6 +163,38 @@ export class MetricsInstrumentation { }); } + /** + * Capture the information of the user agent. + */ + private _trackNavigator(): void { + const navigator = window.navigator as null | FooNavigator; + + // track network connectivity + + const connection = navigator?.connection; + if (connection) { + if (connection.effectiveType) { + this._browserContext.effectiveConnectionType = connection.effectiveType; + } + + if (typeof connection.rtt === 'number') { + this._measurements['connection.rtt'] = { value: connection.rtt }; + } + + if (typeof connection.downlink === 'number') { + this._measurements['connection.downlink'] = { value: connection.downlink }; + } + } + + if (typeof navigator?.deviceMemory === 'number') { + this._browserContext.deviceMemory = navigator.deviceMemory; + } + + if (typeof navigator?.hardwareConcurrency === 'number') { + this._browserContext.hardwareConcurrency = navigator.hardwareConcurrency; + } + } + /** Starts tracking the Largest Contentful Paint on the current page. */ private _trackLCP(): void { getLCP(metric => { diff --git a/packages/tracing/src/browser/web-vitals/types.ts b/packages/tracing/src/browser/web-vitals/types.ts index d0973c54e4a3..cd80b29f89a5 100644 --- a/packages/tracing/src/browser/web-vitals/types.ts +++ b/packages/tracing/src/browser/web-vitals/types.ts @@ -43,3 +43,42 @@ export interface Metric { export interface ReportHandler { (metric: Metric): void; } + +// http://wicg.github.io/netinfo/#navigatornetworkinformation-interface +export interface NavigatorNetworkInformation { + readonly connection?: NetworkInformation; +} + +// http://wicg.github.io/netinfo/#connection-types +type ConnectionType = 'bluetooth' | 'cellular' | 'ethernet' | 'mixed' | 'none' | 'other' | 'unknown' | 'wifi' | 'wimax'; + +// http://wicg.github.io/netinfo/#effectiveconnectiontype-enum +type EffectiveConnectionType = '2g' | '3g' | '4g' | 'slow-2g'; + +// http://wicg.github.io/netinfo/#dom-megabit +type Megabit = number; +// http://wicg.github.io/netinfo/#dom-millisecond +type Millisecond = number; + +// http://wicg.github.io/netinfo/#networkinformation-interface +interface NetworkInformation extends EventTarget { + // http://wicg.github.io/netinfo/#type-attribute + readonly type?: ConnectionType; + // http://wicg.github.io/netinfo/#effectivetype-attribute + readonly effectiveType?: EffectiveConnectionType; + // http://wicg.github.io/netinfo/#downlinkmax-attribute + readonly downlinkMax?: Megabit; + // http://wicg.github.io/netinfo/#downlink-attribute + readonly downlink?: Megabit; + // http://wicg.github.io/netinfo/#rtt-attribute + readonly rtt?: Millisecond; + // http://wicg.github.io/netinfo/#savedata-attribute + readonly saveData?: boolean; + // http://wicg.github.io/netinfo/#handling-changes-to-the-underlying-connection + onchange?: EventListener; +} + +// https://w3c.github.io/device-memory/#sec-device-memory-js-api +export interface NavigatorDeviceMemory { + readonly deviceMemory?: number; +} diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 7f51f84462d7..15b9c779944f 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -1,5 +1,5 @@ import { getCurrentHub, Hub } from '@sentry/hub'; -import { Event, Measurements, Transaction as TransactionInterface, TransactionContext } from '@sentry/types'; +import { Contexts, Event, Measurements, Transaction as TransactionInterface, TransactionContext } from '@sentry/types'; import { isInstanceOf, logger } from '@sentry/utils'; import { Span as SpanClass, SpanRecorder } from './span'; @@ -8,6 +8,7 @@ import { Span as SpanClass, SpanRecorder } from './span'; export class Transaction extends SpanClass implements TransactionInterface { public name: string; private _measurements: Measurements = {}; + private _contexts: Contexts = {}; /** * The reference to the current hub. @@ -64,6 +65,13 @@ export class Transaction extends SpanClass implements TransactionInterface { this._measurements = { ...measurements }; } + /** + * + */ + public setContexts(contexts: Contexts): void { + this._contexts = contexts; + } + /** * @inheritDoc */ @@ -100,6 +108,7 @@ export class Transaction extends SpanClass implements TransactionInterface { const transaction: Event = { contexts: { + ...this._contexts, trace: this.getTraceContext(), }, spans: finishedSpans, From f50ed63aae189292a9cf0c566cc4e0534046404e Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 9 Oct 2020 16:21:18 -0400 Subject: [PATCH 02/13] remove FooNavigator --- packages/tracing/src/browser/metrics.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index 58ace69dd28f..a8915da60064 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -15,8 +15,6 @@ import { NavigatorDeviceMemory, NavigatorNetworkInformation } from './web-vitals const global = getGlobalObject(); -type FooNavigator = Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory; - type BrowserContext = { effectiveConnectionType?: string; deviceMemory?: number; @@ -167,7 +165,7 @@ export class MetricsInstrumentation { * Capture the information of the user agent. */ private _trackNavigator(): void { - const navigator = window.navigator as null | FooNavigator; + const navigator = window.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory); // track network connectivity From 22e13c0b10b08f251ba1f55cc0950d302c7212a3 Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 16 Oct 2020 17:29:30 -0400 Subject: [PATCH 03/13] use global instead --- packages/tracing/src/browser/metrics.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index a8915da60064..c23f66a2759e 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -165,11 +165,15 @@ export class MetricsInstrumentation { * Capture the information of the user agent. */ private _trackNavigator(): void { - const navigator = window.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory); + const navigator = global.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory); + + if(!navigator) { + return; + } // track network connectivity - const connection = navigator?.connection; + const connection = navigator.connection; if (connection) { if (connection.effectiveType) { this._browserContext.effectiveConnectionType = connection.effectiveType; From 978aba5f397d31ebb8c11ba43715bccffe665c6e Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 16 Oct 2020 17:30:16 -0400 Subject: [PATCH 04/13] dont track navigator in the beginning --- packages/tracing/src/browser/metrics.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index c23f66a2759e..b96361407919 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -39,7 +39,6 @@ export class MetricsInstrumentation { this._trackLCP(); this._trackFID(); this._trackTTFB(); - this._trackNavigator(); } } From 93061c306f1c98b52c0c5dfcb80f449457adda77 Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 16 Oct 2020 17:47:25 -0400 Subject: [PATCH 05/13] capture navigation for all transactions --- packages/tracing/src/browser/metrics.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index b96361407919..dbd703f03d94 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -138,9 +138,10 @@ export class MetricsInstrumentation { this._performanceCursor = Math.max(performance.getEntries().length - 1, 0); + this._trackNavigator(transaction); + // Measurements are only available for pageload transactions if (transaction.op === 'pageload') { - this._trackNavigator(); transaction.setContexts({ browser: this._browserContext }); transaction.setMeasurements(this._measurements); } @@ -163,7 +164,7 @@ export class MetricsInstrumentation { /** * Capture the information of the user agent. */ - private _trackNavigator(): void { + private _trackNavigator(transaction: Transaction): void { const navigator = global.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory); if(!navigator) { From d9b2a63dea5ed1ecfb67eb733110cdea1058b46a Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 16 Oct 2020 17:47:37 -0400 Subject: [PATCH 06/13] set tags --- packages/tracing/src/browser/metrics.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index dbd703f03d94..596db40842d3 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -181,19 +181,23 @@ export class MetricsInstrumentation { if (typeof connection.rtt === 'number') { this._measurements['connection.rtt'] = { value: connection.rtt }; + transaction.setTag('connection.rtt', String(connection.rtt)); } if (typeof connection.downlink === 'number') { this._measurements['connection.downlink'] = { value: connection.downlink }; + transaction.setTag('connection.downlink', String(connection.downlink)); } } if (typeof navigator?.deviceMemory === 'number') { this._browserContext.deviceMemory = navigator.deviceMemory; + transaction.setTag('deviceMemory', String(navigator.deviceMemory)); } if (typeof navigator?.hardwareConcurrency === 'number') { this._browserContext.hardwareConcurrency = navigator.hardwareConcurrency; + transaction.setTag('hardwareConcurrency', String(navigator.hardwareConcurrency)); } } From 287db9a189b10ee1fada00bfe22f72833492f746 Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 16 Oct 2020 17:56:17 -0400 Subject: [PATCH 07/13] add isMeasurementValue --- packages/tracing/src/browser/metrics.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index 596db40842d3..490d737435a0 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -179,23 +179,23 @@ export class MetricsInstrumentation { this._browserContext.effectiveConnectionType = connection.effectiveType; } - if (typeof connection.rtt === 'number') { + if (isMeasurementValue(connection.rtt)) { this._measurements['connection.rtt'] = { value: connection.rtt }; transaction.setTag('connection.rtt', String(connection.rtt)); } - if (typeof connection.downlink === 'number') { + if (isMeasurementValue(connection.downlink)) { this._measurements['connection.downlink'] = { value: connection.downlink }; transaction.setTag('connection.downlink', String(connection.downlink)); } } - if (typeof navigator?.deviceMemory === 'number') { + if (isMeasurementValue(navigator.deviceMemory)) { this._browserContext.deviceMemory = navigator.deviceMemory; transaction.setTag('deviceMemory', String(navigator.deviceMemory)); } - if (typeof navigator?.hardwareConcurrency === 'number') { + if (isMeasurementValue(navigator.hardwareConcurrency)) { this._browserContext.hardwareConcurrency = navigator.hardwareConcurrency; transaction.setTag('hardwareConcurrency', String(navigator.hardwareConcurrency)); } @@ -384,3 +384,7 @@ export function _startChild(transaction: Transaction, { startTimestamp, ...ctx } ...ctx, }); } + +function isMeasurementValue(measurement: any): measurement is number { + return typeof measurement === 'number' && isFinite(measurement); +} From c1766e4fb2b77446af29870b4679e31ddeb4ff5d Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 16 Oct 2020 17:59:47 -0400 Subject: [PATCH 08/13] set connection type as tags --- packages/tracing/src/browser/metrics.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index 490d737435a0..3a72267d0fc3 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -177,6 +177,11 @@ export class MetricsInstrumentation { if (connection) { if (connection.effectiveType) { this._browserContext.effectiveConnectionType = connection.effectiveType; + transaction.setTag('effectiveConnectionType', connection.effectiveType); + } + + if (connection.type) { + transaction.setTag('connectionType', connection.type); } if (isMeasurementValue(connection.rtt)) { From db79316efa1911103d422e24a450843dff6d2750 Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 16 Oct 2020 18:15:11 -0400 Subject: [PATCH 09/13] remove browser context --- packages/tracing/src/browser/metrics.ts | 12 ------------ packages/tracing/src/transaction.ts | 11 +---------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index 3a72267d0fc3..d493861a8b53 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -15,17 +15,9 @@ import { NavigatorDeviceMemory, NavigatorNetworkInformation } from './web-vitals const global = getGlobalObject(); -type BrowserContext = { - effectiveConnectionType?: string; - deviceMemory?: number; - // number of CPUs - hardwareConcurrency?: number; -}; - /** Class tracking metrics */ export class MetricsInstrumentation { private _measurements: Measurements = {}; - private _browserContext: BrowserContext = {}; private _performanceCursor: number = 0; @@ -142,7 +134,6 @@ export class MetricsInstrumentation { // Measurements are only available for pageload transactions if (transaction.op === 'pageload') { - transaction.setContexts({ browser: this._browserContext }); transaction.setMeasurements(this._measurements); } } @@ -176,7 +167,6 @@ export class MetricsInstrumentation { const connection = navigator.connection; if (connection) { if (connection.effectiveType) { - this._browserContext.effectiveConnectionType = connection.effectiveType; transaction.setTag('effectiveConnectionType', connection.effectiveType); } @@ -196,12 +186,10 @@ export class MetricsInstrumentation { } if (isMeasurementValue(navigator.deviceMemory)) { - this._browserContext.deviceMemory = navigator.deviceMemory; transaction.setTag('deviceMemory', String(navigator.deviceMemory)); } if (isMeasurementValue(navigator.hardwareConcurrency)) { - this._browserContext.hardwareConcurrency = navigator.hardwareConcurrency; transaction.setTag('hardwareConcurrency', String(navigator.hardwareConcurrency)); } } diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 15b9c779944f..7f51f84462d7 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -1,5 +1,5 @@ import { getCurrentHub, Hub } from '@sentry/hub'; -import { Contexts, Event, Measurements, Transaction as TransactionInterface, TransactionContext } from '@sentry/types'; +import { Event, Measurements, Transaction as TransactionInterface, TransactionContext } from '@sentry/types'; import { isInstanceOf, logger } from '@sentry/utils'; import { Span as SpanClass, SpanRecorder } from './span'; @@ -8,7 +8,6 @@ import { Span as SpanClass, SpanRecorder } from './span'; export class Transaction extends SpanClass implements TransactionInterface { public name: string; private _measurements: Measurements = {}; - private _contexts: Contexts = {}; /** * The reference to the current hub. @@ -65,13 +64,6 @@ export class Transaction extends SpanClass implements TransactionInterface { this._measurements = { ...measurements }; } - /** - * - */ - public setContexts(contexts: Contexts): void { - this._contexts = contexts; - } - /** * @inheritDoc */ @@ -108,7 +100,6 @@ export class Transaction extends SpanClass implements TransactionInterface { const transaction: Event = { contexts: { - ...this._contexts, trace: this.getTraceContext(), }, spans: finishedSpans, From b81b4491462b5dd653a98cc08af8035e24e2e3df Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Fri, 16 Oct 2020 21:47:35 -0400 Subject: [PATCH 10/13] don't track rtt and downlink as tags --- packages/tracing/src/browser/metrics.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index d493861a8b53..73545555f0c3 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -176,12 +176,10 @@ export class MetricsInstrumentation { if (isMeasurementValue(connection.rtt)) { this._measurements['connection.rtt'] = { value: connection.rtt }; - transaction.setTag('connection.rtt', String(connection.rtt)); } if (isMeasurementValue(connection.downlink)) { this._measurements['connection.downlink'] = { value: connection.downlink }; - transaction.setTag('connection.downlink', String(connection.downlink)); } } From 84dac1ca14e192a5352a01590a53ad5e2462bdfe Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Mon, 19 Oct 2020 10:48:29 -0400 Subject: [PATCH 11/13] add jsdoc for isMeasurementValue --- packages/tracing/src/browser/metrics.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index 73545555f0c3..89ae6727821d 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -376,6 +376,9 @@ export function _startChild(transaction: Transaction, { startTimestamp, ...ctx } }); } -function isMeasurementValue(measurement: any): measurement is number { - return typeof measurement === 'number' && isFinite(measurement); +/** + * Checks if a given value is a valid measurement value. + */ +function isMeasurementValue(value: any): value is number { + return typeof value === 'number' && isFinite(value); } From 13b88060d3d28d85724f76a9557c1d2c5b1cd04b Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Mon, 19 Oct 2020 10:49:42 -0400 Subject: [PATCH 12/13] yarn fix --- packages/tracing/src/browser/metrics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index 89ae6727821d..eadf1ae10218 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -158,7 +158,7 @@ export class MetricsInstrumentation { private _trackNavigator(transaction: Transaction): void { const navigator = global.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory); - if(!navigator) { + if (!navigator) { return; } From 221360d1923dda98d1912d09d4369188a9680419 Mon Sep 17 00:00:00 2001 From: Alberto Leal Date: Mon, 19 Oct 2020 11:00:28 -0400 Subject: [PATCH 13/13] remove usage of type predicates --- packages/tracing/src/browser/metrics.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index eadf1ae10218..aec84d976720 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -175,11 +175,11 @@ export class MetricsInstrumentation { } if (isMeasurementValue(connection.rtt)) { - this._measurements['connection.rtt'] = { value: connection.rtt }; + this._measurements['connection.rtt'] = { value: connection.rtt as number }; } if (isMeasurementValue(connection.downlink)) { - this._measurements['connection.downlink'] = { value: connection.downlink }; + this._measurements['connection.downlink'] = { value: connection.downlink as number }; } } @@ -379,6 +379,6 @@ export function _startChild(transaction: Transaction, { startTimestamp, ...ctx } /** * Checks if a given value is a valid measurement value. */ -function isMeasurementValue(value: any): value is number { +function isMeasurementValue(value: any): boolean { return typeof value === 'number' && isFinite(value); }