diff --git a/content/assets/images/guide/ns-sentry-android.png b/content/assets/images/guide/ns-sentry-android.png new file mode 100644 index 00000000..58257664 Binary files /dev/null and b/content/assets/images/guide/ns-sentry-android.png differ diff --git a/content/assets/images/guide/ns-sentry-ios-native.png b/content/assets/images/guide/ns-sentry-ios-native.png new file mode 100644 index 00000000..ac376508 Binary files /dev/null and b/content/assets/images/guide/ns-sentry-ios-native.png differ diff --git a/content/assets/images/guide/ns-sentry-ios.png b/content/assets/images/guide/ns-sentry-ios.png new file mode 100644 index 00000000..7cd0db95 Binary files /dev/null and b/content/assets/images/guide/ns-sentry-ios.png differ diff --git a/content/guide/crash-reporting-sentry.md b/content/guide/crash-reporting-sentry.md new file mode 100644 index 00000000..3d52265a --- /dev/null +++ b/content/guide/crash-reporting-sentry.md @@ -0,0 +1,284 @@ +--- +title: Crash Reporting with Sentry +description: When your app breaks, fix it faster with Sentry. +contributors: + - NathanWalker +--- + + + +Gain critical insights into app issues by integrating [Sentry](https://sentry.io), an advanced error tracking and performance monitoring service. Follow this guide to set up Sentry in your NativeScript application using the [@nativescript-community/sentry](https://github.com/nativescript-community/sentry) plugin. + +--- + +## Step 1: Create a Sentry Account and Project + +First, [sign up](https://sentry.io) for a Sentry account and create a new project specifically for your NativeScript app. + +## Step 2: Install the Plugin + +Install the Sentry SDK into your NativeScript project: + +```bash +npm install @nativescript-community/sentry +``` + +## Step 3: Set Up Environment Variables + +Create a `.env` file at the root of your project with these Sentry-related variables: + +- **`SENTRY_ORG_SLUG`**: Found under your project's Organization Settings. +- **`SENTRY_PROJECT_SLUG_IOS` & `SENTRY_PROJECT_SLUG_ANDROID`**: Identifiers for your iOS and Android projects. +- **`SENTRY_DSN_IOS` & `SENTRY_DSN_ANDROID`**: Obtain from `[Project] > Settings > Client Keys (DSN)`. +- **`SENTRY_AUTH_TOKEN`**: Generate via your Sentry account under User Auth Tokens. + +Example `.env`: + +```bash +SENTRY_ORG_SLUG=nativescript +SENTRY_PROJECT_SLUG_IOS=myapp-ios +SENTRY_PROJECT_SLUG_ANDROID=myapp-android +SENTRY_DSN_IOS=your-ios-dsn +SENTRY_DSN_ANDROID=your-android-dsn +SENTRY_AUTH_TOKEN=your-auth-token +``` + +Replace the above placeholders with your actual Sentry details. + +## Step 4: Configure Webpack + +We will use Webpack to manage environment variables and source maps with plugins: + +```bash +npm install plist @sentry/webpack-plugin dotenv -D +``` + +Update your `webpack.config.js` to configure Sentry integration: + +```js +const webpack = require('@nativescript/webpack') +const { resolve, join, relative } = require('path') +const { readFileSync } = require('fs') +const { parse } = require('plist') +// load .env without having to specify cli env flags +require('dotenv').config() + +const SentryCliPlugin = require('@sentry/webpack-plugin').sentryWebpackPlugin +const SourceMapDevToolPlugin = require('webpack').SourceMapDevToolPlugin + +const SENTRY_PREFIX = process.env.SENTRY_PREFIX || 'app:///' +const SENTRY_SOURCE_MAP_PATH = join(__dirname, 'dist', 'sourcemaps') + +module.exports = (env) => { + webpack.init(env) + + webpack.chainWebpack((config) => { + const isStoreBuild = !!env.production + const sentryDev = !isStoreBuild && !!env['sentryDev'] + + const platform = webpack.Utils.platform.getPlatformName() + const projectSlug = + platform === 'android' + ? process.env.SENTRY_PROJECT_SLUG_ANDROID + : process.env.SENTRY_PROJECT_SLUG_IOS + const versionString = + platform === 'android' + ? readFileSync( + resolve(__dirname, 'App_Resources/Android/app.gradle'), + 'utf8', + ).match(/versionName\s+"([^"]+)"/)[1] + : parse( + readFileSync( + resolve(__dirname, 'App_Resources/iOS/Info.plist'), + 'utf8', + ), + )['CFBundleShortVersionString'] + + const SENTRY_DIST = sentryDev ? `dev-${Date.now()}` : `${Date.now()}` + const SENTRY_RELEASE = sentryDev ? SENTRY_DIST : versionString + + config.plugin('DefinePlugin').tap((args) => { + Object.assign(args[0], { + __SENTRY_DIST__: `'${SENTRY_DIST}'`, + __SENTRY_RELEASE__: `'${SENTRY_RELEASE}'`, + __SENTRY_ENVIRONMENT__: `'${ + isStoreBuild ? 'production' : 'development' + }'`, + __ENABLE_SENTRY__: isStoreBuild || sentryDev, + __SENTRY_PREFIX__: `'${SENTRY_PREFIX}'`, + __SENTRY_DSN_IOS__: JSON.stringify(process.env.SENTRY_DSN_IOS), + __SENTRY_DSN_ANDROID__: JSON.stringify(process.env.SENTRY_DSN_ANDROID), + }) + return args + }) + + if (isStoreBuild || sentryDev) { + config.devtool(false) + + config + .plugin('SourceMapDevToolPlugin|sentry') + .use(SourceMapDevToolPlugin, [ + { + append: `\n//# sourceMappingURL=${SENTRY_PREFIX}[name].js.map`, + filename: relative( + webpack.Utils.platform.getAbsoluteDistPath(), + join(SENTRY_SOURCE_MAP_PATH, '[name].js.map'), + ), + }, + ]) + + config + .plugin('SentryCliPlugin') + .init(() => + SentryCliPlugin({ + org: process.env.SENTRY_ORG_SLUG, + project: projectSlug, + // force ignore non-legacy sourcemaps + sourcemaps: { + assets: '/dev/null', + }, + release: { + uploadLegacySourcemaps: { + paths: [ + join(__dirname, 'dist', 'sourcemaps'), + webpack.Utils.platform.getAbsoluteDistPath(), + ], + urlPrefix: SENTRY_PREFIX, + }, + dist: SENTRY_DIST, + cleanArtifacts: true, + deploy: { + env: sentryDev ? 'development' : 'production', + }, + setCommits: { + auto: true, + ignoreMissing: true, + }, + ...(SENTRY_RELEASE ? { name: SENTRY_RELEASE } : {}), + }, + authToken: process.env.SENTRY_AUTH_TOKEN, + }), + ) + .use(SentryCliPlugin) + + config.optimization.minimizer('TerserPlugin').tap((args) => { + // we format here otherwise the sourcemaps will be broken + args[0].terserOptions.format = { + ...args[0].terserOptions.format, + max_line_len: 1000, + indent_level: 1, + } + return args + }) + } + }) + + return webpack.resolveConfig() +} +``` + +## Step 5: Initialize Sentry in Your App + +Create `sentry.ts` to initialize Sentry: + +```ts +import { Application, Trace, TraceErrorHandler } from '@nativescript/core' +import * as Sentry from '@nativescript-community/sentry' + +declare const __SENTRY_DIST__: string +declare const __SENTRY_RELEASE__: string +declare const __SENTRY_ENVIRONMENT__: string +declare const __ENABLE_SENTRY__: boolean +declare const __SENTRY_PREFIX__: string +declare const __SENTRY_DSN_IOS__: string +declare const __SENTRY_DSN_ANDROID__: string + +let initialized = false +export function initSentry() { + if (initialized || !__ENABLE_SENTRY__) return + initialized = true + + Sentry.init({ + dsn: __APPLE__ ? __SENTRY_DSN_IOS__ : __SENTRY_DSN_ANDROID__, + debug: __DEV__, + enableAppHangTracking: false, + enableNativeCrashHandling: true, + enableAutoPerformanceTracking: true, + enableAutoSessionTracking: true, + attachScreenshot: false, + dist: __SENTRY_DIST__, + release: __SENTRY_RELEASE__, + environment: __SENTRY_ENVIRONMENT__, + appPrefix: __SENTRY_PREFIX__, + appHangsTimeoutInterval: 5, + }) + + Application.on('uncaughtError', (event) => + Sentry.captureException(event.error), + ) + Application.on('discardedError', (event) => + Sentry.captureException(event.error), + ) + Trace.setErrorHandler(errorHandler) +} + +const errorHandler: TraceErrorHandler = { + handlerError(error: Error) { + if (__DEV__) { + // (development) - log it + console.error(error) + // (development) - or use Trace writing (categorical logging) + Trace.write(error, Trace.categories.Error) + // (development) - throw it + throw error + } + + // (production) - send it to sentry + Sentry.captureException(error) + }, +} +``` + +In your main bootstrap file (`app.ts` or `main.ts`), initialize on launch: + +```ts +import { initSentry } from './sentry' + +Application.on('launch', () => { + initSentry() +}) +``` + +## Step 6: Test Your Setup + +Trigger a test crash to verify setup: + +```ts +throw new Error('Sentry test crash') +``` + +For native crashes: + +- iOS: + +```ts +NSString.stringWithString(null) +``` + +- Android: + +```ts +new java.lang.String(null) +``` + +Your crashes should appear in your Sentry dashboard shortly after triggering. + +Sentry Crash Reporting with iOS + +Sentry Crash Reporting with iOS for Native Stacktraces + +Sentry Crash Reporting with Android + +--- + +You're now successfully integrated with Sentry, gaining powerful insights into your app's performance and stability. diff --git a/content/sidebar.ts b/content/sidebar.ts index a4079e7f..599d93f5 100644 --- a/content/sidebar.ts +++ b/content/sidebar.ts @@ -209,6 +209,15 @@ export default [ } ], }, + { + text: 'Crash Reporting', + items: [ + { + text: 'Using Sentry', + link: '/guide/crash-reporting-sentry', + }, + ] + }, { text: 'Developing with Vision Pro', items: [