diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index bcef09afc79e..aec84d976720 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -11,6 +11,7 @@ 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(); @@ -129,6 +130,8 @@ 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') { transaction.setMeasurements(this._measurements); @@ -149,6 +152,46 @@ export class MetricsInstrumentation { }); } + /** + * Capture the information of the user agent. + */ + private _trackNavigator(transaction: Transaction): void { + const navigator = global.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory); + + if (!navigator) { + return; + } + + // track network connectivity + + const connection = navigator.connection; + if (connection) { + if (connection.effectiveType) { + transaction.setTag('effectiveConnectionType', connection.effectiveType); + } + + if (connection.type) { + transaction.setTag('connectionType', connection.type); + } + + if (isMeasurementValue(connection.rtt)) { + this._measurements['connection.rtt'] = { value: connection.rtt as number }; + } + + if (isMeasurementValue(connection.downlink)) { + this._measurements['connection.downlink'] = { value: connection.downlink as number }; + } + } + + if (isMeasurementValue(navigator.deviceMemory)) { + transaction.setTag('deviceMemory', String(navigator.deviceMemory)); + } + + if (isMeasurementValue(navigator.hardwareConcurrency)) { + transaction.setTag('hardwareConcurrency', String(navigator.hardwareConcurrency)); + } + } + /** Starts tracking the Largest Contentful Paint on the current page. */ private _trackLCP(): void { getLCP(metric => { @@ -332,3 +375,10 @@ export function _startChild(transaction: Transaction, { startTimestamp, ...ctx } ...ctx, }); } + +/** + * Checks if a given value is a valid measurement value. + */ +function isMeasurementValue(value: any): boolean { + return typeof value === 'number' && isFinite(value); +} 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; +}