diff --git a/app/client/src/actions/actionActions.ts b/app/client/src/actions/actionActions.ts index 8ded9afeed..11f299e8a0 100644 --- a/app/client/src/actions/actionActions.ts +++ b/app/client/src/actions/actionActions.ts @@ -192,6 +192,7 @@ export const executeApiActionRequest = (payload: { id: string }) => ({ export const executeApiActionSuccess = (payload: { id: string; response: ActionResponse; + isPageLoad?: boolean; }) => ({ type: ReduxActionTypes.EXECUTE_API_ACTION_SUCCESS, payload: payload, diff --git a/app/client/src/actions/widgetActions.tsx b/app/client/src/actions/widgetActions.tsx index c77afa35b0..8af9293970 100644 --- a/app/client/src/actions/widgetActions.tsx +++ b/app/client/src/actions/widgetActions.tsx @@ -9,6 +9,9 @@ import { PageAction, } from "constants/ActionConstants"; import { BatchAction, batchAction } from "actions/batchActions"; +import PerformanceTracker, { + PerformanceTransactionName, +} from "utils/PerformanceTracker"; export const executeAction = ( payload: ExecuteActionPayload, @@ -79,6 +82,9 @@ export const closeAllModals = () => { }; export const forceOpenPropertyPane = (id: string) => { + PerformanceTracker.startTracking( + PerformanceTransactionName.OPEN_PROPERTY_PANE, + ); return { type: ReduxActionTypes.SHOW_PROPERTY_PANE, payload: { diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index b751baf292..084d5e30af 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -11,6 +11,9 @@ import { import AnalyticsUtil from "utils/AnalyticsUtil"; import { WidgetType } from "constants/WidgetConstants"; import HelpControl from "./HelpControl"; +import PerformanceTracker, { + PerformanceTransactionName, +} from "utils/PerformanceTracker"; const PositionStyle = styled.div` position: absolute; @@ -68,6 +71,9 @@ export const WidgetNameComponent = (props: WidgetNameComponentProps) => { props.widgetId === propertyPaneState.widgetId) || props.widgetId !== propertyPaneState.widgetId ) { + PerformanceTracker.startTracking( + PerformanceTransactionName.OPEN_PROPERTY_PANE, + ); AnalyticsUtil.logEvent("PROPERTY_PANE_OPEN_CLICK", { widgetType: props.type, widgetId: props.widgetId, diff --git a/app/client/src/configs/index.ts b/app/client/src/configs/index.ts index dd8f94dda8..50e1206945 100644 --- a/app/client/src/configs/index.ts +++ b/app/client/src/configs/index.ts @@ -178,7 +178,7 @@ export const getAppsmithConfigs = (): AppsmithUIConfigs => { routingInstrumentation: Sentry.reactRouterV5Instrumentation(history), }), ], - tracesSampleRate: 1.0, + tracesSampleRate: 0.5, }, smartLook: { enabled: smartLook.enabled, diff --git a/app/client/src/constants/ActionConstants.tsx b/app/client/src/constants/ActionConstants.tsx index 88a5697d16..484f812f1d 100644 --- a/app/client/src/constants/ActionConstants.tsx +++ b/app/client/src/constants/ActionConstants.tsx @@ -65,6 +65,7 @@ export interface PageAction { export interface ExecuteErrorPayload { actionId: string; error: any; + isPageLoad?: boolean; } // Group 1 = datasource (https://www.domain.com) diff --git a/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx b/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx index e68975be60..0864197d78 100644 --- a/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx +++ b/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx @@ -17,9 +17,6 @@ import { getCurrentPageName, } from "selectors/editorSelectors"; import ConfirmRunModal from "pages/Editor/ConfirmRunModal"; -import PerformanceTracker, { - PerformanceTransactionName, -} from "utils/PerformanceTracker"; const Section = styled.section` background: ${props => props.theme.colors.bodyBG}; @@ -112,15 +109,11 @@ class AppViewerPageContainer extends Component { } const mapStateToProps = (state: AppState) => { - PerformanceTracker.startTracking( - PerformanceTransactionName.GENERATE_VIEW_MODE_PROPS, - ); const props = { isFetchingPage: getIsFetchingPage(state), widgets: getCanvasWidgetDsl(state), currentPageName: getCurrentPageName(state), }; - PerformanceTracker.stopTracking(); return props; }; diff --git a/app/client/src/pages/Editor/APIEditor/index.tsx b/app/client/src/pages/Editor/APIEditor/index.tsx index bc2cdbb7b7..c8cae4772a 100644 --- a/app/client/src/pages/Editor/APIEditor/index.tsx +++ b/app/client/src/pages/Editor/APIEditor/index.tsx @@ -40,8 +40,8 @@ const LoadingContainer = styled(CenteredWrapper)` interface ReduxStateProps { actions: ActionDataState; - isRunning: Record; - isDeleting: Record; + isRunning: boolean; + isDeleting: boolean; isCreating: boolean; apiName: string; currentApplication: UserApplication; @@ -71,6 +71,9 @@ type Props = ReduxActionProps & class ApiEditor extends React.Component { componentDidMount() { + PerformanceTracker.stopTracking(PerformanceTransactionName.OPEN_ACTION, { + actionType: "API", + }); this.props.changeAPIPage(this.props.match.params.apiId); } handleDeleteClick = () => { @@ -87,6 +90,9 @@ class ApiEditor extends React.Component { }; componentDidUpdate(prevProps: Props) { + if (prevProps.isRunning === true && this.props.isRunning === false) { + PerformanceTracker.stopTracking(PerformanceTransactionName.RUN_API_CLICK); + } if (prevProps.match.params.apiId !== this.props.match.params.apiId) { this.props.changeAPIPage(this.props.match.params.apiId); } @@ -97,6 +103,9 @@ class ApiEditor extends React.Component { this.props.pages, this.props.match.params.pageId, ); + PerformanceTracker.startTracking(PerformanceTransactionName.RUN_API_CLICK, { + apiId: this.props.match.params.apiId, + }); AnalyticsUtil.logEvent("RUN_API_CLICK", { apiName: this.props.apiName, apiID: this.props.match.params.apiId, @@ -174,8 +183,8 @@ class ApiEditor extends React.Component { { apiName={this.props.apiName} apiId={this.props.match.params.apiId} paginationType={paginationType} - isRunning={isRunning[apiId]} - isDeleting={isDeleting[apiId]} + isRunning={isRunning} + isDeleting={isDeleting} onDeleteClick={this.handleDeleteClick} onRunClick={this.handleRunClick} appName={ @@ -217,9 +226,6 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => { const apiAction = getActionById(state, props); const apiName = getApiName(state, props.match.params.apiId); const { isDeleting, isRunning, isCreating } = state.ui.apiPane; - PerformanceTracker.startTracking( - PerformanceTransactionName.GENERATE_API_PROPS, - ); const apiEditorState = { actions: state.entities.actions, currentApplication: getCurrentApplication(state), @@ -230,12 +236,11 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => { pluginId: _.get(apiAction, "pluginId"), paginationType: _.get(apiAction, "actionConfiguration.paginationType"), apiAction, - isRunning, - isDeleting, - isCreating, + isRunning: isRunning[props.match.params.apiId], + isDeleting: isDeleting[props.match.params.apiId], + isCreating: isCreating, isEditorInitialized: getIsEditorInitialized(state), }; - PerformanceTracker.stopTracking(); return apiEditorState; }; diff --git a/app/client/src/pages/Editor/Explorer/Actions/ActionEntity.tsx b/app/client/src/pages/Editor/Explorer/Actions/ActionEntity.tsx index 4bab144064..f696e7ab82 100644 --- a/app/client/src/pages/Editor/Explorer/Actions/ActionEntity.tsx +++ b/app/client/src/pages/Editor/Explorer/Actions/ActionEntity.tsx @@ -7,6 +7,9 @@ import EntityProperties from "../Entity/EntityProperties"; import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory"; import { ExplorerURLParams } from "../helpers"; import { useParams } from "react-router"; +import PerformanceTracker, { + PerformanceTransactionName, +} from "utils/PerformanceTracker"; const getUpdateActionNameReduxAction = (id: string, name: string) => { return saveActionName({ id, name }); @@ -25,6 +28,9 @@ type ExplorerActionEntityProps = { export const ExplorerActionEntity = memo((props: ExplorerActionEntityProps) => { const { pageId } = useParams(); const switchToAction = useCallback(() => { + PerformanceTracker.startTracking(PerformanceTransactionName.OPEN_ACTION, { + url: props.url, + }); props.url && history.push(props.url); }, [props.url]); diff --git a/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx b/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx index cfecce8382..dcc8ebd72a 100644 --- a/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx +++ b/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx @@ -26,7 +26,9 @@ export const EntityProperties = (props: { PerformanceTransactionName.ENTITY_EXPLORER_ENTITY, ); useEffect(() => { - PerformanceTracker.stopTracking(); + PerformanceTracker.stopTracking( + PerformanceTransactionName.ENTITY_EXPLORER_ENTITY, + ); }); let entity: any; const dataTree: DataTree = useSelector(evaluateDataTreeWithoutFunctions); diff --git a/app/client/src/pages/Editor/PropertyPane/index.tsx b/app/client/src/pages/Editor/PropertyPane/index.tsx index f4749366c0..f0163cccf0 100644 --- a/app/client/src/pages/Editor/PropertyPane/index.tsx +++ b/app/client/src/pages/Editor/PropertyPane/index.tsx @@ -175,11 +175,20 @@ class PropertyPane extends Component< } }; + componentDidMount() { + PerformanceTracker.stopTracking( + PerformanceTransactionName.OPEN_PROPERTY_PANE, + ); + } + componentDidUpdate(prevProps: PropertyPaneProps & PropertyPaneFunctions) { if ( this.props.widgetId !== prevProps.widgetId && this.props.widgetId !== undefined ) { + PerformanceTracker.stopTracking( + PerformanceTransactionName.OPEN_PROPERTY_PANE, + ); if (prevProps.widgetId && prevProps.widgetProperties) { AnalyticsUtil.logEvent("PROPERTY_PANE_CLOSE", { widgetType: prevProps.widgetProperties.type, @@ -227,16 +236,12 @@ class PropertyPane extends Component< } const mapStateToProps = (state: AppState): PropertyPaneProps => { - PerformanceTracker.startTracking( - PerformanceTransactionName.GENERATE_PROPERTY_PANE_PROPS, - ); const props = { propertySections: getPropertyConfig(state), widgetId: getCurrentWidgetId(state), widgetProperties: getWidgetPropsForPropertyPane(state), isVisible: getIsPropertyPaneVisible(state), }; - PerformanceTracker.stopTracking(); return props; }; diff --git a/app/client/src/pages/Editor/QueryEditor/index.tsx b/app/client/src/pages/Editor/QueryEditor/index.tsx index ac86fcad16..033c33c842 100644 --- a/app/client/src/pages/Editor/QueryEditor/index.tsx +++ b/app/client/src/pages/Editor/QueryEditor/index.tsx @@ -12,7 +12,6 @@ import { getIsEditorInitialized } from "selectors/editorSelectors"; import { QUERY_EDITOR_FORM_NAME } from "constants/forms"; import { Plugin } from "api/PluginApi"; import { Datasource } from "api/DatasourcesApi"; -import { QueryPaneReduxState } from "reducers/uiReducers/queryPaneReducer"; import { getPluginIdsOfPackageNames, getPlugins, @@ -26,6 +25,10 @@ import { QueryAction } from "entities/Action"; import Spinner from "components/editorComponents/Spinner"; import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper"; import { changeQuery } from "actions/queryPaneActions"; +import PerformanceTracker, { + PerformanceTransactionName, +} from "utils/PerformanceTracker"; +import AnalyticsUtil from "utils/AnalyticsUtil"; const EmptyStateContainer = styled.div` display: flex; @@ -46,7 +49,8 @@ type ReduxDispatchProps = { type ReduxStateProps = { plugins: Plugin[]; dataSources: Datasource[]; - queryPane: QueryPaneReduxState; + isRunning: boolean; + isDeleting: boolean; formData: QueryAction; runErrorMessage: Record; pluginIds: Array | undefined; @@ -65,6 +69,9 @@ type Props = StateAndRouteProps & ReduxDispatchProps & ReduxStateProps; class QueryEditor extends React.Component { componentDidMount() { this.props.changeQueryPage(this.props.match.params.queryId); + PerformanceTracker.stopTracking(PerformanceTransactionName.OPEN_ACTION, { + actionType: "QUERY", + }); } handleDeleteClick = () => { const { queryId } = this.props.match.params; @@ -74,10 +81,22 @@ class QueryEditor extends React.Component { handleRunClick = () => { const { match } = this.props; + PerformanceTracker.startTracking( + PerformanceTransactionName.RUN_QUERY_CLICK, + { queryId: this.props.match.params.queryId }, + ); + AnalyticsUtil.logEvent("RUN_QUERY_CLICK", { + queryId: this.props.match.params.queryId, + }); this.props.runAction(match.params.queryId); }; componentDidUpdate(prevProps: Props) { + if (prevProps.isRunning === true && this.props.isRunning === false) { + PerformanceTracker.stopTracking( + PerformanceTransactionName.RUN_QUERY_CLICK, + ); + } if (prevProps.match.params.queryId !== this.props.match.params.queryId) { this.props.changeQueryPage(this.props.match.params.queryId); } @@ -86,7 +105,8 @@ class QueryEditor extends React.Component { render() { const { dataSources, - queryPane, + isRunning, + isDeleting, match: { params: { queryId }, }, @@ -114,7 +134,6 @@ class QueryEditor extends React.Component { ); } - const { isRunning, isDeleting } = queryPane; const DATASOURCES_OPTIONS = dataSources.map(dataSource => ({ label: dataSource.name, @@ -128,8 +147,8 @@ class QueryEditor extends React.Component { location={this.props.location} applicationId={applicationId} pageId={pageId} - isRunning={isRunning[queryId]} - isDeleting={isDeleting[queryId]} + isRunning={isRunning} + isDeleting={isDeleting} onDeleteClick={this.handleDeleteClick} onRunClick={this.handleRunClick} dataSources={dataSources} @@ -175,7 +194,8 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => { pluginIds: getPluginIdsOfPackageNames(state, PLUGIN_PACKAGE_DBS), dataSources: getDBDatasources(state), responses: getActionResponses(state), - queryPane: state.ui.queryPane, + isRunning: state.ui.queryPane.isRunning[props.match.params.queryId], + isDeleting: state.ui.queryPane.isDeleting[props.match.params.queryId], formData, editorConfig, loadingFormConfigs, diff --git a/app/client/src/pages/Editor/WidgetsEditor.tsx b/app/client/src/pages/Editor/WidgetsEditor.tsx index abd8bb85e5..4314d33677 100644 --- a/app/client/src/pages/Editor/WidgetsEditor.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor.tsx @@ -61,7 +61,7 @@ const WidgetsEditor = () => { useEffect(() => { PerformanceTracker.stopTracking(PerformanceTransactionName.EDITOR_MOUNT); - PerformanceTracker.stopTracking(PerformanceTransactionName.CLOSE_API); + PerformanceTracker.stopTracking(PerformanceTransactionName.CLOSE_SIDE_PANE); }); // Switch page diff --git a/app/client/src/pages/Editor/routes.tsx b/app/client/src/pages/Editor/routes.tsx index e11560552d..42ffe67650 100644 --- a/app/client/src/pages/Editor/routes.tsx +++ b/app/client/src/pages/Editor/routes.tsx @@ -88,7 +88,10 @@ class EditorsRouter extends React.Component< } handleClose = (e: React.MouseEvent) => { - PerformanceTracker.startTracking(PerformanceTransactionName.CLOSE_API); + PerformanceTracker.startTracking( + PerformanceTransactionName.CLOSE_SIDE_PANE, + { path: this.props.location.pathname }, + ); e.stopPropagation(); const { applicationId, pageId } = this.props.match.params; this.setState({ diff --git a/app/client/src/reducers/uiReducers/editorReducer.tsx b/app/client/src/reducers/uiReducers/editorReducer.tsx index 7f2466f805..3e9d9d73ad 100644 --- a/app/client/src/reducers/uiReducers/editorReducer.tsx +++ b/app/client/src/reducers/uiReducers/editorReducer.tsx @@ -66,9 +66,14 @@ const editorReducer = createReducer(initialState, { [ReduxActionErrorTypes.INITIALIZE_EDITOR_ERROR]: ( state: EditorReduxState, ) => { - state.loadingStates.loading = false; - state.loadingStates.loadingError = true; - return { ...state }; + return { + ...state, + loadingStates: { + ...state.loadingStates, + loading: false, + loadingError: true, + }, + }; }, [ReduxActionTypes.PUBLISH_APPLICATION_INIT]: (state: EditorReduxState) => { state.loadingStates.publishing = true; diff --git a/app/client/src/sagas/ActionExecutionSagas.ts b/app/client/src/sagas/ActionExecutionSagas.ts index dd26bee5c1..25db87946c 100644 --- a/app/client/src/sagas/ActionExecutionSagas.ts +++ b/app/client/src/sagas/ActionExecutionSagas.ts @@ -77,6 +77,9 @@ import { updateAppStore } from "actions/pageActions"; import { getAppStoreName } from "constants/AppConstants"; import downloadjs from "downloadjs"; import { getType, Types } from "utils/TypeHelpers"; +import PerformanceTracker, { + PerformanceTransactionName, +} from "utils/PerformanceTracker"; function* navigateActionSaga( action: { pageNameOrUrl: string; params: Record }, @@ -283,6 +286,13 @@ export function* executeActionSaga( event: ExecuteActionPayloadEvent, ) { const { actionId, onSuccess, onError, params } = apiAction; + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.EXECUTE_ACTION, + { + actionId: actionId, + }, + actionId, + ); try { const api: RestAction = yield select(getAction, actionId); @@ -326,6 +336,11 @@ export function* executeActionSaga( }), ); if (isErrorResponse(response)) { + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.EXECUTE_ACTION, + { failed: true }, + actionId, + ); if (onError) { yield put( executeAction({ @@ -348,6 +363,11 @@ export function* executeActionSaga( type: "error", }); } else { + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.EXECUTE_ACTION, + undefined, + actionId, + ); if (onSuccess) { yield put( executeAction({ @@ -579,6 +599,14 @@ function* confirmRunActionSaga() { } function* executePageLoadAction(pageAction: PageAction) { + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.EXECUTE_ACTION, + { + actionId: pageAction.id, + }, + pageAction.id, + PerformanceTransactionName.EXECUTE_PAGE_LOAD_ACTIONS, + ); yield put(executeApiActionRequest({ id: pageAction.id })); const params: Property[] = yield call( getActionParams, @@ -592,20 +620,33 @@ function* executePageLoadAction(pageAction: PageAction) { executeActionRequest, pageAction.timeoutInMillisecond, ); - if (isErrorResponse(response)) { yield put( executeActionError({ actionId: pageAction.id, error: response.responseMeta.error, + isPageLoad: true, }), ); + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.EXECUTE_ACTION, + { + failed: true, + }, + pageAction.id, + ); } else { const payload = createActionExecutionResponse(response); + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.EXECUTE_ACTION, + undefined, + pageAction.id, + ); yield put( executeApiActionSuccess({ id: pageAction.id, response: payload, + isPageLoad: true, }), ); } @@ -613,10 +654,20 @@ function* executePageLoadAction(pageAction: PageAction) { function* executePageLoadActionsSaga(action: ReduxAction) { const pageActions = action.payload; + const actionCount = _.flatten(pageActions).length; + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.EXECUTE_PAGE_LOAD_ACTIONS, + { numActions: actionCount }, + ); for (const actionSet of pageActions) { // Load all sets in parallel - yield* yield all(actionSet.map(a => call(executePageLoadAction, a))); + yield* yield all( + actionSet.map(apiAction => call(executePageLoadAction, apiAction)), + ); } + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.EXECUTE_PAGE_LOAD_ACTIONS, + ); } export function* watchActionExecutionSagas() { diff --git a/app/client/src/sagas/ActionSagas.ts b/app/client/src/sagas/ActionSagas.ts index 8773ce6fd1..ff178b3b3f 100644 --- a/app/client/src/sagas/ActionSagas.ts +++ b/app/client/src/sagas/ActionSagas.ts @@ -60,6 +60,9 @@ import { QUERIES_EDITOR_ID_URL, API_EDITOR_ID_URL, } from "constants/routes"; +import PerformanceTracker, { + PerformanceTransactionName, +} from "utils/PerformanceTracker"; export function* createActionSaga(actionPayload: ReduxAction) { try { @@ -94,8 +97,12 @@ export function* createActionSaga(actionPayload: ReduxAction) { } export function* fetchActionsSaga(action: ReduxAction) { + const { applicationId } = action.payload; + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.FETCH_ACTIONS_API, + { mode: "EDITOR", appId: applicationId }, + ); try { - const { applicationId } = action.payload; const response: GenericApiResponse = yield ActionAPI.fetchActions( applicationId, ); @@ -105,20 +112,31 @@ export function* fetchActionsSaga(action: ReduxAction) { type: ReduxActionTypes.FETCH_ACTIONS_SUCCESS, payload: response.data, }); + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_ACTIONS_API, + ); } } catch (error) { yield put({ type: ReduxActionErrorTypes.FETCH_ACTIONS_ERROR, payload: { error }, }); + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_ACTIONS_API, + { failed: true }, + ); } } export function* fetchActionsForViewModeSaga( action: ReduxAction, ) { + const { applicationId } = action.payload; + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.FETCH_ACTIONS_API, + { mode: "VIEWER", appId: applicationId }, + ); try { - const { applicationId } = action.payload; const response: GenericApiResponse = yield ActionAPI.fetchActionsForViewMode( applicationId, ); @@ -128,20 +146,31 @@ export function* fetchActionsForViewModeSaga( type: ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS, payload: response.data, }); + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_ACTIONS_API, + ); } } catch (error) { yield put({ type: ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR, payload: { error }, }); + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_ACTIONS_API, + { failed: true }, + ); } } export function* fetchActionsForPageSaga( action: ReduxAction<{ pageId: string }>, ) { + const { pageId } = action.payload; + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_ACTIONS_API, + { pageId: pageId }, + ); try { - const { pageId } = action.payload; const response: GenericApiResponse = yield call( ActionAPI.fetchActionsByPageId, pageId, @@ -149,8 +178,15 @@ export function* fetchActionsForPageSaga( const isValidResponse = yield validateResponse(response); if (isValidResponse) { yield put(fetchActionsForPageSuccess(response.data)); + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_ACTIONS_API, + ); } } catch (error) { + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_ACTIONS_API, + { failed: true }, + ); yield put({ type: ReduxActionErrorTypes.FETCH_ACTIONS_FOR_PAGE_ERROR, payload: { error }, @@ -160,6 +196,10 @@ export function* fetchActionsForPageSaga( export function* updateActionSaga(actionPayload: ReduxAction<{ id: string }>) { try { + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.UPDATE_ACTION_API, + { actionid: actionPayload.payload.id }, + ); let action: Action = yield select(getAction, actionPayload.payload.id); const isApi = action.pluginType === "API"; const isDB = action.pluginType === "DB"; @@ -193,10 +233,16 @@ export function* updateActionSaga(actionPayload: ReduxAction<{ id: string }>) { pageName: pageName, }); } - + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.UPDATE_ACTION_API, + ); yield put(updateActionSuccess({ data: response.data })); } } catch (error) { + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.UPDATE_ACTION_API, + { failed: true }, + ); yield put({ type: ReduxActionErrorTypes.UPDATE_ACTION_ERROR, payload: { error, id: actionPayload.payload.id }, @@ -350,6 +396,10 @@ export function* refactorActionName( newName: string, ) { // fetch page of the action + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.REFACTOR_ACTION_NAME, + { actionId: id }, + ); const pageResponse = yield call(PageApi.fetchPage, { id: pageId, }); @@ -369,6 +419,11 @@ export function* refactorActionName( const isRefactorSuccessful = yield validateResponse(refactorResponse); const currentPageId = yield select(getCurrentPageId); + + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.REFACTOR_ACTION_NAME, + { isSuccess: isRefactorSuccessful }, + ); if (isRefactorSuccessful) { yield put({ type: ReduxActionTypes.SAVE_ACTION_NAME_SUCCESS, diff --git a/app/client/src/sagas/PageSagas.tsx b/app/client/src/sagas/PageSagas.tsx index 0d51c3aff8..5e0e6c1838 100644 --- a/app/client/src/sagas/PageSagas.tsx +++ b/app/client/src/sagas/PageSagas.tsx @@ -72,6 +72,9 @@ import { import { clearCaches } from "utils/DynamicBindingUtils"; import { UrlDataState } from "reducers/entityReducers/appReducer"; import { getQueryParams } from "utils/AppsmithUtils"; +import PerformanceTracker, { + PerformanceTransactionName, +} from "utils/PerformanceTracker"; const getWidgetName = (state: AppState, widgetId: string) => state.entities.canvasWidgets[widgetId]; @@ -79,6 +82,9 @@ const getWidgetName = (state: AppState, widgetId: string) => export function* fetchPageListSaga( fetchPageListAction: ReduxAction, ) { + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_LIST_API, + ); try { const { applicationId } = fetchPageListAction.payload; const response: FetchPageListResponse = yield call( @@ -106,8 +112,15 @@ export function* fetchPageListSaga( applicationId, }, }); + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_LIST_API, + ); } } catch (error) { + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_LIST_API, + { failed: true }, + ); yield put({ type: ReduxActionErrorTypes.FETCH_PAGE_LIST_ERROR, payload: { @@ -139,6 +152,10 @@ export function* fetchPageSaga( ) { try { const { id } = pageRequestAction.payload; + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_API, + { pageId: id }, + ); const fetchPageResponse: FetchPageResponse = yield call(PageApi.fetchPage, { id, }); @@ -167,9 +184,18 @@ export function* fetchPageSaga( dsl: extractCurrentDSL(fetchPageResponse), }, }); + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_API, + ); } } catch (error) { console.log(error); + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_API, + { + failed: true, + }, + ); yield put({ type: ReduxActionErrorTypes.FETCH_PAGE_ERROR, payload: { @@ -184,6 +210,10 @@ export function* fetchPublishedPageSaga( ) { try { const { pageId, bustCache } = pageRequestAction.payload; + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_API, + { pageId: pageId, published: true }, + ); const request: FetchPublishedPageRequest = { pageId, bustCache, @@ -213,9 +243,18 @@ export function* fetchPublishedPageSaga( }), ); // Execute page load actions + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_API, + ); yield put(executePageLoadActions(canvasWidgetsPayload.pageActions)); } } catch (error) { + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.FETCH_PAGE_API, + { + failed: true, + }, + ); yield put({ type: ReduxActionErrorTypes.FETCH_PUBLISHED_PAGE_ERROR, payload: { @@ -242,6 +281,12 @@ function* savePageSaga() { const widgets = yield select(getWidgets); const editorConfigs = yield select(getEditorConfigs) as any; const savePageRequest = getLayoutSavePayload(widgets, editorConfigs); + PerformanceTracker.startAsyncTracking( + PerformanceTransactionName.SAVE_PAGE_API, + { + pageId: savePageRequest.pageId, + }, + ); try { // Store the updated DSL in the pageDSLs reducer yield put({ @@ -266,8 +311,17 @@ function* savePageSaga() { } } yield put(savePageSuccess(savePageResponse)); + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.SAVE_PAGE_API, + ); } } catch (error) { + PerformanceTracker.stopAsyncTracking( + PerformanceTransactionName.SAVE_PAGE_API, + { + failed: true, + }, + ); yield put({ type: ReduxActionErrorTypes.SAVE_PAGE_ERROR, payload: { diff --git a/app/client/src/utils/AnalyticsUtil.tsx b/app/client/src/utils/AnalyticsUtil.tsx index 3e53acc8af..28f2d68c1b 100644 --- a/app/client/src/utils/AnalyticsUtil.tsx +++ b/app/client/src/utils/AnalyticsUtil.tsx @@ -45,6 +45,7 @@ export type EventName = | "DUPLICATE_API" | "DUPLICATE_API_CLICK" | "RUN_QUERY" + | "RUN_QUERY_CLICK" | "DELETE_QUERY" | "SAVE_QUERY" | "MOVE_API" diff --git a/app/client/src/utils/PerformanceTracker.ts b/app/client/src/utils/PerformanceTracker.ts index bc6975bd8a..197ee7451c 100644 --- a/app/client/src/utils/PerformanceTracker.ts +++ b/app/client/src/utils/PerformanceTracker.ts @@ -5,13 +5,6 @@ import * as log from "loglevel"; export enum PerformanceTransactionName { DEPLOY_APPLICATION = "DEPLOY_APPLICATION", - RUN_ACTION = "RUN_ACTION", - PAGE_SWITCH_EDIT = "PAGE_SWITCH_EDIT", - PAGE_SWITCH_VIEW = "PAGE_SWITCH_VIEW", - CREATE_ACTION = "CREATE_ACTION", - CURL_IMPORT = "CURL_IMPORT", - EXECUTE_WIDGET_ACTION = "EXECUTE_WIDGET_ACTION", - RUN_ACTION_WAIT_FOR_SAVE = "RUN_ACTION_WAIT_FOR_SAVE", DATA_TREE_EVALUATION = "DATA_TREE_EVALUATION", CONSTRUCT_UNEVAL_TREE = "CONSTRUCT_UNEVAL_TREE", CONSTRUCT_CANVAS_DSL = "CONSTRUCT_CANVAS_DSL", @@ -20,22 +13,27 @@ export enum PerformanceTransactionName { SET_WIDGET_LOADING = "SET_WIDGET_LOADING", VALIDATE_DATA_TREE = "VALIDATE_DATA_TREE", EXECUTE_PAGE_LOAD_ACTIONS = "EXECUTE_PAGE_LOAD_ACTIONS", - SAVE_PAGE_LAYOUT = "SAVE_PAGE_LAYOUT", - SAVE_ACTION = "SAVE_ACTION", EVALUATE_BINDING = "EVALUATE_BINDING", - GENERATE_PROPERTY_PANE_PROPS = "GENERATE_PROPERTY_PANE_PROPS", - GENERATE_VIEW_MODE_PROPS = "GENERATE_VIEW_MODE_PROPS", - GENERATE_WIDGET_EDITOR_PROPS = "GENERATE_WIDGET_EDITOR_PROPS", ENTITY_EXPLORER_ENTITY = "ENTITY_EXPLORER_ENTITY", ENTITY_EXPLORER = "ENTITY_EXPLORER", - CLOSE_API = "CLOSE_API", - OPEN_API = "OPEN_API", + CLOSE_SIDE_PANE = "CLOSE_SIDE_PANE", + OPEN_ACTION = "OPEN_ACTION", EDITOR_MOUNT = "EDITOR_MOUNT", SIDE_BAR_MOUNT = "SIDE_BAR_MOUNT", CANVAS_MOUNT = "CANVAS_MOUNT", - GENERATE_API_PROPS = "GENERATE_API_PROPS", + EXECUTE_ACTION = "EXECUTE_ACTION", CHANGE_API_SAGA = "CHANGE_API_SAGA", SYNC_PARAMS_SAGA = "SYNC_PARAMS_SAGA", + RUN_API_CLICK = "RUN_API_CLICK", + RUN_QUERY_CLICK = "RUN_QUERY_CLICK", + FETCH_ACTIONS_API = "FETCH_ACTIONS_API", + FETCH_PAGE_LIST_API = "FETCH_PAGE_LIST_API", + FETCH_PAGE_ACTIONS_API = "FETCH_PAGE_ACTIONS_API", + FETCH_PAGE_API = "FETCH_PAGE_API", + SAVE_PAGE_API = "SAVE_PAGE_API", + UPDATE_ACTION_API = "UPDATE_ACTION_API", + OPEN_PROPERTY_PANE = "OPEN_PROPERTY_PANE", + REFACTOR_ACTION_NAME = "REFACTOR_ACTION_NAME", } export enum PerformanceTagNames { @@ -53,6 +51,7 @@ export interface PerfLog { class PerformanceTracker { private static perfLogQueue: PerfLog[] = []; + private static perfAsyncMap: Map = new Map(); static startTracking = ( eventName: PerformanceTransactionName, @@ -84,6 +83,7 @@ class PerformanceTracker { ); } const newTransaction = Sentry.startTransaction({ name: eventName }); + newTransaction.setData("startData", data); Sentry.getCurrentHub().configureScope(scope => scope.setSpan(newTransaction), ); @@ -117,8 +117,8 @@ class PerformanceTracker { }; static stopTracking = ( - data?: any, eventName?: PerformanceTransactionName, + data?: any, ) => { if (eventName) { const index = _.findLastIndex( @@ -128,9 +128,13 @@ class PerformanceTracker { }, ); if (index !== -1) { - for (let i = PerformanceTracker.perfLogQueue.length - 1; i >= 0; i--) { - const perfLog = PerformanceTracker.perfLogQueue[i]; - if (i >= index) { + for ( + let i = PerformanceTracker.perfLogQueue.length - 1; + i >= index; + i-- + ) { + const perfLog = PerformanceTracker.perfLogQueue.pop(); + if (perfLog) { const currentSpan = perfLog.sentrySpan; currentSpan.finish(); if (!perfLog?.skipLog) { @@ -143,9 +147,6 @@ class PerformanceTracker { } } } - PerformanceTracker.perfLogQueue = PerformanceTracker.perfLogQueue.splice( - index, - ); } } else { const perfLog = PerformanceTracker.perfLogQueue.pop(); @@ -165,6 +166,69 @@ class PerformanceTracker { } }; + static startAsyncTracking = ( + eventName: PerformanceTransactionName, + data?: any, + uniqueId?: string, + parentEventId?: string, + skipLog = false, + ) => { + if (!skipLog) { + log.debug( + "Async " + + PerformanceTracker.generateSpaces(0) + + eventName + + " Track Transaction ", + ); + } + if (!parentEventId) { + const newTransaction = Sentry.startTransaction({ name: eventName }); + newTransaction.setData("startData", data); + PerformanceTracker.perfAsyncMap.set(uniqueId ? uniqueId : eventName, { + sentrySpan: newTransaction, + eventName: eventName, + skipLog: skipLog, + }); + } else { + const perfLog = PerformanceTracker.perfAsyncMap.get(parentEventId); + const childSpan = perfLog?.sentrySpan.startChild({ + op: eventName, + data: data, + }); + if (childSpan) { + PerformanceTracker.perfAsyncMap.set(uniqueId ? uniqueId : eventName, { + sentrySpan: childSpan, + eventName: eventName, + skipLog: skipLog, + }); + } + } + }; + + static stopAsyncTracking( + eventName: PerformanceTransactionName, + data?: any, + uniqueId?: string, + ) { + const perfLog = PerformanceTracker.perfAsyncMap.get( + uniqueId ? uniqueId : eventName, + ); + if (perfLog) { + const currentSpan = perfLog.sentrySpan; + currentSpan.setData("endData", data); + currentSpan.finish(); + if (!perfLog?.skipLog) { + PerformanceTracker.printDuration( + perfLog.eventName, + 0, + currentSpan.startTimestamp, + currentSpan.endTimestamp, + true, + ); + } + } + } + static generateSpaces(num: number) { let str = ""; for (let i = 0; i < num; i++) { @@ -178,10 +242,18 @@ class PerformanceTracker { level: number, startTime: number, endTime?: number, + isAsync?: boolean, ) { const duration = ((endTime || 0) - startTime) * 1000; const spaces = PerformanceTracker.generateSpaces(level); - log.debug(spaces + eventName + " Finish Tracking in " + duration + "ms"); + log.debug( + (isAsync ? "Async " : "") + + spaces + + eventName + + " Finish Tracking in " + + duration + + "ms", + ); } }