From 4271f7fd1ae19a34025692ec5ada96c66963b4e7 Mon Sep 17 00:00:00 2001 From: awongh Date: Wed, 12 Feb 2025 14:07:03 +0100 Subject: [PATCH 1/2] add sentry error monitoring to react native --- nr-app/app.json | 12 ++- nr-app/app/_layout.tsx | 30 +++++- nr-app/metro.config.js | 6 +- nr-app/package.json | 1 + nr-common/constants.ts | 4 + pnpm-lock.yaml | 216 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 263 insertions(+), 6 deletions(-) diff --git a/nr-app/app.json b/nr-app/app.json index 306495b..dc4fdc8 100644 --- a/nr-app/app.json +++ b/nr-app/app.json @@ -42,7 +42,17 @@ "plugins": [ "expo-router", "expo-font", - "expo-secure-store" + "expo-secure-store", + [ + "@sentry/react-native/expo", + { + "organization": "sentry org slug, or use the `SENTRY_ORG` environment variable", + "project": "sentry project name, or use the `SENTRY_PROJECT` environment variable", + // If you are using a self-hosted instance, update the value of the url property + // to point towards your self-hosted instance. For example, https://self-hosted.example.com/. + "url": "https://sentry.io/" + } + ] ], "experiments": { "typedRoutes": true, diff --git a/nr-app/app/_layout.tsx b/nr-app/app/_layout.tsx index e8bfcc6..b034114 100644 --- a/nr-app/app/_layout.tsx +++ b/nr-app/app/_layout.tsx @@ -8,16 +8,37 @@ import { Stack } from "expo-router"; import * as SplashScreen from "expo-splash-screen"; import { useEffect } from "react"; import "react-native-reanimated"; -import { Provider } from "react-redux"; import { RootSiblingParent } from "react-native-root-siblings"; +import { Provider } from "react-redux"; + +import { SENTRY_DSN } from "../../nr-common/constants.ts"; + +import * as Sentry from "@sentry/react-native"; +import { isRunningInExpoGo } from "expo"; -import { store } from "@/redux/store"; import { useColorScheme } from "@/hooks/useColorScheme"; +import { store } from "@/redux/store"; + +// Construct a new integration instance. This is needed to communicate between the integration and React +const navigationIntegration = Sentry.reactNavigationIntegration({ + enableTimeToInitialDisplay: !isRunningInExpoGo(), +}); + +Sentry.init({ + dsn: SENTRY_DSN, + debug: __DEV__, // If `true`, Sentry will try to print out useful debugging information if something goes wrong with sending the event. Set it to `false` in production + tracesSampleRate: 1.0, // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing. Adjusting this value in production. + integrations: [ + // Pass integration + navigationIntegration, + ], + enableNativeFramesTracking: !isRunningInExpoGo(), // Tracks slow and frozen frames in the application +}); // Prevent the splash screen from auto-hiding before asset loading is complete. SplashScreen.preventAutoHideAsync(); -export default function RootLayout() { +function RootLayout() { const colorScheme = useColorScheme(); const [loaded] = useFonts({ SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"), @@ -46,3 +67,6 @@ export default function RootLayout() { ); } + +// Wrap the Root Layout route component with `Sentry.wrap` to capture gesture info and profiling data. +export default Sentry.wrap(RootLayout); diff --git a/nr-app/metro.config.js b/nr-app/metro.config.js index 35fca16..a6cdbac 100644 --- a/nr-app/metro.config.js +++ b/nr-app/metro.config.js @@ -1,11 +1,13 @@ // Learn more https://docs.expo.dev/guides/monorepos -const { getDefaultConfig } = require("expo/metro-config"); +const { getSentryExpoConfig } = require("@sentry/react-native/metro"); + const path = require("path"); const projectRoot = __dirname; const workspaceRoot = path.resolve(projectRoot, ".."); -const config = getDefaultConfig(projectRoot); +// This replaces `const config = getDefaultConfig(__dirname);` +const config = getSentryExpoConfig(__dirname); // Since we are using pnpm, we have to setup the monorepo manually for Metro // #1 - Watch all files in the monorepo diff --git a/nr-app/package.json b/nr-app/package.json index fab154f..42d4665 100644 --- a/nr-app/package.json +++ b/nr-app/package.json @@ -23,6 +23,7 @@ "@mobily/ts-belt": "^3.13.1", "@react-navigation/native": "^7.0.3", "@reduxjs/toolkit": "^2.2.7", + "@sentry/react-native": "~6.3.0", "@trustroots/nr-common": "workspace:*", "expo": "~52.0.8", "expo-constants": "~17.0.3", diff --git a/nr-common/constants.ts b/nr-common/constants.ts index 58b2e18..621653a 100644 --- a/nr-common/constants.ts +++ b/nr-common/constants.ts @@ -24,6 +24,10 @@ export const HITCHWIKI_AUTHOR_PUBLIC_KEY = export const TIMESAFARI_AUTHOR_PUBLIC_KEY = "76e88d2e653fc3655f8e0b97f6bc85f5468eaffc5d64522b584ce13eedbd8af7" as const; +// cross-env DSN id - use for native + server +export const SENTRY_DSN = "https://ea370f9e4aba87f6e69a479f2d41bc23@o4508806276841472.ingest.de.sentry.io/4508806292176976" as const; + + export type MapLayer = { title: string; rootUrl: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 178bdc7..023687f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,9 @@ importers: '@reduxjs/toolkit': specifier: ^2.2.7 version: 2.5.0(react-redux@9.2.0(@types/react@18.3.18)(react@18.3.1)(redux@5.0.1))(react@18.3.1) + '@sentry/react-native': + specifier: ~6.3.0 + version: 6.3.0(expo@52.0.23(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@expo/metro-runtime@4.0.0(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.18)(react@18.3.1)))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1) '@trustroots/nr-common': specifier: workspace:* version: link:../nr-common/build @@ -1455,6 +1458,105 @@ packages: '@segment/loosely-validate-event@2.0.0': resolution: {integrity: sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw==} + '@sentry-internal/browser-utils@8.40.0': + resolution: {integrity: sha512-tx7gb/PWMbTEyil/XPETVeRUeS3nKHIvQY2omyebw30TbhyLnibPZsUmXJiaIysL5PcY3k9maub3W/o0Y37T7Q==} + engines: {node: '>=14.18'} + + '@sentry-internal/feedback@8.40.0': + resolution: {integrity: sha512-1O9F3z80HNE0VfepKS+v+dixdatNqWlrlwgvvWl4BGzzoA+XhqvZo+HWxiOt7yx7+k1TuZNrB6Gy3u/QvpozXA==} + engines: {node: '>=14.18'} + + '@sentry-internal/replay-canvas@8.40.0': + resolution: {integrity: sha512-Zr+m/le0SH4RowZB7rBCM0aRnvH3wZTaOFhwUk03/oGf2BRcgKuDCUMjnXKC9MyOpmey7UYXkzb8ro+81R6Q8w==} + engines: {node: '>=14.18'} + + '@sentry-internal/replay@8.40.0': + resolution: {integrity: sha512-0SaDsBCSWxNVgNmPKu23frrHEXzN/MKl0hIkfuO55vL5TgjLTwpgkf0Ne4rNvaZQ5omIKk9Qd63HuQP3PHAMaw==} + engines: {node: '>=14.18'} + + '@sentry/babel-plugin-component-annotate@2.20.1': + resolution: {integrity: sha512-4mhEwYTK00bIb5Y9UWIELVUfru587Vaeg0DQGswv4aIRHIiMKLyNqCEejaaybQ/fNChIZOKmvyqXk430YVd7Qg==} + engines: {node: '>= 14'} + + '@sentry/browser@8.40.0': + resolution: {integrity: sha512-m/Yor6IDBeDHtQochu8n6z4HXrXkrPhu6+o5Ouve0Zi3ptthSoK1FOGvJxVBat3nRq0ydQyuuPuTB6WfdWbwHQ==} + engines: {node: '>=14.18'} + + '@sentry/cli-darwin@2.38.2': + resolution: {integrity: sha512-21ywIcJCCFrCTyiF1o1PaT7rbelFC2fWmayKYgFElnQ55IzNYkcn8BYhbh/QknE0l1NBRaeWMXwTTdeoqETCCg==} + engines: {node: '>=10'} + os: [darwin] + + '@sentry/cli-linux-arm64@2.38.2': + resolution: {integrity: sha512-4Fp/jjQpNZj4Th+ZckMQvldAuuP0ZcyJ9tJCP1CCOn5poIKPYtY6zcbTP036R7Te14PS4ALOcDNX3VNKfpsifA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux, freebsd] + + '@sentry/cli-linux-arm@2.38.2': + resolution: {integrity: sha512-+AiKDBQKIdQe4NhBiHSHGl0KR+b//HHTrnfK1SaTrOm9HtM4ELXAkjkRF3bmbpSzSQCS5WzcbIxxCJOeaUaO0A==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux, freebsd] + + '@sentry/cli-linux-i686@2.38.2': + resolution: {integrity: sha512-6zVJN10dHIn4R1v+fxuzlblzVBhIVwsaN/S7aBED6Vn1HhAyAcNG2tIzeCLGeDfieYjXlE2sCI82sZkQBCbAGw==} + engines: {node: '>=10'} + cpu: [x86, ia32] + os: [linux, freebsd] + + '@sentry/cli-linux-x64@2.38.2': + resolution: {integrity: sha512-4UiLu9zdVtqPeltELR5MDGKcuqAdQY9xz3emISuA6bm+MXGbt2W1WgX+XY3GElwjZbmH8qpyLUEd34sw6sdcbQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux, freebsd] + + '@sentry/cli-win32-i686@2.38.2': + resolution: {integrity: sha512-DYfSvd5qLPerLpIxj3Xu2rRe3CIlpGOOfGSNI6xvJ5D8j6hqbOHlCzvfC4oBWYVYGtxnwQLMeDGJ7o7RMYulig==} + engines: {node: '>=10'} + cpu: [x86, ia32] + os: [win32] + + '@sentry/cli-win32-x64@2.38.2': + resolution: {integrity: sha512-W5UX58PKY1hNUHo9YJxWNhGvgvv2uOYHI27KchRiUvFYBIqlUUcIdPZDfyzetDfd8qBCxlAsFnkL2VJSNdpA9A==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@sentry/cli@2.38.2': + resolution: {integrity: sha512-CR0oujpAnhegK2pBAv6ZReMqbFTuNJLDZLvoD1B+syrKZX+R+oxkgT2e1htsBbht+wGxAsluVWsIAydSws1GAA==} + engines: {node: '>= 10'} + hasBin: true + + '@sentry/core@8.40.0': + resolution: {integrity: sha512-u/U2CJpG/+SmTR2bPM4ZZoPYTJAOUuxzj/0IURnvI0v9+rNu939J/fzrO9huA5IJVxS5TiYykhQm7o6I3Zuo3Q==} + engines: {node: '>=14.18'} + + '@sentry/react-native@6.3.0': + resolution: {integrity: sha512-gbLEiqxBjejxhrD4tUEACQoAPZTpCxsnuY16mGu5M889yvAEkJvDHwS/SApMlSYf8ytprnn/LPHPePhcVz/vYQ==} + hasBin: true + peerDependencies: + expo: '>=49.0.0' + react: '>=17.0.0' + react-native: '>=0.65.0' + peerDependenciesMeta: + expo: + optional: true + + '@sentry/react@8.40.0': + resolution: {integrity: sha512-Ohq/po83r9sh/DCO6VAxx4xU+1ztvFzmXTl3fUnAEc+2bFJK1MsRt6BWfG37XxjQN//mfmyS9KEBgsOpOyd4LQ==} + engines: {node: '>=14.18'} + peerDependencies: + react: ^16.14.0 || 17.x || 18.x || 19.x + + '@sentry/types@8.40.0': + resolution: {integrity: sha512-nuCf3U3deolPM9BjNnwCc33UtFl9ec15/r74ngAkNccn+A2JXdIAsDkGJMO/9mgSFykLe1QyeJ0pQFRisCGOiA==} + engines: {node: '>=14.18'} + + '@sentry/utils@8.40.0': + resolution: {integrity: sha512-JrfnrQ4irbXWTb+8QC5TCefr3KJJ1x4tJr5p+HyVy4df0n7SIvSqQNeG2P8uuT82F4puFsD6hkQYxuGr3y/NSw==} + engines: {node: '>=14.18'} + '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -4423,6 +4525,9 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + psl@1.15.0: resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} @@ -7515,6 +7620,115 @@ snapshots: component-type: 1.2.2 join-component: 1.1.0 + '@sentry-internal/browser-utils@8.40.0': + dependencies: + '@sentry/core': 8.40.0 + '@sentry/types': 8.40.0 + + '@sentry-internal/feedback@8.40.0': + dependencies: + '@sentry/core': 8.40.0 + '@sentry/types': 8.40.0 + + '@sentry-internal/replay-canvas@8.40.0': + dependencies: + '@sentry-internal/replay': 8.40.0 + '@sentry/core': 8.40.0 + '@sentry/types': 8.40.0 + + '@sentry-internal/replay@8.40.0': + dependencies: + '@sentry-internal/browser-utils': 8.40.0 + '@sentry/core': 8.40.0 + '@sentry/types': 8.40.0 + + '@sentry/babel-plugin-component-annotate@2.20.1': {} + + '@sentry/browser@8.40.0': + dependencies: + '@sentry-internal/browser-utils': 8.40.0 + '@sentry-internal/feedback': 8.40.0 + '@sentry-internal/replay': 8.40.0 + '@sentry-internal/replay-canvas': 8.40.0 + '@sentry/core': 8.40.0 + '@sentry/types': 8.40.0 + + '@sentry/cli-darwin@2.38.2': + optional: true + + '@sentry/cli-linux-arm64@2.38.2': + optional: true + + '@sentry/cli-linux-arm@2.38.2': + optional: true + + '@sentry/cli-linux-i686@2.38.2': + optional: true + + '@sentry/cli-linux-x64@2.38.2': + optional: true + + '@sentry/cli-win32-i686@2.38.2': + optional: true + + '@sentry/cli-win32-x64@2.38.2': + optional: true + + '@sentry/cli@2.38.2': + dependencies: + https-proxy-agent: 5.0.1 + node-fetch: 2.7.0 + progress: 2.0.3 + proxy-from-env: 1.1.0 + which: 2.0.2 + optionalDependencies: + '@sentry/cli-darwin': 2.38.2 + '@sentry/cli-linux-arm': 2.38.2 + '@sentry/cli-linux-arm64': 2.38.2 + '@sentry/cli-linux-i686': 2.38.2 + '@sentry/cli-linux-x64': 2.38.2 + '@sentry/cli-win32-i686': 2.38.2 + '@sentry/cli-win32-x64': 2.38.2 + transitivePeerDependencies: + - encoding + - supports-color + + '@sentry/core@8.40.0': + dependencies: + '@sentry/types': 8.40.0 + + '@sentry/react-native@6.3.0(expo@52.0.23(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@expo/metro-runtime@4.0.0(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.18)(react@18.3.1)))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1)': + dependencies: + '@sentry/babel-plugin-component-annotate': 2.20.1 + '@sentry/browser': 8.40.0 + '@sentry/cli': 2.38.2 + '@sentry/core': 8.40.0 + '@sentry/react': 8.40.0(react@18.3.1) + '@sentry/types': 8.40.0 + '@sentry/utils': 8.40.0 + react: 18.3.1 + react-native: 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.18)(react@18.3.1) + optionalDependencies: + expo: 52.0.23(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@expo/metro-runtime@4.0.0(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.18)(react@18.3.1)))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1) + transitivePeerDependencies: + - encoding + - supports-color + + '@sentry/react@8.40.0(react@18.3.1)': + dependencies: + '@sentry/browser': 8.40.0 + '@sentry/core': 8.40.0 + '@sentry/types': 8.40.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + + '@sentry/types@8.40.0': {} + + '@sentry/utils@8.40.0': + dependencies: + '@sentry/core': 8.40.0 + '@sentry/types': 8.40.0 + '@sinclair/typebox@0.27.8': {} '@sinonjs/commons@3.0.1': @@ -11158,6 +11372,8 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + proxy-from-env@1.1.0: {} + psl@1.15.0: dependencies: punycode: 2.3.1 From 252365cef467dccf266087be1905f7c05fe8de35 Mon Sep 17 00:00:00 2001 From: awongh Date: Wed, 12 Feb 2025 14:15:03 +0100 Subject: [PATCH 2/2] fill in app.json org and project names --- nr-app/app.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nr-app/app.json b/nr-app/app.json index dc4fdc8..2f94c8c 100644 --- a/nr-app/app.json +++ b/nr-app/app.json @@ -46,10 +46,8 @@ [ "@sentry/react-native/expo", { - "organization": "sentry org slug, or use the `SENTRY_ORG` environment variable", - "project": "sentry project name, or use the `SENTRY_PROJECT` environment variable", - // If you are using a self-hosted instance, update the value of the url property - // to point towards your self-hosted instance. For example, https://self-hosted.example.com/. + "organization": "trustroots-foundation-fo", + "project": "react-native", "url": "https://sentry.io/" } ]