diff --git a/superset-frontend/src/dashboard/components/Dashboard.jsx b/superset-frontend/src/dashboard/components/Dashboard.jsx index e168b394a5bc8..8a5e037983665 100644 --- a/superset-frontend/src/dashboard/components/Dashboard.jsx +++ b/superset-frontend/src/dashboard/components/Dashboard.jsx @@ -101,6 +101,7 @@ class Dashboard extends React.PureComponent { const bootstrapData = appContainer?.getAttribute('data-bootstrap') || ''; const { dashboardState, layout } = this.props; const eventData = { + is_soft_navigation: Logger.timeOriginOffset > 0, is_edit_mode: dashboardState.editMode, mount_duration: Logger.getTimestamp(), is_empty: isDashboardEmpty(layout), diff --git a/superset-frontend/src/logger/LogUtils.ts b/superset-frontend/src/logger/LogUtils.ts index bfb7a7f112b41..9cdb5e4634bd5 100644 --- a/superset-frontend/src/logger/LogUtils.ts +++ b/superset-frontend/src/logger/LogUtils.ts @@ -59,8 +59,14 @@ export const LOG_EVENT_TYPE_USER = new Set([ ]); export const Logger = { - // note that this returns ms since page load, NOT ms since epoch + timeOriginOffset: 0, + + markTimeOrigin() { + this.timeOriginOffset = window.performance.now(); + }, + + // note that this returns ms since last navigation, NOT ms since epoch getTimestamp() { - return Math.round(window.performance.now()); + return Math.round(window.performance.now() - this.timeOriginOffset); }, }; diff --git a/superset-frontend/src/views/App.tsx b/superset-frontend/src/views/App.tsx index 445a87527f8c7..d04b08d93f147 100644 --- a/superset-frontend/src/views/App.tsx +++ b/superset-frontend/src/views/App.tsx @@ -16,10 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -import React, { Suspense } from 'react'; +import React, { Suspense, useEffect } from 'react'; import { hot } from 'react-hot-loader/root'; import { Provider as ReduxProvider } from 'react-redux'; -import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; +import { + BrowserRouter as Router, + Switch, + Route, + useLocation, +} from 'react-router-dom'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { QueryParamProvider } from 'use-query-params'; @@ -34,6 +39,7 @@ import { theme } from 'src/preamble'; import ToastPresenter from 'src/messageToasts/containers/ToastPresenter'; import setupApp from 'src/setup/setupApp'; import { routes, isFrontendRoute } from 'src/views/routes'; +import { Logger } from 'src/logger/LogUtils'; import { store } from './store'; setupApp(); @@ -43,26 +49,39 @@ const bootstrap = JSON.parse(container?.getAttribute('data-bootstrap') ?? '{}'); const user = { ...bootstrap.user }; const menu = { ...bootstrap.common.menu_data }; const common = { ...bootstrap.common }; +let lastLocationPathname: string; initFeatureFlags(bootstrap.common.feature_flags); -const RootContextProviders: React.FC = ({ children }) => ( - - - - - - - {children} - - - - - - -); +const RootContextProviders: React.FC = ({ children }) => { + const location = useLocation(); + useEffect(() => { + // reset performance logger timer start point to avoid soft navigation + // cause dashboard perf measurement problem + if (lastLocationPathname && lastLocationPathname !== location.pathname) { + Logger.markTimeOrigin(); + } + lastLocationPathname = location.pathname; + }, [location.pathname]); + + return ( + + + + + + + {children} + + + + + + + ); +}; const App = () => (