1 import metrics from '@proton/metrics';
2 import type Metrics from '@proton/metrics/Metrics';
3 import Histogram from '@proton/metrics/lib/Histogram';
4 import { reportWebVitals } from '@proton/shared/lib/metrics/webvitals';
6 import { NAVIGATION_MARK } from '../hooks/util/useReactRouterNavigationLog';
8 export const getCurrentPageType = (path: string = window.location.pathname) => {
9 // Normalize the path: remove leading/trailing slashes and collapse multiple slashes
10 const normalizedPath = path.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/');
11 const pathParts = normalizedPath.split('/');
13 const part = pathParts[0] === 'u' ? pathParts[2] : pathParts[0];
20 return 'shared_by_me';
21 case 'shared-with-me':
22 return 'shared_with_me';
27 if (pathParts[0] === 'urls') {
32 (pathParts.length === 2 && pathParts[0] === 'u') ||
33 (pathParts.length === 5 && pathParts[3] === 'folder') ||
34 (pathParts.length === 5 && pathParts[3] === 'file') ||
35 path === '/' || // Redirect case
36 normalizedPath === '' // Redirect case (nothing)
42 const getBrowserTime = (marker: 'loadEventStart' | 'domContentLoadedEventStart'): number | undefined => {
43 const perfEntries = performance.getEntriesByType('navigation');
45 if (perfEntries && perfEntries.length > 0) {
46 const navigationEntry = perfEntries[0];
47 const startTime = navigationEntry.startTime;
48 return navigationEntry[marker] - startTime;
52 const logBrowserTime = (
53 metric: 'drive_performance_load_histogram' | 'drive_performance_domcontentloaded_histogram',
54 marker: 'loadEventStart' | 'domContentLoadedEventStart'
56 const time = getBrowserTime(marker);
57 const pageType = getCurrentPageType();
58 if (time && pageType) {
59 metrics[metric].observe({
68 const initializeBrowserEvents = () => {
69 window.addEventListener('load', () => logBrowserTime('drive_performance_load_histogram', 'loadEventStart'));
70 if (document.readyState === 'complete') {
71 logBrowserTime('drive_performance_domcontentloaded_histogram', 'domContentLoadedEventStart');
73 document.addEventListener('DOMContentLoaded', () =>
74 logBrowserTime('drive_performance_domcontentloaded_histogram', 'domContentLoadedEventStart')
80 * Initializes performance metrics for the application.
82 * This function sets up browser event listeners to log performance metrics
83 * and initializes web vitals reporting.
85 * @param isPublic - A boolean indicating whether the metrics are for a public or private context.
87 export const initializePerformanceMetrics = (isPublic: boolean) => {
88 initializeBrowserEvents();
89 reportWebVitals(isPublic ? 'public' : 'private');
92 const getNavigationStart = () => {
93 // By default we use the browser navigation
94 const perfEntries = performance.getEntriesByType('navigation');
95 if (perfEntries && perfEntries.length > 0) {
96 const navigationEntry = perfEntries[0];
97 let startTime = navigationEntry.startTime;
99 // If however, we had a react-router navigation, we use this navigation as the beginning of the navigation
100 const reactRouterNavigationEntries = performance
101 .getEntriesByType('mark')
102 .filter((entry) => entry.name === NAVIGATION_MARK);
104 const lastEntry = reactRouterNavigationEntries.at(-1);
106 startTime = lastEntry.startTime;
113 const getTimeFromNavigationStart = (key: string): number | undefined => {
114 const startTime = getNavigationStart();
115 if (startTime !== undefined) {
116 const measureName = `measure-${key}`;
118 performance.measure(measureName, {
123 const measurement = performance.getEntriesByName(measureName)[0];
124 return measurement.duration;
129 * Logs a performance marker and records its timing metrics.
131 * This function creates a performance mark, measures the time from navigation start
132 * to the mark (or uses a provided time), and records the metric to observability.
134 * @param key - The key of the performance marker, which should correspond to a key in the Metrics type (your observability metric).
135 * @param view - Optional parameter specifying the view type ('list' or 'grid'), used for certain metrics.
136 * @param timeInMs - Optional parameter to provide a specific time in milliseconds instead of measuring from navigation start.
137 * @returns The time recorded for the performance marker in milliseconds.
139 export const logPerformanceMarker = (key: keyof Metrics, view?: 'list' | 'grid', timeInMs?: number) => {
140 performance.mark(key);
141 const time = timeInMs !== undefined ? timeInMs : getTimeFromNavigationStart(key);
142 const pageType = getCurrentPageType();
144 if (time && pageType && metrics[key] instanceof Histogram) {
145 metrics[key].observe({
148 ...(view && { view }),