Skip to content

feat(protocol-designer): introduce sentry #18153

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Apr 28, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/pd-test-build-deploy.yaml
Original file line number Diff line number Diff line change
@@ -83,8 +83,12 @@ jobs:
- uses: ./.github/actions/js/setup
- name: 'build PD'
env:
NODE_OPTIONS: '--max-old-space-size=5120'
OT_PD_MIXPANEL_ID: ${{ secrets.OT_PD_MIXPANEL_ID }}
OT_PD_MIXPANEL_DEV_ID: ${{ secrets.OT_PD_MIXPANEL_DEV_ID }}
# ToDo(kk:04/23/2025) need to setup sentry org account and project
# OT_PD_SENTRY_DSN: ${{ secrets.OT_PD_SENTRY_DNS }}
# OT_PD_SENTRY_DEV_DSN: ${{ secrets.OT_PD_SENTRY_DEV_DSN }}
run: |
make -C protocol-designer NODE_ENV=development
- name: 'upload github artifact'
@@ -93,6 +97,23 @@ jobs:
name: 'pd-artifact'
path: protocol-designer/dist

# ToDo(kk:04/23/2025) need to setup sentry org account and project
# upload-sourcemaps-pd:
# timeout-minutes: 10
# name: 'upload protocol designer source maps'
# needs: ['build-pd']
# runs-on: 'ubuntu-24.04'
# if: github.event_name != 'pull_request'

# uses: getsentry/action-release@v3
# env:
# SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
# SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
# SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
# with:
# name: pd-artifact
# sourcemaps: ./dist

deploy-pd:
timeout-minutes: 10
name: 'deploy protocol designer'
4 changes: 3 additions & 1 deletion protocol-designer/package.json
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@
"@opentrons/components": "link:../components",
"@opentrons/step-generation": "link:../step-generation",
"@opentrons/shared-data": "link:../shared-data",
"@sentry/react": "^9.12.0",
"@types/redux-actions": "2.6.1",
"@types/styled-components": "^5.1.26",
"@types/ua-parser-js": "0.7.36",
@@ -70,6 +71,7 @@
"yup": "1.3.3"
},
"devDependencies": {
"@types/mixpanel-browser": "^2.35.6"
"@types/mixpanel-browser": "^2.35.6",
"@sentry/vite-plugin": "^3.3.1"
}
}
4 changes: 4 additions & 0 deletions protocol-designer/src/index.tsx
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
import { GlobalStyle } from './components/atoms'
import { configureStore } from './configureStore'
import { initialize } from './initialize'
import { initializeSentry } from './resources/sentry'

Check warning on line 11 in protocol-designer/src/index.tsx

Codecov / codecov/patch

protocol-designer/src/index.tsx#L11

Added line #L11 was not covered by tests

// initialize Redux
const store = configureStore()
@@ -16,6 +17,9 @@
// initialize analytics
initializeMixpanel(store.getState())

// initialize Sentry
initializeSentry(store.getState())

Check warning on line 21 in protocol-designer/src/index.tsx

Codecov / codecov/patch

protocol-designer/src/index.tsx#L21

Added line #L21 was not covered by tests

const container = document.getElementById('root')
if (container == null) throw new Error('Failed to find the root element')
const root = ReactDOM.createRoot(container)
14 changes: 12 additions & 2 deletions protocol-designer/src/resources/ProtocolDesignerAppFallback.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import { captureException } from '@sentry/react'
import { v4 as uuidv4 } from 'uuid'

import {
@@ -20,16 +22,18 @@ import type { FallbackProps } from 'react-error-boundary'
import type { AnalyticsEvent } from '../analytics/mixpanel'
import type { ThunkDispatch } from '../types'

const LOG_LEVEL = 'error'

export function ProtocolDesignerAppFallback({
error,
resetErrorBoundary,
}: FallbackProps): JSX.Element {
const { t } = useTranslation('shared')
const dispatch: ThunkDispatch<any> = useDispatch()

// Note (kk:02/12/25) this is to track a users' error since many users send a screenshot.
// Note errorId will be used to track a specific user's error
// when the support team share the data(screenshot or errorId) with us
const errorId = uuidv4()

const errorEvent: AnalyticsEvent = {
name: 'protocolDesignerAppError',
properties: {
@@ -47,6 +51,12 @@ export function ProtocolDesignerAppFallback({
dispatch(actions.saveProtocolFile())
}

useEffect(() => {
if (error) {
captureException(error, { extra: { errorId }, level: LOG_LEVEL })
}
}, [error, errorId])

return (
<Modal type="warning" title={t('error_boundary_title')} marginLeft="0">
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing32}>
50 changes: 50 additions & 0 deletions protocol-designer/src/resources/sentry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {

Check warning on line 1 in protocol-designer/src/resources/sentry.ts

Codecov / codecov/patch

protocol-designer/src/resources/sentry.ts#L1

Added line #L1 was not covered by tests
browserTracingIntegration,
init,
replayIntegration,
} from '@sentry/react'

import { getHasOptedIn } from '../analytics/selectors'
import { getIsProduction } from '../networking/opentronsWebApi'

import type { BaseState } from '../types'

let isSentryInitialized = false

Check warning on line 12 in protocol-designer/src/resources/sentry.ts

Codecov / codecov/patch

protocol-designer/src/resources/sentry.ts#L12

Added line #L12 was not covered by tests

const sentryDsn = getIsProduction()
? process.env.OT_PD_SENTRY_DNS
: process.env.OT_PD_SENTRY_DEV_DNS

Check warning on line 16 in protocol-designer/src/resources/sentry.ts

Codecov / codecov/patch

protocol-designer/src/resources/sentry.ts#L14-L16

Added lines #L14 - L16 were not covered by tests

export const initializeSentry = (state: BaseState): void => {
const optedIn = getHasOptedIn(state)?.hasOptedIn ?? false
if (isSentryInitialized) {
console.warn('Sentry is already initialized')
return
}

Check warning on line 23 in protocol-designer/src/resources/sentry.ts

Codecov / codecov/patch

protocol-designer/src/resources/sentry.ts#L18-L23

Added lines #L18 - L23 were not covered by tests

if (sentryDsn == null) {
console.warn('Sentry DSN not found - Sentry is not initialized')
return
}
if (optedIn) {
try {
init({
dsn: sentryDsn,
integrations: [replayIntegration(), browserTracingIntegration()],
tracesSampleRate: 1.0,
tracePropagationTargets: [
'localhost',
/^https:\/\/designer\.opentrons\.com/,
],
replaysSessionSampleRate: 0.0, // No Session Replay
replaysOnErrorSampleRate: 0.0, // No Session Replay
})
isSentryInitialized = true
console.log('Sentry.init done')
} catch (error) {
console.error('Error initializing Sentry:', error)
}
} else {
console.debug('User has opted out of Sentry; stopping Sentry')
}
}

Check warning on line 50 in protocol-designer/src/resources/sentry.ts

Codecov / codecov/patch

protocol-designer/src/resources/sentry.ts#L25-L50

Added lines #L25 - L50 were not covered by tests
26 changes: 22 additions & 4 deletions protocol-designer/vite.config.mts
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import path from 'path'
import { defineConfig } from 'vite'
import { sentryVitePlugin } from '@sentry/vite-plugin'
import react from '@vitejs/plugin-react'
import postCssImport from 'postcss-import'
import lostCss from 'lost'
import postCssApply from 'postcss-apply'
import postColorModFunction from 'postcss-color-mod-function'
import postCssImport from 'postcss-import'
import postCssPresetEnv from 'postcss-preset-env'
import lostCss from 'lost'
import { defineConfig } from 'vite'

import { versionForProject } from '../scripts/git-version.mjs'

import type { UserConfig } from 'vite'

// eslint-disable-next-line import/no-default-export
export default defineConfig(
async (): Promise<UserConfig> => {
const OT_PD_VERSION = await versionForProject('protocol-designer')
const OT_PD_BUILD_DATE = new Date().toUTCString()
const mode = process.env.NODE_ENV ?? 'development'
return {
// this makes imports relative rather than absolute
base: '',
build: {
// Relative to the root
outDir: 'dist',
// sourcemap for Sentry
sourcemap: true,
},
plugins: [
react({
@@ -37,6 +43,18 @@ export default defineConfig(
}
},
},
sentryVitePlugin({
org: 'opentrons-sw',
project: 'protocol-designer',
authToken: process.env.OT_SENTRY_AUTH_TOKEN,
telemetry: false,
sourcemaps: {
assets: ['./dist/**'],
ignore: ['./node_modules/**'],
filesToDeleteAfterUpload:
mode === 'production' ? ['./dist/**/*.js.map'] : undefined,
},
}),
],
optimizeDeps: {
esbuildOptions: {
@@ -74,7 +92,7 @@ export default defineConfig(
port: 5178,
watch: {
ignored: ['**/cypress/downloads/**'],
}
},
},
}
}
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.