From 6b6e348203a4725a6e5c368b3950a4febf6b13ac Mon Sep 17 00:00:00 2001 From: Apeksha Bhosale <7846888+ApekshaBhosale@users.noreply.github.com> Date: Wed, 16 Apr 2025 11:45:47 +0530 Subject: [PATCH] chore: Move sentry to faro for client side (#40220) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description > [!TIP] > _Add a TL;DR when the description is longer than 500 words or extremely technical (helps the content, marketing, and DevRel team)._ > > _Please also include relevant motivation and context. List any dependencies that are required for this change. Add links to Notion, Figma or any other documents that might be relevant to the PR._ Fixes #`Issue Number` _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.All" ### :mag: Cypress test results > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: > Commit: cad55550d2e517bec0031fe4043ec76cfb4d67e3 > Cypress dashboard. > Tags: `@tag.All` > Spec: >
Fri, 11 Apr 2025 12:38:06 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No ## Summary by CodeRabbit - **Refactor** - Revamped the error reporting system across the platform with enhanced contextual logging for faster issue diagnosis. - **Chore** - Removed legacy error tracking integrations, streamlining overall error handling and system reliability. --- app/client/src/AppErrorBoundry.tsx | 4 +- .../components/UQIEditor/FormRender.tsx | 4 +- app/client/src/actions/pageActions.tsx | 11 +- .../api/helpers/validateJsonResponseMeta.ts | 5 +- .../handleMissingResponseMeta.ts | 5 +- .../failureHandlers/handleNotFoundError.ts | 4 +- .../handleUnauthorizedError.ts | 4 +- app/client/src/ce/sagas/PageSagas.tsx | 6 +- app/client/src/ce/utils/AnalyticsUtil.tsx | 6 +- app/client/src/components/SentryRoute.tsx | 3 +- .../CodeEditor/EvaluatedValuePopup.tsx | 6 +- .../editorComponents/CodeEditor/index.tsx | 5 +- .../EntityExplorerSidebar.tsx | 3 +- .../editorComponents/ErrorBoundry.tsx | 4 +- .../editorComponents/PropertyPaneSidebar.tsx | 3 +- .../PrimaryColumnsControl.tsx | 5 +- .../PrimaryColumnsControlV2.tsx | 6 +- .../PrimaryColumnsControlWDS.tsx | 5 +- .../propertyControls/TabControl.tsx | 13 ++- app/client/src/ee/sagas/index.tsx | 4 +- .../src/git/sagas/helpers/handleApiErrors.ts | 4 +- app/client/src/instrumentation/index.ts | 88 +++++++++------ .../src/instrumentation/sendFaroErrors.ts | 21 ++++ app/client/src/pages/AppIDE/AppIDE.tsx | 5 +- app/client/src/pages/AppViewer/index.tsx | 3 +- app/client/src/pages/Editor/Canvas.tsx | 4 +- .../ConversionButton.tsx | 3 +- .../SnapShotBannerCTA.tsx | 3 +- .../pages/Editor/CanvasPropertyPane/index.tsx | 4 +- .../Explorer/Entity/EntityProperties.tsx | 3 +- .../src/pages/Editor/JSEditor/index.tsx | 4 +- .../Editor/MultiSelectPropertyPane/index.tsx | 3 +- .../ThemePropertyPane/SettingSection.tsx | 3 +- .../Editor/ThemePropertyPane/ThemeCard.tsx | 3 +- .../Editor/ThemePropertyPane/ThemeEditor.tsx | 2 +- .../pages/Editor/ThemePropertyPane/index.tsx | 3 +- app/client/src/pages/UserAuth/Login.tsx | 9 +- app/client/src/pages/UserAuth/SignUp.tsx | 9 +- app/client/src/pages/UserAuth/VerifyUser.tsx | 6 +- app/client/src/pages/UserAuth/helpers.ts | 6 +- .../evaluationReducers/treeReducer.ts | 5 +- .../sagas/ActionExecution/PluginActionSaga.ts | 5 +- app/client/src/sagas/AppThemingSaga.tsx | 6 +- app/client/src/sagas/ErrorSagas.tsx | 4 +- app/client/src/sagas/EvalErrorHandler.ts | 24 +++-- app/client/src/sagas/EvaluationsSaga.ts | 6 +- app/client/src/sagas/FormEvaluationSaga.ts | 4 +- app/client/src/sagas/InitSagas.ts | 16 +-- app/client/src/sagas/ReplaySaga.ts | 8 +- app/client/src/sagas/WidgetLoadingSaga.ts | 4 +- app/client/src/sagas/layoutConversionSagas.ts | 4 +- app/client/src/utils/Analytics/sentry.ts | 101 +++--------------- .../getPathAndValueFromActionDiffObject.ts | 13 ++- app/client/src/utils/helpers.test.ts | 21 ++-- app/client/src/utils/helpers.tsx | 5 +- .../CurrencyInputWidget/widget/index.tsx | 8 +- .../fields/CurrencyInputField.tsx | 4 +- .../fields/useRegisterFieldValidity.ts | 6 +- .../MapChartWidget/component/utilities.ts | 6 +- .../widgets/PhoneInputWidget/widget/index.tsx | 4 +- .../cellComponents/InlineCellEditor.tsx | 4 +- .../cellComponents/PlainTextCell.tsx | 4 +- .../src/widgets/TabsMigrator/widget/index.tsx | 8 +- .../WDSCurrencyInputWidget/widget/index.tsx | 8 +- .../wds/WDSPhoneInputWidget/widget/index.tsx | 4 +- .../src/workers/Evaluation/errorModifier.ts | 4 +- 66 files changed, 276 insertions(+), 302 deletions(-) create mode 100644 app/client/src/instrumentation/sendFaroErrors.ts diff --git a/app/client/src/AppErrorBoundry.tsx b/app/client/src/AppErrorBoundry.tsx index 2331fc4d53..5ee4d1346f 100644 --- a/app/client/src/AppErrorBoundry.tsx +++ b/app/client/src/AppErrorBoundry.tsx @@ -1,10 +1,10 @@ import React, { Component } from "react"; import styled from "styled-components"; import AppCrashImage from "assets/images/404-image.png"; -import * as Sentry from "@sentry/react"; import log from "loglevel"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import { Button } from "@appsmith/ads"; +import captureException from "instrumentation/sendFaroErrors"; const Wrapper = styled.div` display: flex; @@ -32,7 +32,7 @@ class AppErrorBoundary extends Component { componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { log.error({ error, errorInfo }); - Sentry.captureException(error); + captureException(error, { errorName: "AppErrorBoundary" }); AnalyticsUtil.logEvent("APP_CRASH", { error, errorInfo }); this.setState({ hasError: true, diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/FormRender.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/FormRender.tsx index 7933a9336e..3bcfdba1b5 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/FormRender.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/FormRender.tsx @@ -9,7 +9,7 @@ import { Tag } from "@blueprintjs/core"; import styled from "styled-components"; import { UIComponentTypes } from "entities/Plugin"; import log from "loglevel"; -import * as Sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; import type { FormEvalOutput } from "reducers/evaluationReducers/formEvaluationReducer"; import { checkIfSectionCanRender, @@ -103,7 +103,7 @@ const FormRender = (props: Props) => { } } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "FormRenderError" }); return ( => { - // Update page *needs* id to be there. We found certain scenarios - // where this was not happening and capturing the error to know gather - // more info: https://github.com/appsmithorg/appsmith/issues/16435 if (!payload.id) { - Sentry.captureException( - new Error("Attempting to update page without page id"), - ); + captureException(new Error("Attempting to update page without page id"), { + errorName: "PageActions_UpdatePage", + }); } return { diff --git a/app/client/src/api/helpers/validateJsonResponseMeta.ts b/app/client/src/api/helpers/validateJsonResponseMeta.ts index 084e9b23f3..7afbea2633 100644 --- a/app/client/src/api/helpers/validateJsonResponseMeta.ts +++ b/app/client/src/api/helpers/validateJsonResponseMeta.ts @@ -1,4 +1,4 @@ -import * as Sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; import type { AxiosResponse } from "axios"; import { CONTENT_TYPE_HEADER_KEY } from "PluginActionEditor/constants/CommonApiConstants"; @@ -7,7 +7,8 @@ export const validateJsonResponseMeta = (response: AxiosResponse) => { response.headers[CONTENT_TYPE_HEADER_KEY] === "application/json" && !response.data.responseMeta ) { - Sentry.captureException(new Error("Api responded without response meta"), { + captureException(new Error("Api responded without response meta"), { + errorName: "ValidateJsonResponseMeta", contexts: { response: response.data }, }); } diff --git a/app/client/src/api/interceptors/response/failureHandlers/handleMissingResponseMeta.ts b/app/client/src/api/interceptors/response/failureHandlers/handleMissingResponseMeta.ts index 4153a6f868..9a7971c5e2 100644 --- a/app/client/src/api/interceptors/response/failureHandlers/handleMissingResponseMeta.ts +++ b/app/client/src/api/interceptors/response/failureHandlers/handleMissingResponseMeta.ts @@ -1,12 +1,13 @@ import type { AxiosError } from "axios"; -import * as Sentry from "@sentry/react"; import type { ApiResponse } from "api/types"; +import captureException from "instrumentation/sendFaroErrors"; export const handleMissingResponseMeta = async ( error: AxiosError, ) => { if (error.response?.data && !error.response.data.responseMeta) { - Sentry.captureException(new Error("Api responded without response meta"), { + captureException(new Error("Api responded without response meta"), { + errorName: "MissingResponseMeta", contexts: { response: { ...error.response.data } }, }); diff --git a/app/client/src/api/interceptors/response/failureHandlers/handleNotFoundError.ts b/app/client/src/api/interceptors/response/failureHandlers/handleNotFoundError.ts index 21e2aa42f3..a9b3e27b0b 100644 --- a/app/client/src/api/interceptors/response/failureHandlers/handleNotFoundError.ts +++ b/app/client/src/api/interceptors/response/failureHandlers/handleNotFoundError.ts @@ -3,10 +3,10 @@ import { ERROR_CODES, SERVER_ERROR_CODES, } from "ee/constants/ApiConstants"; -import * as Sentry from "@sentry/react"; import type { AxiosError } from "axios"; import type { ApiResponse } from "api/types"; import { is404orAuthPath } from "api/helpers"; +import captureException from "instrumentation/sendFaroErrors"; export async function handleNotFoundError(error: AxiosError) { if (is404orAuthPath()) return null; @@ -20,7 +20,7 @@ export async function handleNotFoundError(error: AxiosError) { (SERVER_ERROR_CODES.RESOURCE_NOT_FOUND.includes(errorData.error?.code) || SERVER_ERROR_CODES.UNABLE_TO_FIND_PAGE.includes(errorData?.error?.code)) ) { - Sentry.captureException(error); + captureException(error, { errorName: "NotFoundError" }); return Promise.reject({ ...error, diff --git a/app/client/src/api/interceptors/response/failureHandlers/handleUnauthorizedError.ts b/app/client/src/api/interceptors/response/failureHandlers/handleUnauthorizedError.ts index 343ec7022e..f096a3eeee 100644 --- a/app/client/src/api/interceptors/response/failureHandlers/handleUnauthorizedError.ts +++ b/app/client/src/api/interceptors/response/failureHandlers/handleUnauthorizedError.ts @@ -1,10 +1,10 @@ import store from "store"; import type { AxiosError } from "axios"; -import * as Sentry from "@sentry/react"; import { is404orAuthPath } from "api/helpers"; import { logoutUser } from "actions/userActions"; import { AUTH_LOGIN_URL } from "constants/routes"; import { API_STATUS_CODES, ERROR_CODES } from "ee/constants/ApiConstants"; +import captureException from "instrumentation/sendFaroErrors"; export const handleUnauthorizedError = async (error: AxiosError) => { if (is404orAuthPath()) return null; @@ -20,7 +20,7 @@ export const handleUnauthorizedError = async (error: AxiosError) => { }), ); - Sentry.captureException(error); + captureException(error, { errorName: "UnauthorizedError" }); return Promise.reject({ ...error, diff --git a/app/client/src/ce/sagas/PageSagas.tsx b/app/client/src/ce/sagas/PageSagas.tsx index 53481cfcc8..e4e2dbbe4a 100644 --- a/app/client/src/ce/sagas/PageSagas.tsx +++ b/app/client/src/ce/sagas/PageSagas.tsx @@ -97,7 +97,6 @@ import { clearEvalCache } from "../../sagas/EvaluationsSaga"; import { getQueryParams } from "utils/URLUtils"; import log from "loglevel"; import { migrateIncorrectDynamicBindingPathLists } from "utils/migrations/IncorrectDynamicBindingPathLists"; -import * as Sentry from "@sentry/react"; import { ERROR_CODES } from "ee/constants/ApiConstants"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import DEFAULT_TEMPLATE from "templates/default"; @@ -152,6 +151,7 @@ import { selectCombinedPreviewMode, selectGitApplicationCurrentBranch, } from "selectors/gitModSelectors"; +import captureException from "instrumentation/sendFaroErrors"; export const checkIfMigrationIsNeeded = ( fetchPageResponse?: FetchPageResponse, @@ -570,7 +570,9 @@ export function* savePageSaga(action: ReduxAction<{ isRetry?: boolean }>) { const { message } = incorrectBindingError; if (isRetry) { - Sentry.captureException(new Error("Failed to correct binding paths")); + captureException(new Error("Failed to correct binding paths"), { + errorName: "PageSagas_BindingPathCorrection", + }); yield put({ type: ReduxActionErrorTypes.FAILED_CORRECTING_BINDING_PATHS, payload: { diff --git a/app/client/src/ce/utils/AnalyticsUtil.tsx b/app/client/src/ce/utils/AnalyticsUtil.tsx index e3afcc5699..6bca41a7ea 100644 --- a/app/client/src/ce/utils/AnalyticsUtil.tsx +++ b/app/client/src/ce/utils/AnalyticsUtil.tsx @@ -9,7 +9,7 @@ import SegmentSingleton from "utils/Analytics/segment"; import MixpanelSingleton, { type SessionRecordingConfig, } from "utils/Analytics/mixpanel"; -import SentryUtil from "utils/Analytics/sentry"; +import FaroUtil from "utils/Analytics/sentry"; import SmartlookUtil from "utils/Analytics/smartlook"; import TrackedUser from "ee/utils/Analytics/trackedUser"; @@ -31,7 +31,7 @@ async function initialize( user: User, sessionRecordingConfig: SessionRecordingConfig, ) { - SentryUtil.init(); + // SentryUtil.init(); await SmartlookUtil.init(); segmentAnalytics = SegmentSingleton.getInstance(); @@ -94,7 +94,7 @@ async function identifyUser(userData: User, sendAdditionalData?: boolean) { await segmentAnalytics.identify(trackedUser.userId, userProperties); } - SentryUtil.identifyUser(trackedUser.userId, userData); + FaroUtil.identifyUser(trackedUser.userId, userData); if (trackedUser.email) { SmartlookUtil.identify(trackedUser.userId, trackedUser.email); diff --git a/app/client/src/components/SentryRoute.tsx b/app/client/src/components/SentryRoute.tsx index a5cac6a7f9..a7e3a865c9 100644 --- a/app/client/src/components/SentryRoute.tsx +++ b/app/client/src/components/SentryRoute.tsx @@ -1,4 +1,3 @@ -import * as Sentry from "@sentry/react"; import { Route } from "react-router"; -export const SentryRoute = Sentry.withSentryRouting(Route); +export const SentryRoute = Route; diff --git a/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx b/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx index d7e51b9dc7..0af92d97be 100644 --- a/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx +++ b/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx @@ -14,7 +14,6 @@ import { Classes, Collapse } from "@blueprintjs/core"; import { UNDEFINED_VALIDATION } from "utils/validation/common"; import copy from "copy-to-clipboard"; -import * as Sentry from "@sentry/react"; import type { CodeEditorExpected } from "components/editorComponents/CodeEditor/index"; import type { Indices } from "constants/Layers"; import { Layers } from "constants/Layers"; @@ -29,6 +28,7 @@ import { getPathNavigationUrl } from "selectors/navigationSelectors"; import { Button, Icon, Link, toast, Tooltip } from "@appsmith/ads"; import type { EvaluationError } from "utils/DynamicBindingUtils"; import { DEBUGGER_TAB_KEYS } from "../Debugger/constants"; +import captureException from "instrumentation/sendFaroErrors"; const modifiers: IPopoverSharedProps["modifiers"] = { offset: { @@ -290,8 +290,8 @@ export function PreparedStatementViewer(props: { const { parameters, value } = props.evaluatedValue; if (!value) { - Sentry.captureException("Prepared statement got no value", { - level: "debug", + captureException(new Error("Prepared statement got no value"), { + errorName: "PreparedStatementError", extra: { props }, }); diff --git a/app/client/src/components/editorComponents/CodeEditor/index.tsx b/app/client/src/components/editorComponents/CodeEditor/index.tsx index 705ecf6273..7e5c0f734a 100644 --- a/app/client/src/components/editorComponents/CodeEditor/index.tsx +++ b/app/client/src/components/editorComponents/CodeEditor/index.tsx @@ -78,7 +78,6 @@ import { Button } from "@appsmith/ads"; import "codemirror/addon/fold/brace-fold"; import "codemirror/addon/fold/foldgutter"; import "codemirror/addon/fold/foldgutter.css"; -import * as Sentry from "@sentry/react"; import type { EvaluationError, LintError } from "utils/DynamicBindingUtils"; import { getEvalErrorPath, isDynamicValue } from "utils/DynamicBindingUtils"; import { @@ -1919,6 +1918,4 @@ const mapDispatchToProps = (dispatch: any) => ({ resetActiveField: () => dispatch(resetActiveEditorField()), }); -export default Sentry.withProfiler( - connect(mapStateToProps, mapDispatchToProps)(CodeEditor), -); +export default connect(mapStateToProps, mapDispatchToProps)(CodeEditor); diff --git a/app/client/src/components/editorComponents/EntityExplorerSidebar.tsx b/app/client/src/components/editorComponents/EntityExplorerSidebar.tsx index 2cd1a4ee50..f3b9d6d871 100644 --- a/app/client/src/components/editorComponents/EntityExplorerSidebar.tsx +++ b/app/client/src/components/editorComponents/EntityExplorerSidebar.tsx @@ -1,4 +1,3 @@ -import * as Sentry from "@sentry/react"; import classNames from "classnames"; import React, { memo, @@ -173,4 +172,4 @@ export const EntityExplorerSidebar = memo(({ children }: Props) => { EntityExplorerSidebar.displayName = "EntityExplorerSidebar"; -export default Sentry.withProfiler(EntityExplorerSidebar); +export default EntityExplorerSidebar; diff --git a/app/client/src/components/editorComponents/ErrorBoundry.tsx b/app/client/src/components/editorComponents/ErrorBoundry.tsx index 44bf4bfb63..e0d1efcf7b 100644 --- a/app/client/src/components/editorComponents/ErrorBoundry.tsx +++ b/app/client/src/components/editorComponents/ErrorBoundry.tsx @@ -1,7 +1,7 @@ import React from "react"; import styled from "styled-components"; -import * as Sentry from "@sentry/react"; import * as log from "loglevel"; +import captureException from "instrumentation/sendFaroErrors"; import type { ReactNode, CSSProperties } from "react"; @@ -39,7 +39,7 @@ class ErrorBoundary extends React.Component { // eslint-disable-next-line @typescript-eslint/no-explicit-any componentDidCatch(error: any, errorInfo: any) { log.error({ error, errorInfo }); - Sentry.captureException(error); + captureException(error, { errorName: "ErrorBoundary" }); } render() { diff --git a/app/client/src/components/editorComponents/PropertyPaneSidebar.tsx b/app/client/src/components/editorComponents/PropertyPaneSidebar.tsx index bfa50589dc..102f54b765 100644 --- a/app/client/src/components/editorComponents/PropertyPaneSidebar.tsx +++ b/app/client/src/components/editorComponents/PropertyPaneSidebar.tsx @@ -6,7 +6,6 @@ import React, { useMemo, useRef, } from "react"; -import * as Sentry from "@sentry/react"; import { useSelector } from "react-redux"; import { getSelectedWidgets } from "selectors/ui"; import WidgetPropertyPane from "pages/Editor/PropertyPane"; @@ -104,4 +103,4 @@ export const PropertyPaneSidebar = memo(() => { PropertyPaneSidebar.displayName = "PropertyPaneSidebar"; -export default Sentry.withProfiler(PropertyPaneSidebar); +export default PropertyPaneSidebar; diff --git a/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx b/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx index e820748620..58800b9d1c 100644 --- a/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx +++ b/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx @@ -2,7 +2,6 @@ import React, { Component } from "react"; import type { AppState } from "ee/reducers"; import { connect } from "react-redux"; import type { Placement } from "popper.js"; -import * as Sentry from "@sentry/react"; import _ from "lodash"; import type { ControlProps } from "./BaseControl"; import BaseControl from "./BaseControl"; @@ -407,6 +406,6 @@ const mapStateToProps = (state: AppState): ReduxStateProps => ({ datasources: state.entities.datasources, }); -const EvaluatedValuePopupWrapper = Sentry.withProfiler( - connect(mapStateToProps)(EvaluatedValuePopupWrapperClass), +const EvaluatedValuePopupWrapper = connect(mapStateToProps)( + EvaluatedValuePopupWrapperClass, ); diff --git a/app/client/src/components/propertyControls/PrimaryColumnsControlV2.tsx b/app/client/src/components/propertyControls/PrimaryColumnsControlV2.tsx index 10c965efc9..d37229a9e8 100644 --- a/app/client/src/components/propertyControls/PrimaryColumnsControlV2.tsx +++ b/app/client/src/components/propertyControls/PrimaryColumnsControlV2.tsx @@ -1,5 +1,5 @@ import type { AppState } from "ee/reducers"; -import * as Sentry from "@sentry/react"; + import type { CodeEditorExpected } from "components/editorComponents/CodeEditor"; import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; import EvaluatedValuePopup from "components/editorComponents/CodeEditor/EvaluatedValuePopup"; @@ -548,6 +548,6 @@ const mapStateToProps = ( }; }; -const EvaluatedValuePopupWrapper = Sentry.withProfiler( - connect(mapStateToProps)(EvaluatedValuePopupWrapperClass), +const EvaluatedValuePopupWrapper = connect(mapStateToProps)( + EvaluatedValuePopupWrapperClass, ); diff --git a/app/client/src/components/propertyControls/PrimaryColumnsControlWDS.tsx b/app/client/src/components/propertyControls/PrimaryColumnsControlWDS.tsx index 51906284e1..3684d74cd3 100644 --- a/app/client/src/components/propertyControls/PrimaryColumnsControlWDS.tsx +++ b/app/client/src/components/propertyControls/PrimaryColumnsControlWDS.tsx @@ -2,7 +2,6 @@ import React, { Component } from "react"; import type { AppState } from "ee/reducers"; import { connect } from "react-redux"; import type { Placement } from "popper.js"; -import * as Sentry from "@sentry/react"; import _, { toString } from "lodash"; import type { ControlProps } from "./BaseControl"; import BaseControl from "./BaseControl"; @@ -497,6 +496,6 @@ const mapStateToProps = ( }; }; -const EvaluatedValuePopupWrapper = Sentry.withProfiler( - connect(mapStateToProps)(EvaluatedValuePopupWrapperClass), +const EvaluatedValuePopupWrapper = connect(mapStateToProps)( + EvaluatedValuePopupWrapperClass, ); diff --git a/app/client/src/components/propertyControls/TabControl.tsx b/app/client/src/components/propertyControls/TabControl.tsx index efb966424e..94500969ba 100644 --- a/app/client/src/components/propertyControls/TabControl.tsx +++ b/app/client/src/components/propertyControls/TabControl.tsx @@ -10,7 +10,7 @@ import isString from "lodash/isString"; import isUndefined from "lodash/isUndefined"; import includes from "lodash/includes"; import map from "lodash/map"; -import * as Sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; import { useDispatch } from "react-redux"; import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; import { DraggableListControl } from "pages/Editor/PropertyPane/DraggableListControl"; @@ -131,10 +131,13 @@ class TabControl extends BaseControl { return parsedData; } catch (error) { - Sentry.captureException({ - message: "Tab Migration Failed", - oldData: this.props.propertyValue, - }); + captureException( + { + message: "Tab Migration Failed", + oldData: this.props.propertyValue, + }, + { errorName: "TabMigrationError" }, + ); } } else { return this.props.propertyValue; diff --git a/app/client/src/ee/sagas/index.tsx b/app/client/src/ee/sagas/index.tsx index ea98eb7e15..1de9786084 100644 --- a/app/client/src/ee/sagas/index.tsx +++ b/app/client/src/ee/sagas/index.tsx @@ -3,7 +3,7 @@ import { sagas as CE_Sagas } from "ce/sagas"; import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; import { call, all, spawn, race, take } from "redux-saga/effects"; import log from "loglevel"; -import * as sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; const sagasArr = [...CE_Sagas]; @@ -22,7 +22,7 @@ export function* rootSaga(sagasToRun = sagasArr): any { break; } catch (e) { log.error(e); - sentry.captureException(e); + captureException(e, { errorName: "RootSagaError" }); } } }), diff --git a/app/client/src/git/sagas/helpers/handleApiErrors.ts b/app/client/src/git/sagas/helpers/handleApiErrors.ts index 099711ce47..fd7fd69e91 100644 --- a/app/client/src/git/sagas/helpers/handleApiErrors.ts +++ b/app/client/src/git/sagas/helpers/handleApiErrors.ts @@ -1,4 +1,4 @@ -import { captureException } from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; import type { ApiResponse } from "api/types"; import log from "loglevel"; @@ -22,7 +22,7 @@ export default function handleApiErrors(error?: Error, response?: ApiResponse) { } } else { log.error(error); - captureException(error); + captureException(error, { errorName: "GitApiError" }); } return apiError; diff --git a/app/client/src/instrumentation/index.ts b/app/client/src/instrumentation/index.ts index 2eaacdd870..fb0d7629cf 100644 --- a/app/client/src/instrumentation/index.ts +++ b/app/client/src/instrumentation/index.ts @@ -23,6 +23,12 @@ import { import log from "loglevel"; import { isTracingEnabled } from "instrumentation/utils"; +declare global { + interface Window { + faro: Faro | null; + } +} + const { appVersion, observability } = getAppsmithConfigs(); const { deploymentName, serviceInstanceId, serviceName, tracingUrl } = observability; @@ -36,29 +42,41 @@ if (isTracingEnabled()) { ? InternalLoggerLevel.ERROR : InternalLoggerLevel.OFF; - faro = initializeFaro({ - url: tracingUrl, - app: { - name: serviceName, - version: appVersion.sha, - environment: deploymentName, - }, - instrumentations: [new ReactIntegration(), ...getWebInstrumentations()], - ignoreUrls, - consoleInstrumentation: { - consoleErrorAsLog: true, - }, - trackResources: true, - trackWebVitalsAttribution: true, - internalLoggerLevel, - sessionTracking: { - generateSessionId: () => { - // Disabling session tracing will not send any instrumentation data to the grafana backend - // Instead, hardcoding the session id to a constant value to indirecly disable session tracing - return "SESSION_ID"; - }, - }, - }); + try { + if (!window.faro) { + faro = initializeFaro({ + url: tracingUrl, + app: { + name: serviceName, + version: appVersion.sha, + environment: deploymentName, + }, + instrumentations: [ + new ReactIntegration(), + ...getWebInstrumentations({}), + ], + ignoreUrls, + consoleInstrumentation: { + consoleErrorAsLog: false, + }, + trackResources: true, + trackWebVitalsAttribution: true, + internalLoggerLevel, + sessionTracking: { + generateSessionId: () => { + // Disabling session tracing will not send any instrumentation data to the grafana backend + // Instead, hardcoding the session id to a constant value to indirecly disable session tracing + return "SESSION_ID"; + }, + }, + }); + } + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + } + + faro = window.faro; const tracerProvider = new WebTracerProvider({ resource: new Resource({ @@ -68,18 +86,20 @@ if (isTracingEnabled()) { }), }); - tracerProvider.addSpanProcessor( - new FaroSessionSpanProcessor( - new BatchSpanProcessor(new FaroTraceExporter({ ...faro })), - faro.metas, - ), - ); + if (faro) { + tracerProvider.addSpanProcessor( + new FaroSessionSpanProcessor( + new BatchSpanProcessor(new FaroTraceExporter({ ...faro })), + faro.metas, + ), + ); - tracerProvider.register({ - contextManager: new ZoneContextManager(), - }); + tracerProvider.register({ + contextManager: new ZoneContextManager(), + }); - faro.api.initOTEL(trace, context); + faro.api.initOTEL(trace, context); + } } export const getTraceAndContext = () => { @@ -91,3 +111,5 @@ export const getTraceAndContext = () => { // return default OTEL context and trace if faro is not initialized return faro.api.getOTEL() || { trace, context }; }; + +export { faro }; diff --git a/app/client/src/instrumentation/sendFaroErrors.ts b/app/client/src/instrumentation/sendFaroErrors.ts new file mode 100644 index 0000000000..13e4677641 --- /dev/null +++ b/app/client/src/instrumentation/sendFaroErrors.ts @@ -0,0 +1,21 @@ +import { v4 as uuidv4 } from "uuid"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function captureException(exception: any, hint?: any): string { + const eventId = uuidv4(); + const context = hint || {}; + + try { + window.faro?.api.pushError( + exception instanceof Error ? exception : new Error(String(exception)), + { type: "error", context: context }, + ); + } catch (error) { + // eslint-disable-next-line + console.error(error); + } + + return eventId; +} + +export default captureException; diff --git a/app/client/src/pages/AppIDE/AppIDE.tsx b/app/client/src/pages/AppIDE/AppIDE.tsx index 3f96f5b229..12ef00c9b1 100644 --- a/app/client/src/pages/AppIDE/AppIDE.tsx +++ b/app/client/src/pages/AppIDE/AppIDE.tsx @@ -20,7 +20,6 @@ import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper"; import { getCurrentUser } from "selectors/usersSelectors"; import type { User } from "constants/userConstants"; import RequestConfirmationModal from "pages/Editor/RequestConfirmationModal"; -import * as Sentry from "@sentry/react"; import { getTheme, ThemeMode } from "selectors/themeSelectors"; import { ThemeProvider } from "styled-components"; import type { Theme } from "constants/DefaultTheme"; @@ -222,6 +221,4 @@ const mapDispatchToProps = (dispatch: any) => { }; }; -export default withRouter( - connect(mapStateToProps, mapDispatchToProps)(Sentry.withProfiler(Editor)), -); +export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Editor)); diff --git a/app/client/src/pages/AppViewer/index.tsx b/app/client/src/pages/AppViewer/index.tsx index 1fdb6e1250..a9db0e1e25 100644 --- a/app/client/src/pages/AppViewer/index.tsx +++ b/app/client/src/pages/AppViewer/index.tsx @@ -15,7 +15,6 @@ import { } from "selectors/appViewSelectors"; import EditorContextProvider from "components/editorComponents/EditorContextProvider"; import AppViewerPageContainer from "./AppViewerPageContainer"; -import * as Sentry from "@sentry/react"; import { getCurrentPageDescription, getIsAutoLayout, @@ -259,4 +258,4 @@ function AppViewer(props: Props) { return {renderChildren()}; } -export default withRouter(Sentry.withProfiler(AppViewer)); +export default withRouter(AppViewer); diff --git a/app/client/src/pages/Editor/Canvas.tsx b/app/client/src/pages/Editor/Canvas.tsx index b0d1140e50..96d5c0534c 100644 --- a/app/client/src/pages/Editor/Canvas.tsx +++ b/app/client/src/pages/Editor/Canvas.tsx @@ -1,7 +1,6 @@ import log from "loglevel"; import React, { useCallback } from "react"; import styled from "styled-components"; -import * as Sentry from "@sentry/react"; import { useDispatch, useSelector } from "react-redux"; import type { CanvasWidgetStructure } from "WidgetProvider/constants"; import useWidgetFocus from "utils/hooks/useWidgetFocus"; @@ -20,6 +19,7 @@ import { getAppThemeSettings } from "ee/selectors/applicationSelectors"; import CodeModeTooltip from "pages/Editor/WidgetsEditor/components/CodeModeTooltip"; import { getIsAnvilLayout } from "layoutSystems/anvil/integrations/selectors"; import { focusWidget } from "actions/widgetActions"; +import captureException from "instrumentation/sendFaroErrors"; interface CanvasProps { widgetsStructure: CanvasWidgetStructure; @@ -120,7 +120,7 @@ const Canvas = (props: CanvasProps) => { return renderChildren(); } catch (error) { log.error("Error rendering DSL", error); - Sentry.captureException(error); + captureException(error, { errorName: "Canvas" }); return null; } diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx b/app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx index 46f3fb9282..71ccd7b31d 100644 --- a/app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx @@ -1,4 +1,3 @@ -import * as Sentry from "@sentry/react"; import React, { useRef } from "react"; import { Button, @@ -108,4 +107,4 @@ function ConversionButton() { ConversionButton.displayName = "ConversionButton"; -export default Sentry.withProfiler(ConversionButton); +export default ConversionButton; diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotBannerCTA.tsx b/app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotBannerCTA.tsx index d5e105ffa5..7dee5c4688 100644 --- a/app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotBannerCTA.tsx +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotBannerCTA.tsx @@ -1,4 +1,3 @@ -import * as Sentry from "@sentry/react"; import React, { useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import type { AppState } from "ee/reducers"; @@ -130,4 +129,4 @@ export function SnapShotBannerCTA() { SnapShotBannerCTA.displayName = "SnapShotBannerCTA"; -export default Sentry.withProfiler(SnapShotBannerCTA); +export default SnapShotBannerCTA; diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index edc4fa2bc1..e40a61ccd6 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -1,5 +1,3 @@ -import * as Sentry from "@sentry/react"; - import React from "react"; import ConversionButton from "../CanvasLayoutConversion/ConversionButton"; import styled from "styled-components"; @@ -47,4 +45,4 @@ export function CanvasPropertyPane() { CanvasPropertyPane.displayName = "CanvasPropertyPane"; -export default Sentry.withProfiler(CanvasPropertyPane); +export default CanvasPropertyPane; diff --git a/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx b/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx index a25f940226..b46a066aa1 100644 --- a/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx +++ b/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useEffect } from "react"; import EntityProperty from "./EntityProperty"; import { useDispatch, useSelector } from "react-redux"; -import * as Sentry from "@sentry/react"; import type { AppState } from "ee/reducers"; import classNames from "classnames"; import styled from "styled-components"; @@ -177,4 +176,4 @@ export function EntityProperties() { ); } -export default Sentry.withProfiler(EntityProperties); +export default EntityProperties; diff --git a/app/client/src/pages/Editor/JSEditor/index.tsx b/app/client/src/pages/Editor/JSEditor/index.tsx index bc696590ca..33aa5ffede 100644 --- a/app/client/src/pages/Editor/JSEditor/index.tsx +++ b/app/client/src/pages/Editor/JSEditor/index.tsx @@ -2,7 +2,6 @@ import React, { useMemo } from "react"; import { useRouteMatch } from "react-router-dom"; import { useDispatch, useSelector } from "react-redux"; import JsEditorForm from "./Form"; -import * as Sentry from "@sentry/react"; import { getJSCollectionDataByBaseId } from "selectors/editorSelectors"; import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper"; import Spinner from "components/editorComponents/Spinner"; @@ -12,6 +11,7 @@ import AppJSEditorContextMenu from "./AppJSEditorContextMenu"; import { updateFunctionProperty } from "actions/jsPaneActions"; import type { OnUpdateSettingsProps } from "./JSEditorToolbar"; import { saveJSObjectName } from "actions/jsActionActions"; + const LoadingContainer = styled(CenteredWrapper)` height: 50%; `; @@ -67,4 +67,4 @@ function JSEditor() { return ; } -export default Sentry.withProfiler(JSEditor); +export default JSEditor; diff --git a/app/client/src/pages/Editor/MultiSelectPropertyPane/index.tsx b/app/client/src/pages/Editor/MultiSelectPropertyPane/index.tsx index 1da57618fd..95312c7a51 100644 --- a/app/client/src/pages/Editor/MultiSelectPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/MultiSelectPropertyPane/index.tsx @@ -1,5 +1,4 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { createMessage, MULTI_SELECT_PROPERTY_PANE_MESSAGE, @@ -57,4 +56,4 @@ function MultiSelectPropertyPane() { MultiSelectPropertyPane.displayName = "MultiSelectPropertyPane"; -export default Sentry.withProfiler(MultiSelectPropertyPane); +export default MultiSelectPropertyPane; diff --git a/app/client/src/pages/Editor/ThemePropertyPane/SettingSection.tsx b/app/client/src/pages/Editor/ThemePropertyPane/SettingSection.tsx index 8496125b85..7ab12d3a1e 100644 --- a/app/client/src/pages/Editor/ThemePropertyPane/SettingSection.tsx +++ b/app/client/src/pages/Editor/ThemePropertyPane/SettingSection.tsx @@ -1,4 +1,3 @@ -import * as Sentry from "@sentry/react"; import type { ComponentPropsWithoutRef } from "react"; import React, { useState } from "react"; import { Collapse } from "@blueprintjs/core"; @@ -56,4 +55,4 @@ export function SettingSection(props: SettingSectionProps) { SettingSection.displayName = "SettingSection"; -export default Sentry.withProfiler(SettingSection); +export default SettingSection; diff --git a/app/client/src/pages/Editor/ThemePropertyPane/ThemeCard.tsx b/app/client/src/pages/Editor/ThemePropertyPane/ThemeCard.tsx index afde6fddf6..434148ac75 100644 --- a/app/client/src/pages/Editor/ThemePropertyPane/ThemeCard.tsx +++ b/app/client/src/pages/Editor/ThemePropertyPane/ThemeCard.tsx @@ -1,7 +1,6 @@ import { last } from "lodash"; import classNames from "classnames"; import styled from "styled-components"; -import * as Sentry from "@sentry/react"; import React, { useState } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -246,4 +245,4 @@ export function ThemeCard(props: ThemeCard) { ThemeCard.displayName = "ThemeCard"; -export default Sentry.withProfiler(ThemeCard); +export default ThemeCard; diff --git a/app/client/src/pages/Editor/ThemePropertyPane/ThemeEditor.tsx b/app/client/src/pages/Editor/ThemePropertyPane/ThemeEditor.tsx index f6b708d09c..445f77a5dc 100644 --- a/app/client/src/pages/Editor/ThemePropertyPane/ThemeEditor.tsx +++ b/app/client/src/pages/Editor/ThemePropertyPane/ThemeEditor.tsx @@ -3,7 +3,7 @@ import { get, startCase } from "lodash"; import { useDispatch, useSelector } from "react-redux"; import React, { useCallback } from "react"; -import ThemeCard from "./ThemeCard"; +import { ThemeCard } from "./ThemeCard"; import { AppThemingMode, getAppThemingStack, diff --git a/app/client/src/pages/Editor/ThemePropertyPane/index.tsx b/app/client/src/pages/Editor/ThemePropertyPane/index.tsx index 72d303a8f5..94d1e177d2 100644 --- a/app/client/src/pages/Editor/ThemePropertyPane/index.tsx +++ b/app/client/src/pages/Editor/ThemePropertyPane/index.tsx @@ -1,5 +1,4 @@ import React, { useMemo } from "react"; -import * as Sentry from "@sentry/react"; import { last } from "lodash"; import ThemeEditor from "./ThemeEditor"; @@ -47,4 +46,4 @@ export function ThemePropertyPane() { ThemePropertyPane.displayName = "ThemePropertyPane"; -export default Sentry.withProfiler(ThemePropertyPane); +export default ThemePropertyPane; diff --git a/app/client/src/pages/UserAuth/Login.tsx b/app/client/src/pages/UserAuth/Login.tsx index 93cc583ca6..602669ff7c 100644 --- a/app/client/src/pages/UserAuth/Login.tsx +++ b/app/client/src/pages/UserAuth/Login.tsx @@ -51,8 +51,8 @@ import Helmet from "react-helmet"; import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; import { getHTMLPageTitle } from "ee/utils/BusinessFeatures/brandingPageHelpers"; -import * as Sentry from "@sentry/react"; import CsrfTokenInput from "pages/UserAuth/CsrfTokenInput"; +import captureException from "instrumentation/sendFaroErrors"; import { getSafeErrorMessage } from "ee/constants/approvedErrorMessages"; const validate = (values: LoginFormValues, props: ValidateProps) => { @@ -116,12 +116,7 @@ export function Login(props: LoginFormProps) { if (queryParams.get("error")) { errorMessage = queryParams.get("message") || queryParams.get("error") || ""; showError = true; - Sentry.captureException("Login failed", { - level: "error", - extra: { - error: new Error(errorMessage), - }, - }); + captureException(new Error(errorMessage), { errorName: "LoginError" }); } let loginURL = "/api/v1/" + LOGIN_SUBMIT_PATH; diff --git a/app/client/src/pages/UserAuth/SignUp.tsx b/app/client/src/pages/UserAuth/SignUp.tsx index 3eb4203b28..1112ee89af 100644 --- a/app/client/src/pages/UserAuth/SignUp.tsx +++ b/app/client/src/pages/UserAuth/SignUp.tsx @@ -60,11 +60,11 @@ import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; import { getHTMLPageTitle } from "ee/utils/BusinessFeatures/brandingPageHelpers"; import log from "loglevel"; import { SELF_HOSTING_DOC } from "constants/ThirdPartyConstants"; -import * as Sentry from "@sentry/react"; import CsrfTokenInput from "pages/UserAuth/CsrfTokenInput"; import { useIsCloudBillingEnabled } from "hooks"; import { isLoginHostname } from "utils/cloudBillingUtils"; import { getIsAiAgentFlowEnabled } from "ee/selectors/aiAgentSelectors"; +import captureException from "instrumentation/sendFaroErrors"; import { getSafeErrorMessage } from "ee/constants/approvedErrorMessages"; declare global { @@ -145,11 +145,8 @@ export function SignUp(props: SignUpFormProps) { if (queryParams.get("error")) { errorMessage = queryParams.get("error") || ""; showError = true; - Sentry.captureException("Sign up failed", { - level: "error", - extra: { - error: new Error(errorMessage), - }, + captureException(new Error(errorMessage), { + errorName: "SignUp", }); } diff --git a/app/client/src/pages/UserAuth/VerifyUser.tsx b/app/client/src/pages/UserAuth/VerifyUser.tsx index 45d94c5a44..80af8a62df 100644 --- a/app/client/src/pages/UserAuth/VerifyUser.tsx +++ b/app/client/src/pages/UserAuth/VerifyUser.tsx @@ -2,11 +2,11 @@ import React, { useEffect } from "react"; import Container from "./Container"; import type { RouteComponentProps } from "react-router-dom"; import { Spinner } from "@appsmith/ads"; -import * as Sentry from "@sentry/react"; import { EMAIL_VERIFICATION_PATH } from "ee/constants/ApiConstants"; import { Redirect } from "react-router-dom"; import { VerificationErrorType } from "./VerificationError"; import CsrfTokenInput from "pages/UserAuth/CsrfTokenInput"; +import captureException from "instrumentation/sendFaroErrors"; const VerifyUser = ( props: RouteComponentProps<{ @@ -24,7 +24,9 @@ const VerifyUser = ( useEffect(() => { if (!token || !email) { - Sentry.captureMessage("User email verification link is damaged"); + captureException(new Error("User email verification link is damaged"), { + errorName: "VerificationLinkDamaged", + }); } const formElement: HTMLFormElement = document.getElementById( diff --git a/app/client/src/pages/UserAuth/helpers.ts b/app/client/src/pages/UserAuth/helpers.ts index aa901b8ca3..389a7a148c 100644 --- a/app/client/src/pages/UserAuth/helpers.ts +++ b/app/client/src/pages/UserAuth/helpers.ts @@ -2,10 +2,10 @@ import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; import { SubmissionError } from "redux-form"; import { useCallback, useEffect, useState } from "react"; import type { Dispatch } from "redux"; -import * as Sentry from "@sentry/react"; import UserApi from "ee/api/UserApi"; import { toast } from "@appsmith/ads"; import type { ApiResponse } from "../../api/ApiResponses"; +import captureException from "instrumentation/sendFaroErrors"; export interface LoginFormValues { username?: string; @@ -95,7 +95,9 @@ export const useResendEmailVerification = ( if (!email) { const errorMessage = "Email not found for retry verification"; - Sentry.captureMessage(errorMessage); + captureException(new Error(errorMessage), { + errorName: "EmailVerificationRetryError", + }); toast.show(errorMessage, { kind: "error" }); return; diff --git a/app/client/src/reducers/evaluationReducers/treeReducer.ts b/app/client/src/reducers/evaluationReducers/treeReducer.ts index a8be85d9f1..fc1f3f60a1 100644 --- a/app/client/src/reducers/evaluationReducers/treeReducer.ts +++ b/app/client/src/reducers/evaluationReducers/treeReducer.ts @@ -3,7 +3,7 @@ import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; import { applyChange, type Diff } from "deep-diff"; import type { DataTree } from "entities/DataTree/dataTreeTypes"; import { createImmerReducer } from "utils/ReducerUtils"; -import * as Sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; export type EvaluatedTreeState = DataTree; @@ -32,7 +32,8 @@ const evaluatedTreeReducer = createImmerReducer(initialState, { applyChange(state, undefined, update); } catch (e) { - Sentry.captureException(e, { + captureException(e, { + errorName: "TreeReducer", extra: { update, updateLength: updates.length, diff --git a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts index c21366784f..df5e52e894 100644 --- a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts @@ -7,7 +7,6 @@ import { take, takeLatest, } from "redux-saga/effects"; -import * as Sentry from "@sentry/react"; import { clearActionResponse, executePageLoadActions, @@ -170,6 +169,7 @@ import { selectGitOpsModalOpen, } from "selectors/gitModSelectors"; import { createActionExecutionResponse } from "./PluginActionSagaUtils"; +import captureException from "instrumentation/sendFaroErrors"; interface FilePickerInstumentationObject { numberOfFiles: number; @@ -989,11 +989,12 @@ function* executeOnPageLoadJSAction(pageAction: PageAction) { ); if (!collection) { - Sentry.captureException( + captureException( new Error( "Collection present in layoutOnLoadActions but no collection exists ", ), { + errorName: "MissingJSCollection", extra: { collectionId, actionId: pageAction.id, diff --git a/app/client/src/sagas/AppThemingSaga.tsx b/app/client/src/sagas/AppThemingSaga.tsx index a986a0c31d..5d1054aed5 100644 --- a/app/client/src/sagas/AppThemingSaga.tsx +++ b/app/client/src/sagas/AppThemingSaga.tsx @@ -44,7 +44,7 @@ import { selectApplicationVersion, } from "selectors/editorSelectors"; import { find } from "lodash"; -import * as Sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; import { getAllPageIdentities } from "./selectors"; import type { SagaIterator } from "@redux-saga/types"; import type { AxiosPromise } from "axios"; @@ -126,8 +126,8 @@ export function* fetchAppSelectedTheme( payload: response.data, }); } else { - Sentry.captureException("Unable to fetch the selected theme", { - level: "fatal", + captureException(new Error("Unable to fetch the selected theme"), { + errorName: "ThemeFetchError", extra: { pageIdentities, applicationId, diff --git a/app/client/src/sagas/ErrorSagas.tsx b/app/client/src/sagas/ErrorSagas.tsx index 7e3a798749..176ace8f11 100644 --- a/app/client/src/sagas/ErrorSagas.tsx +++ b/app/client/src/sagas/ErrorSagas.tsx @@ -34,7 +34,6 @@ import { } from "ee/constants/messages"; import store from "store"; -import * as Sentry from "@sentry/react"; import { getLoginUrl } from "ee/utils/adminSettingsHelpers"; import type { PluginErrorDetails } from "api/ActionAPI"; import showToast from "sagas/ToastSagas"; @@ -42,6 +41,7 @@ import AppsmithConsole from "../utils/AppsmithConsole"; import type { SourceEntity } from "../entities/AppsmithConsole"; import { getAppMode } from "ee/selectors/applicationSelectors"; import { APP_MODE } from "../entities/App"; +import captureException from "instrumentation/sendFaroErrors"; const shouldShowToast = (action: string) => { return action in toastMessageErrorTypes; @@ -298,7 +298,7 @@ export function* errorSaga(errorAction: ReduxAction) { break; } case ErrorEffectTypes.LOG_TO_SENTRY: { - yield call(Sentry.captureException, error); + yield call(captureException, error, { errorName: "ErrorSagaError" }); break; } } diff --git a/app/client/src/sagas/EvalErrorHandler.ts b/app/client/src/sagas/EvalErrorHandler.ts index 812c29e916..46777a3efc 100644 --- a/app/client/src/sagas/EvalErrorHandler.ts +++ b/app/client/src/sagas/EvalErrorHandler.ts @@ -17,7 +17,7 @@ import { get } from "lodash"; import LOG_TYPE from "entities/AppsmithConsole/logtype"; import { select } from "redux-saga/effects"; import AppsmithConsole from "utils/AppsmithConsole"; -import * as Sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import { createMessage, @@ -232,7 +232,8 @@ export function* evalErrorHandler( if (error.context.logToSentry) { // Send the generic error message to sentry for better grouping - Sentry.captureException(new Error(error.message), { + captureException(new Error(error.message), { + errorName: "CyclicalDependencyError", tags: { node, entityType, @@ -264,22 +265,23 @@ export function* evalErrorHandler( kind: "error", }); log.error(error); - Sentry.captureException(error); + captureException(error, { errorName: "EvalTreeError" }); break; } case EvalErrorTypes.BAD_UNEVAL_TREE_ERROR: { log.error(error); - Sentry.captureException(error); + captureException(error, { errorName: "BadUnevalTreeError" }); break; } case EvalErrorTypes.EVAL_PROPERTY_ERROR: { - Sentry.captureException(error); + captureException(error, { errorName: "EvalPropertyError" }); log.error(error); break; } case EvalErrorTypes.CLONE_ERROR: { log.debug(error); - Sentry.captureException(new Error(error.message), { + captureException(new Error(error.message), { + errorName: "CloneError", extra: { request: error.context, }, @@ -294,18 +296,22 @@ export function* evalErrorHandler( text: `${error.message} at: ${error.context?.propertyPath}`, }); log.error(error); - Sentry.captureException(error); + captureException(error, { + errorName: "ParseJSError", + entity: error.context, + }); break; } case EvalErrorTypes.EXTRACT_DEPENDENCY_ERROR: { - Sentry.captureException(new Error(error.message), { + captureException(new Error(error.message), { + errorName: "ExtractDependencyError", extra: error.context, }); break; } default: { log.error(error); - Sentry.captureException(error); + captureException(error, { errorName: "UnknownEvalError" }); } } }); diff --git a/app/client/src/sagas/EvaluationsSaga.ts b/app/client/src/sagas/EvaluationsSaga.ts index 8bbe2180b3..82fec84a8d 100644 --- a/app/client/src/sagas/EvaluationsSaga.ts +++ b/app/client/src/sagas/EvaluationsSaga.ts @@ -30,7 +30,6 @@ import { PropertyEvaluationErrorType } from "utils/DynamicBindingUtils"; import { EVAL_WORKER_ACTIONS } from "ee/workers/Evaluation/evalWorkerActions"; import log from "loglevel"; import type { WidgetProps } from "widgets/BaseWidget"; -import * as Sentry from "@sentry/react"; import type { Action } from "redux"; import { EVAL_AND_LINT_REDUX_ACTIONS, @@ -123,6 +122,7 @@ import type { AffectedJSObjects, EvaluationReduxAction, } from "actions/EvaluationReduxActionTypes"; +import captureException from "instrumentation/sendFaroErrors"; const APPSMITH_CONFIGS = getAppsmithConfigs(); @@ -932,7 +932,7 @@ export function* evaluateActionSelectorFieldSaga(action: any) { ); } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "EvaluationError" }); } } @@ -1003,7 +1003,7 @@ export default function* evaluationSagaListeners() { yield call(evaluationChangeListenerSaga); } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "EvaluationError" }); } } } diff --git a/app/client/src/sagas/FormEvaluationSaga.ts b/app/client/src/sagas/FormEvaluationSaga.ts index 4c324c7de0..c5f9cbeff5 100644 --- a/app/client/src/sagas/FormEvaluationSaga.ts +++ b/app/client/src/sagas/FormEvaluationSaga.ts @@ -11,7 +11,7 @@ import { import type { ReduxAction } from "actions/ReduxActionTypes"; import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; import log from "loglevel"; -import * as Sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; import { getFormEvaluationState } from "selectors/formSelectors"; import { evalFormConfig } from "./EvaluationsSaga"; import type { @@ -372,7 +372,7 @@ export default function* formEvaluationChangeListener() { yield call(formEvaluationChangeListenerSaga); } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "FormEvaluationError" }); } } } diff --git a/app/client/src/sagas/InitSagas.ts b/app/client/src/sagas/InitSagas.ts index edf4e42df0..34f88ff154 100644 --- a/app/client/src/sagas/InitSagas.ts +++ b/app/client/src/sagas/InitSagas.ts @@ -20,7 +20,6 @@ import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; import { resetApplicationWidgets, resetPageList } from "actions/pageActions"; import { resetCurrentApplication } from "ee/actions/applicationActions"; import log from "loglevel"; -import * as Sentry from "@sentry/react"; import { resetRecentEntities } from "actions/globalSearchActions"; import { @@ -93,6 +92,7 @@ import type { ApplicationPayload } from "entities/Application"; import type { Page } from "entities/Page"; import type { PACKAGE_PULL_STATUS } from "ee/constants/ModuleConstants"; import { validateSessionToken } from "utils/SessionUtils"; +import captureException from "instrumentation/sendFaroErrors"; export const URL_CHANGE_ACTIONS = [ ReduxActionTypes.CURRENT_APPLICATION_NAME_UPDATE, @@ -278,10 +278,12 @@ export function* getInitResponses({ ReduxActionTypes.END_CONSOLIDATED_PAGE_LOAD, shouldInitialiseUserDetails, ); - Sentry.captureMessage( - `consolidated api failure for ${JSON.stringify( - params, - )} errored message response ${e}`, + captureException( + new Error(`consolidated api failure for ${JSON.stringify(params)}`), + { + errorName: "ConsolidatedApiError", + extra: { error: e }, + }, ); throw new PageNotFoundError(`Cannot find page with base id: ${basePageId}`); } @@ -372,7 +374,7 @@ export function* startAppEngine(action: ReduxAction) { if (e instanceof AppEngineApiError) return; - Sentry.captureException(e); + captureException(e, { errorName: "AppEngineError" }); yield put(safeCrashAppRequest()); } finally { endSpan(rootSpan); @@ -469,7 +471,7 @@ function* eagerPageInitSaga() { } catch (error) { // Log error but don't block the rest of the initialization log.error("Error validating session token:", error); - Sentry.captureException(error); + captureException(error, { errorName: "SessionValidationError" }); } const url = window.location.pathname; diff --git a/app/client/src/sagas/ReplaySaga.ts b/app/client/src/sagas/ReplaySaga.ts index a1dc00a8b5..6f454e96ce 100644 --- a/app/client/src/sagas/ReplaySaga.ts +++ b/app/client/src/sagas/ReplaySaga.ts @@ -8,7 +8,7 @@ import { takeLatest, } from "redux-saga/effects"; -import * as Sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; import log from "loglevel"; import { @@ -132,7 +132,7 @@ export function* openPropertyPaneSaga(replay: any) { ); } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "OpenPropertyPaneError" }); } } @@ -164,7 +164,7 @@ export function* postUndoRedoSaga(replay: any) { scrollWidgetIntoView(widgetIds[0]); } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "PostUndoRedoError" }); } } @@ -257,7 +257,7 @@ export function* undoRedoSaga(action: ReduxAction) { } } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "UndoRedoSagaError" }); } } diff --git a/app/client/src/sagas/WidgetLoadingSaga.ts b/app/client/src/sagas/WidgetLoadingSaga.ts index 17fc4d3c54..9c5987f0c9 100644 --- a/app/client/src/sagas/WidgetLoadingSaga.ts +++ b/app/client/src/sagas/WidgetLoadingSaga.ts @@ -16,7 +16,7 @@ import { ReduxActionTypes, } from "ee/constants/ReduxActionConstants"; import log from "loglevel"; -import * as Sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; import { findLoadingEntities } from "utils/WidgetLoadingStateUtils"; const actionExecutionRequestActions = [ @@ -101,7 +101,7 @@ export default function* actionExecutionChangeListeners() { yield call(actionExecutionChangeListenerSaga); } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "WidgetLoadingError" }); } } } diff --git a/app/client/src/sagas/layoutConversionSagas.ts b/app/client/src/sagas/layoutConversionSagas.ts index adbacd1161..d6fd62fe7c 100644 --- a/app/client/src/sagas/layoutConversionSagas.ts +++ b/app/client/src/sagas/layoutConversionSagas.ts @@ -16,7 +16,6 @@ import { createSnapshotSaga, deleteApplicationSnapshotSaga, } from "./SnapshotSagas"; -import * as Sentry from "@sentry/react"; import log from "loglevel"; import { saveAllPagesSaga } from "ee/sagas/PageSagas"; import { updateApplicationLayout } from "ee/actions/applicationActions"; @@ -27,6 +26,7 @@ import { import { updateApplicationLayoutType } from "./AutoLayoutUpdateSagas"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import { nestDSL } from "@shared/dsl"; +import captureException from "instrumentation/sendFaroErrors"; /** * This method is used to convert from auto-layout to fixed layout @@ -214,7 +214,7 @@ function* logLayoutConversionErrorSaga() { (state: AppState) => state.ui.layoutConversion.conversionError, ); - yield call(Sentry.captureException, error); + yield call(captureException, error, { errorName: "LayoutConversionError" }); } catch (e) { throw e; } diff --git a/app/client/src/utils/Analytics/sentry.ts b/app/client/src/utils/Analytics/sentry.ts index 23475001a8..ea4bd48fb9 100644 --- a/app/client/src/utils/Analytics/sentry.ts +++ b/app/client/src/utils/Analytics/sentry.ts @@ -1,99 +1,24 @@ -import * as Sentry from "@sentry/react"; -import { getAppsmithConfigs } from "ee/configs"; -import log from "loglevel"; +import { captureException } from "instrumentation/sendFaroErrors"; import type { User } from "constants/userConstants"; -class SentryUtil { +class FaroUtil { static init() { - const { sentry } = getAppsmithConfigs(); - - try { - if (sentry.enabled && !window.Sentry) { - window.Sentry = Sentry; - Sentry.init({ - ...sentry, - beforeSend(event) { - const exception = SentryUtil.extractSentryException(event); - - if (exception?.type === "ChunkLoadError") { - // Only log ChunkLoadErrors after the 2 retries - if (!exception.value?.includes("failed after 2 retries")) { - return null; - } - } - - // Handle Non-Error rejections - if (exception?.value?.startsWith("Non-Error")) { - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const serializedData: any = event.extra?.__serialized__; - - if (!serializedData) return null; // if no data is attached, ignore error - - const actualErrorMessage = serializedData.error - ? serializedData.error.message - : serializedData.message; - - if (!actualErrorMessage) return null; // If no message is attached, ignore error - - // Now modify the original error - exception.value = actualErrorMessage; - event.message = actualErrorMessage; - } - - return event; - }, - beforeBreadcrumb(breadcrumb) { - if ( - breadcrumb.category === "console" && - breadcrumb.level !== "error" - ) { - return null; - } - - if (breadcrumb.category === "sentry.transaction") { - return null; - } - - if (breadcrumb.category === "redux.action") { - if ( - breadcrumb.data && - breadcrumb.data.type === "SET_EVALUATED_TREE" - ) { - breadcrumb.data = undefined; - } - } - - return breadcrumb; - }, - }); - } - } catch (error) { - log.error("Failed to initialize Sentry:", error); - } + // No initialization needed } public static identifyUser(userId: string, userData: User) { - const { sentry } = getAppsmithConfigs(); - - if (sentry.enabled) { - Sentry.configureScope(function (scope) { - scope.setUser({ - id: userId, - username: userData.username, - email: userData.email, - }); - }); - } + // Set user context for error reporting + window.faro?.api.setUser({ + id: userId, + username: userData.username, + email: userData.email, + }); } - private static extractSentryException(event: Sentry.Event) { - if (!event.exception) return null; - - const value = event.exception.values ? event.exception.values[0] : null; - - return value; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public static captureException(error: Error, context?: any) { + captureException(error, context); } } -export default SentryUtil; +export default FaroUtil; diff --git a/app/client/src/utils/getPathAndValueFromActionDiffObject.ts b/app/client/src/utils/getPathAndValueFromActionDiffObject.ts index a98e18eb04..a7a1f68ae9 100644 --- a/app/client/src/utils/getPathAndValueFromActionDiffObject.ts +++ b/app/client/src/utils/getPathAndValueFromActionDiffObject.ts @@ -1,4 +1,4 @@ -import * as Sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; //Following function is the fix for the missing where key /** @@ -47,10 +47,13 @@ export function getPathAndValueFromActionDiffObject(actionObjectDiff: any) { return acc; } catch (error) { - Sentry.captureException({ - message: `Adding key: where failed, cannot create path`, - oldData: actionObjectDiff, - }); + captureException( + { + message: `Adding key: where failed, cannot create path`, + oldData: actionObjectDiff, + }, + { errorName: "ActionDiffPathError" }, + ); } }, "", diff --git a/app/client/src/utils/helpers.test.ts b/app/client/src/utils/helpers.test.ts index d8266c2eec..2f0d35768c 100644 --- a/app/client/src/utils/helpers.test.ts +++ b/app/client/src/utils/helpers.test.ts @@ -15,8 +15,10 @@ import { concatWithArray, } from "./helpers"; import WidgetFactory from "../WidgetProvider/factory"; -import * as Sentry from "@sentry/react"; import { Colors } from "constants/Colors"; +import * as FaroErrors from "instrumentation/sendFaroErrors"; + +jest.mock("instrumentation/sendFaroErrors"); describe("flattenObject test", () => { it("Check if non nested object is returned correctly", () => { @@ -202,6 +204,10 @@ describe("#getLocale", () => { }); describe("#captureInvalidDynamicBindingPath", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + it("DSL should not be altered", () => { const baseDSL = { widgetName: "RadioGroup1", @@ -278,7 +284,6 @@ describe("#captureInvalidDynamicBindingPath", () => { helpText: "Sets a default selected option", propertyName: "defaultOptionValue", label: "Default selected value", - // placeholderText: "Y", controlType: "INPUT_TEXT", isBindProperty: true, isTriggerProperty: false, @@ -340,7 +345,6 @@ describe("#captureInvalidDynamicBindingPath", () => { label: "Animate loading", controlType: "SWITCH", helpText: "Controls the loading of the widget", - // defaultValue: true, isJSConvertible: true, isBindProperty: true, isTriggerProperty: false, @@ -449,7 +453,6 @@ describe("#captureInvalidDynamicBindingPath", () => { helpText: "Sets a default selected option", propertyName: "defaultOptionValue", label: "Default selected value", - // placeholderText: "Y", controlType: "INPUT_TEXT", isBindProperty: true, isTriggerProperty: false, @@ -511,7 +514,6 @@ describe("#captureInvalidDynamicBindingPath", () => { label: "Animate loading", controlType: "SWITCH", helpText: "Controls the loading of the widget", - // defaultValue: true, isJSConvertible: true, isBindProperty: true, isTriggerProperty: false, @@ -541,13 +543,18 @@ describe("#captureInvalidDynamicBindingPath", () => { }, ]); - const sentrySpy = jest.spyOn(Sentry, "captureException"); + const mockCaptureException = jest.fn(); + + (FaroErrors.captureException as jest.Mock).mockImplementation( + mockCaptureException, + ); captureInvalidDynamicBindingPath(baseDSL); - expect(sentrySpy).toHaveBeenCalledWith( + expect(mockCaptureException).toHaveBeenCalledWith( new Error( `INVALID_DynamicPathBinding_CLIENT_ERROR: Invalid dynamic path binding list: RadioGroup1.options`, ), + { errorName: "InvalidDynamicPathBinding" }, ); }); }); diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index 948615ea79..bd4d83897b 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -17,7 +17,6 @@ import moment from "moment"; import { isDynamicValue } from "./DynamicBindingUtils"; import type { ApiResponse } from "api/ApiResponses"; import type { DSLWidget } from "WidgetProvider/constants"; -import * as Sentry from "@sentry/react"; import { matchPath } from "react-router"; import { BUILDER_CUSTOM_PATH, @@ -45,6 +44,7 @@ import { klona as klonaJson } from "klona/json"; import { startAndEndSpanForFn } from "instrumentation/generateTraces"; import type { Property } from "entities/Action"; +import captureException from "instrumentation/sendFaroErrors"; export const snapToGrid = ( columnWidth: number, @@ -944,10 +944,11 @@ export const captureInvalidDynamicBindingPath = ( * Checks if dynamicBindingPathList contains a property path that doesn't have a binding */ if (!isDynamicValue(pathValue)) { - Sentry.captureException( + captureException( new Error( `INVALID_DynamicPathBinding_CLIENT_ERROR: Invalid dynamic path binding list: ${currentDSL.widgetName}.${dBindingPath.key}`, ), + { errorName: "InvalidDynamicPathBinding" }, ); return; diff --git a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx index 874ef81808..99a27b137f 100644 --- a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx +++ b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx @@ -16,7 +16,6 @@ import _ from "lodash"; import derivedProperties from "./parsedDerivedProperties"; import BaseInputWidget from "widgets/BaseInputWidget"; import type { BaseInputWidgetProps } from "widgets/BaseInputWidget/widget"; -import * as Sentry from "@sentry/react"; import log from "loglevel"; import { formatCurrencyNumber, @@ -44,6 +43,7 @@ import { getDefaultCurrency } from "../component/CurrencyCodeDropdown"; import IconSVG from "../icon.svg"; import ThumbnailSVG from "../thumbnail.svg"; import { WIDGET_TAGS } from "constants/WidgetConstants"; +import captureException from "instrumentation/sendFaroErrors"; export function defaultValueValidation( // TODO: Fix this the next time the file is edited @@ -499,7 +499,7 @@ class CurrencyInputWidget extends BaseInputWidget< this.props.updateWidgetMetaProperty("text", formattedValue); } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "CurrencyInputWidget" }); } } } @@ -517,7 +517,7 @@ class CurrencyInputWidget extends BaseInputWidget< } catch (e) { formattedValue = value; log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "CurrencyInputWidget" }); } // text is stored as what user has typed @@ -575,7 +575,7 @@ class CurrencyInputWidget extends BaseInputWidget< } } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "CurrencyInputWidget" }); this.props.updateWidgetMetaProperty("text", this.props.text); } diff --git a/app/client/src/widgets/JSONFormWidget/fields/CurrencyInputField.tsx b/app/client/src/widgets/JSONFormWidget/fields/CurrencyInputField.tsx index e6e5749c93..aca1efe751 100644 --- a/app/client/src/widgets/JSONFormWidget/fields/CurrencyInputField.tsx +++ b/app/client/src/widgets/JSONFormWidget/fields/CurrencyInputField.tsx @@ -1,4 +1,3 @@ -import * as Sentry from "@sentry/react"; import React, { useCallback, useContext, useMemo, useState } from "react"; import type { BaseInputComponentProps } from "./BaseInputField"; @@ -15,6 +14,7 @@ import derived from "widgets/CurrencyInputWidget/widget/derived"; import { isEmpty } from "../helper"; import { BASE_LABEL_TEXT_SIZE } from "../component/FieldLabel"; import { getLocaleDecimalSeperator } from "widgets/WidgetUtils"; +import captureException from "instrumentation/sendFaroErrors"; type CurrencyInputComponentProps = BaseInputComponentProps & { currencyCountryCode: string; @@ -132,7 +132,7 @@ function CurrencyInputField({ } } catch (e) { text = inputValue; - Sentry.captureException(e); + captureException(e, { errorName: "JSONFormWidget_CurrencyInputField" }); } const value = derived.value({ text }); diff --git a/app/client/src/widgets/JSONFormWidget/fields/useRegisterFieldValidity.ts b/app/client/src/widgets/JSONFormWidget/fields/useRegisterFieldValidity.ts index 30720b4faf..b24a193b18 100644 --- a/app/client/src/widgets/JSONFormWidget/fields/useRegisterFieldValidity.ts +++ b/app/client/src/widgets/JSONFormWidget/fields/useRegisterFieldValidity.ts @@ -1,4 +1,3 @@ -import * as Sentry from "@sentry/react"; import { set } from "lodash"; import type { ControllerProps } from "react-hook-form"; import { useFormContext } from "react-hook-form"; @@ -8,6 +7,7 @@ import FormContext from "../FormContext"; import type { FieldType } from "../constants"; import { startAndEndSpanForFn } from "instrumentation/generateTraces"; import { klonaRegularWithTelemetry } from "utils/helpers"; +import captureException from "instrumentation/sendFaroErrors"; export interface UseRegisterFieldValidityProps { isValid: boolean; @@ -53,7 +53,9 @@ function useRegisterFieldValidity({ } } } catch (e) { - Sentry.captureException(e); + captureException(e, { + errorName: "JSONFormWidget_useRegisterFieldValidity", + }); } }, 0); }, [isValid, fieldName, fieldType, error, clearErrors, setError]); diff --git a/app/client/src/widgets/MapChartWidget/component/utilities.ts b/app/client/src/widgets/MapChartWidget/component/utilities.ts index 35b55796d1..20babb089d 100644 --- a/app/client/src/widgets/MapChartWidget/component/utilities.ts +++ b/app/client/src/widgets/MapChartWidget/component/utilities.ts @@ -3,7 +3,7 @@ import countryDetails from "./countryDetails"; import { MapTypes } from "../constants"; import { geoAlbers, geoAzimuthalEqualArea, geoMercator } from "d3-geo"; import log from "loglevel"; -import * as Sentry from "@sentry/react"; +import captureException from "instrumentation/sendFaroErrors"; import { retryPromise } from "utils/AppsmithUtils"; interface GeoSpecialAreas { @@ -75,7 +75,9 @@ export const loadMapGenerator = () => { if (error.code !== 20) { log.error({ error }); - Sentry.captureException(error); + captureException(error, { + errorName: "MapChartWidget_utilities", + }); } }, ) diff --git a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx index 05c9b650dd..c04b2c8b49 100644 --- a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx +++ b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx @@ -19,7 +19,6 @@ import type { BaseInputWidgetProps } from "widgets/BaseInputWidget/widget"; import { mergeWidgetConfig } from "utils/helpers"; import type { CountryCode } from "libphonenumber-js"; import { AsYouType, parseIncompletePhoneNumber } from "libphonenumber-js"; -import * as Sentry from "@sentry/react"; import log from "loglevel"; import type { SetterConfig, Stylesheet } from "entities/AppTheming"; import { @@ -39,6 +38,7 @@ import { getDefaultISDCode } from "../component/ISDCodeDropdown"; import IconSVG from "../icon.svg"; import ThumbnailSVG from "../thumbnail.svg"; import { WIDGET_TAGS } from "constants/WidgetConstants"; +import captureException from "instrumentation/sendFaroErrors"; export function defaultValueValidation( // TODO: Fix this the next time the file is edited @@ -348,7 +348,7 @@ class PhoneInputWidget extends BaseInputWidget< this.props.updateWidgetMetaProperty("text", formattedValue); } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "PhoneInputWidget" }); } } } diff --git a/app/client/src/widgets/TableWidgetV2/component/cellComponents/InlineCellEditor.tsx b/app/client/src/widgets/TableWidgetV2/component/cellComponents/InlineCellEditor.tsx index 9a189aee4d..f024c3ffad 100644 --- a/app/client/src/widgets/TableWidgetV2/component/cellComponents/InlineCellEditor.tsx +++ b/app/client/src/widgets/TableWidgetV2/component/cellComponents/InlineCellEditor.tsx @@ -19,8 +19,8 @@ import { getLocaleThousandSeparator, } from "widgets/WidgetUtils"; import { limitDecimalValue } from "widgets/CurrencyInputWidget/component/utilities"; -import * as Sentry from "@sentry/react"; import { getLocale } from "utils/helpers"; +import captureException from "instrumentation/sendFaroErrors"; const FOCUS_CLASS = "has-focus"; @@ -237,7 +237,7 @@ export function InlineCellEditor({ value = convertToNumber(inputValue); } catch (e) { - Sentry.captureException(e); + captureException(e, { errorName: "TableWidgetV2_InlineCellEditor" }); } } diff --git a/app/client/src/widgets/TableWidgetV2/component/cellComponents/PlainTextCell.tsx b/app/client/src/widgets/TableWidgetV2/component/cellComponents/PlainTextCell.tsx index f9dd1874fe..f107324e09 100644 --- a/app/client/src/widgets/TableWidgetV2/component/cellComponents/PlainTextCell.tsx +++ b/app/client/src/widgets/TableWidgetV2/component/cellComponents/PlainTextCell.tsx @@ -19,8 +19,8 @@ import CurrencyTypeDropdown, { CurrencyDropdownOptions, } from "widgets/CurrencyInputWidget/component/CurrencyCodeDropdown"; import { getLocale } from "utils/helpers"; -import * as Sentry from "@sentry/react"; import { getLocaleThousandSeparator } from "widgets/WidgetUtils"; +import captureException from "instrumentation/sendFaroErrors"; const Container = styled.div<{ isCellEditMode?: boolean; @@ -227,7 +227,7 @@ function PlainTextCell( return currency?.id + " " + formattedValue; } } catch (e) { - Sentry.captureException(e); + captureException(e, { errorName: "TableWidgetV2_PlainTextCell" }); return value; } diff --git a/app/client/src/widgets/TabsMigrator/widget/index.tsx b/app/client/src/widgets/TabsMigrator/widget/index.tsx index 3ab9a9a583..b126624151 100644 --- a/app/client/src/widgets/TabsMigrator/widget/index.tsx +++ b/app/client/src/widgets/TabsMigrator/widget/index.tsx @@ -10,9 +10,9 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { generateReactKey } from "utils/generators"; import { EVAL_VALUE_PATH } from "utils/DynamicBindingUtils"; import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType"; -import * as Sentry from "@sentry/react"; import type { DSLWidget } from "WidgetProvider/constants"; import { DATA_BIND_REGEX_GLOBAL } from "constants/BindingsConstants"; +import captureException from "instrumentation/sendFaroErrors"; function migrateTabsDataUsingMigrator(currentDSL: DSLWidget) { if (currentDSL.type === "TABS_WIDGET" && currentDSL.version === 1) { @@ -20,7 +20,8 @@ function migrateTabsDataUsingMigrator(currentDSL: DSLWidget) { currentDSL.type = "TABS_MIGRATOR_WIDGET"; currentDSL.version = 1; } catch (error) { - Sentry.captureException({ + captureException(error, { + errorName: "TabsMigrator", message: "Tabs Migration Failed", oldData: currentDSL.tabs, }); @@ -116,7 +117,8 @@ const migrateTabsData = (currentDSL: DSLWidget) => { currentDSL.version = 2; delete currentDSL.tabs; } catch (error) { - Sentry.captureException({ + captureException(error, { + errorName: "TabsMigrator", message: "Tabs Migration Failed", oldData: currentDSL.tabs, }); diff --git a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/index.tsx b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/index.tsx index d057970c5f..640de05edc 100644 --- a/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/index.tsx +++ b/app/client/src/widgets/wds/WDSCurrencyInputWidget/widget/index.tsx @@ -1,7 +1,6 @@ import _ from "lodash"; import React from "react"; import log from "loglevel"; -import * as Sentry from "@sentry/react"; import type { WidgetState } from "widgets/BaseWidget"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; @@ -30,6 +29,7 @@ import type { CurrencyInputWidgetProps } from "./types"; import { WDSBaseInputWidget } from "widgets/wds/WDSBaseInputWidget"; import { getCountryCodeFromCurrencyCode, validateInput } from "./helpers"; import type { KeyDownEvent } from "widgets/wds/WDSBaseInputWidget/component/types"; +import captureException from "instrumentation/sendFaroErrors"; class WDSCurrencyInputWidget extends WDSBaseInputWidget< CurrencyInputWidgetProps, @@ -189,7 +189,7 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget< } catch (e) { formattedValue = value; log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "WDSCurrencyInputWidget" }); } this.props.updateWidgetMetaProperty("text", String(formattedValue)); @@ -248,7 +248,7 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget< } } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "WDSCurrencyInputWidget" }); this.props.updateWidgetMetaProperty("text", this.props.text); } @@ -311,7 +311,7 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget< this.props.updateWidgetMetaProperty("text", formattedValue); } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "WDSCurrencyInputWidget" }); } } } diff --git a/app/client/src/widgets/wds/WDSPhoneInputWidget/widget/index.tsx b/app/client/src/widgets/wds/WDSPhoneInputWidget/widget/index.tsx index 80a2a58608..f3c4e5b1db 100644 --- a/app/client/src/widgets/wds/WDSPhoneInputWidget/widget/index.tsx +++ b/app/client/src/widgets/wds/WDSPhoneInputWidget/widget/index.tsx @@ -1,7 +1,6 @@ import React from "react"; import log from "loglevel"; import merge from "lodash/merge"; -import * as Sentry from "@sentry/react"; import { klonaRegularWithTelemetry, mergeWidgetConfig } from "utils/helpers"; import type { CountryCode } from "libphonenumber-js"; import type { WidgetState } from "widgets/BaseWidget"; @@ -21,6 +20,7 @@ import * as config from "../config"; import { PhoneInputComponent } from "../component"; import type { PhoneInputWidgetProps } from "./types"; import { getCountryCode, validateInput } from "./helpers"; +import captureException from "instrumentation/sendFaroErrors"; class WDSPhoneInputWidget extends WDSBaseInputWidget< PhoneInputWidgetProps, @@ -163,7 +163,7 @@ class WDSPhoneInputWidget extends WDSBaseInputWidget< this.props.updateWidgetMetaProperty("text", formattedValue); } catch (e) { log.error(e); - Sentry.captureException(e); + captureException(e, { errorName: "WDSPhoneInputWidget" }); } } } diff --git a/app/client/src/workers/Evaluation/errorModifier.ts b/app/client/src/workers/Evaluation/errorModifier.ts index 74bef47bdb..69cd601ab6 100644 --- a/app/client/src/workers/Evaluation/errorModifier.ts +++ b/app/client/src/workers/Evaluation/errorModifier.ts @@ -12,8 +12,8 @@ import { get, isEmpty, toPath } from "lodash"; import { APP_MODE } from "entities/App"; import { isAction } from "ee/workers/Evaluation/evaluationUtils"; import log from "loglevel"; -import * as Sentry from "@sentry/react"; import { getMemberExpressionObjectFromProperty } from "@shared/ast"; +import captureException from "instrumentation/sendFaroErrors"; interface ErrorMetaData { userScript: string; @@ -224,7 +224,7 @@ export function convertAllDataTypesToString(e: any) { return JSON.stringify(e); } catch (error) { log.debug(error); - Sentry.captureException(error); + captureException(error, { errorName: "ErrorModifier_StringifyError" }); } } }