Feature/instrumentation (#744)

* bumped sentry version
modified performance monitor to use a queue internally to make synchronous logging easy
added logging for api pane close / open and various different methods

* added use effect to stop tracking on mount of entity properties

* removed open api tracking

* fixed stop tracking to pop from the end instead of the beginning
added tracking for editor mount and sidebar mount

* added tracking for entity explorer

* moved from app route to sentry app route because passing JSX to app route causes re-renders

* Fixing theme and route change issues.

* added performance tracking for API / Query execution and Page Load actions

* added isntrumentation to open / close actions

* added instrumentation for apis & actions

* reduced sample rate
added async tracking

* added tracking for property pane

* Remove tracking from reducer

* added option to attach async operations to parent transactions

* Fix typo

Co-authored-by: Nikhil Nandagopal <nikhil@appsmith.com>
Co-authored-by: Satbir Singh <satbir121@gmail.com>
Co-authored-by: Hetu Nandu <hetunandu@gmail.com>
This commit is contained in:
Nikhil Nandagopal 2020-09-28 10:42:23 +05:30 committed by GitHub
parent dc1e2e124f
commit 3fdd0a246a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 353 additions and 67 deletions

View File

@ -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,

View File

@ -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: {

View File

@ -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,

View File

@ -178,7 +178,7 @@ export const getAppsmithConfigs = (): AppsmithUIConfigs => {
routingInstrumentation: Sentry.reactRouterV5Instrumentation(history),
}),
],
tracesSampleRate: 1.0,
tracesSampleRate: 0.5,
},
smartLook: {
enabled: smartLook.enabled,

View File

@ -65,6 +65,7 @@ export interface PageAction {
export interface ExecuteErrorPayload {
actionId: string;
error: any;
isPageLoad?: boolean;
}
// Group 1 = datasource (https://www.domain.com)

View File

@ -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<AppViewerPageContainerProps> {
}
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;
};

View File

@ -40,8 +40,8 @@ const LoadingContainer = styled(CenteredWrapper)`
interface ReduxStateProps {
actions: ActionDataState;
isRunning: Record<string, boolean>;
isDeleting: Record<string, boolean>;
isRunning: boolean;
isDeleting: boolean;
isCreating: boolean;
apiName: string;
currentApplication: UserApplication;
@ -71,6 +71,9 @@ type Props = ReduxActionProps &
class ApiEditor extends React.Component<Props> {
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<Props> {
};
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<Props> {
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<Props> {
<ApiEditorForm
pluginId={pluginId}
paginationType={paginationType}
isRunning={isRunning[apiId]}
isDeleting={isDeleting[apiId]}
isRunning={isRunning}
isDeleting={isDeleting}
onDeleteClick={this.handleDeleteClick}
onRunClick={this.handleRunClick}
appName={
@ -192,8 +201,8 @@ class ApiEditor extends React.Component<Props> {
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;
};

View File

@ -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<ExplorerURLParams>();
const switchToAction = useCallback(() => {
PerformanceTracker.startTracking(PerformanceTransactionName.OPEN_ACTION, {
url: props.url,
});
props.url && history.push(props.url);
}, [props.url]);

View File

@ -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);

View File

@ -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;
};

View File

@ -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<string, string>;
pluginIds: Array<string> | undefined;
@ -65,6 +69,9 @@ type Props = StateAndRouteProps & ReduxDispatchProps & ReduxStateProps;
class QueryEditor extends React.Component<Props> {
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<Props> {
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<Props> {
render() {
const {
dataSources,
queryPane,
isRunning,
isDeleting,
match: {
params: { queryId },
},
@ -114,7 +134,6 @@ class QueryEditor extends React.Component<Props> {
</LoadingContainer>
);
}
const { isRunning, isDeleting } = queryPane;
const DATASOURCES_OPTIONS = dataSources.map(dataSource => ({
label: dataSource.name,
@ -128,8 +147,8 @@ class QueryEditor extends React.Component<Props> {
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,

View File

@ -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

View File

@ -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({

View File

@ -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;

View File

@ -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<string, string> },
@ -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<PageAction[][]>) {
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() {

View File

@ -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<RestAction>) {
try {
@ -94,8 +97,12 @@ export function* createActionSaga(actionPayload: ReduxAction<RestAction>) {
}
export function* fetchActionsSaga(action: ReduxAction<FetchActionsPayload>) {
const { applicationId } = action.payload;
PerformanceTracker.startAsyncTracking(
PerformanceTransactionName.FETCH_ACTIONS_API,
{ mode: "EDITOR", appId: applicationId },
);
try {
const { applicationId } = action.payload;
const response: GenericApiResponse<RestAction[]> = yield ActionAPI.fetchActions(
applicationId,
);
@ -105,20 +112,31 @@ export function* fetchActionsSaga(action: ReduxAction<FetchActionsPayload>) {
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<FetchActionsPayload>,
) {
const { applicationId } = action.payload;
PerformanceTracker.startAsyncTracking(
PerformanceTransactionName.FETCH_ACTIONS_API,
{ mode: "VIEWER", appId: applicationId },
);
try {
const { applicationId } = action.payload;
const response: GenericApiResponse<RestAction[]> = 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<RestAction[]> = 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,

View File

@ -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<FetchPageListPayload>,
) {
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: {

View File

@ -45,6 +45,7 @@ export type EventName =
| "DUPLICATE_API"
| "DUPLICATE_API_CLICK"
| "RUN_QUERY"
| "RUN_QUERY_CLICK"
| "DELETE_QUERY"
| "SAVE_QUERY"
| "MOVE_API"

View File

@ -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<string, PerfLog> = 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",
);
}
}