diff --git a/app/client/src/AppErrorBoundry.tsx b/app/client/src/AppErrorBoundry.tsx index 2331fc4d531b..5ee4d1346ff0 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 7933a9336e19..3bcfdba1b515 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 084e9b23f3b2..7afbea263391 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 4153a6f868bc..9a7971c5e207 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 21e2aa42f313..a9b3e27b0bd9 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 343ec7022e48..f096a3eeeed1 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 53481cfcc892..e4e2dbbe4a03 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 e3afcc5699c0..6bca41a7ea2d 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 a5cac6a7f9d8..a7e3a865c9d9 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 d7e51b9dc779..0af92d97beed 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 705ecf6273df..7e5c0f734ac2 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 2cd1a4ee50ee..f3b9d6d8716c 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 44bf4bfb639e..e0d1efcf7b02 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 bfa50589dc6f..102f54b76554 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 e82074862001..58800b9d1cce 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 10c965efc9f0..d37229a9e89e 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 51906284e179..3684d74cd32d 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 efb966424e63..94500969ba34 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 ea98eb7e1575..1de97860848a 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 099711ce4782..fd7fd69e91f5 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 2eaacdd8701a..fb0d7629cf43 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 000000000000..13e467764176 --- /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 3f96f5b2290c..12ef00c9b1dc 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 1fdb6e1250ab..a9db0e1e255b 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 b0d1140e50e1..96d5c0534c38 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 46f3fb9282d1..71ccd7b31d3b 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 d5e105ffa529..7dee5c468870 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 edc4fa2bc106..e40a61ccd652 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 a25f9402264f..b46a066aa102 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 bc696590ca16..33aa5ffedeae 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 1da57618fd18..95312c7a516d 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 8496125b85c2..7ab12d3a1ec1 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 afde6fddf6a6..434148ac759f 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 f6b708d09c24..445f77a5dca5 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 72d303a8f57e..94d1e177d2c5 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 93cc583ca681..602669ff7c1f 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 3eb4203b2868..1112ee89af8e 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 45d94c5a44a5..80af8a62dfaf 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 aa901b8ca366..389a7a148c49 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 a8be85d9f1dd..fc1f3f60a17e 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 c21366784fc8..df5e52e894af 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 a986a0c31d37..5d1054aed5eb 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 7e3a79874984..176ace8f1162 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 812c29e916b7..46777a3efc0e 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 8bbe2180b365..82fec84a8d0e 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 4c324c7de072..c5f9cbeff504 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 edf4e42df07a..34f88ff154e8 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 a1dc00a8b5bc..6f454e96ce78 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 17fc4d3c54c1..9c5987f0c937 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 adbacd11616b..d6fd62fe7c21 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 23475001a806..ea4bd48fb9c5 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 a98e18eb04a7..a7a1f68ae99d 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 d8266c2eecf6..2f0d35768c0b 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 948615ea7929..bd4d83897b02 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 874ef8180897..99a27b137f4e 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 e6e5749c9330..aca1efe75199 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 30720b4faf98..b24a193b1882 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 35b55796d18f..20babb089d1c 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 05c9b650ddd4..c04b2c8b49d8 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 9a189aee4d58..f024c3ffad25 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 f9dd1874fe3a..f107324e09f6 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 3ab9a9a5834a..b126624151dd 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 d057970c5f2a..640de05edc6b 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 80a2a58608b8..f3c4e5b1db3b 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 74bef47bdbd0..69cd601ab689 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" }); } } }