diff --git a/examples/sdk/reactNative/README.md b/examples/sdk/reactNative/README.md index 23e34e42..e0e2af5e 100644 --- a/examples/sdk/reactNative/README.md +++ b/examples/sdk/reactNative/README.md @@ -31,44 +31,54 @@ module.exports = mergeConfig(getDefaultConfig(__dirname), config); ``` -Add Backtrace to build automation to ensure every build has source map support. - -In order to upload source maps to Backtrace, you can: +Add the upload step to your native build. Every release build then uploads its source map. **On Android:** -Enable source map support in `app/build.gradle` by uncommenting hermes source map flags. With additional parameters, source maps will be generated. To automatically upload them to Backtrace, you can use the gradle task available the @backtrace/react-native library. +Hermes writes a source map for every release build unless a `hermesFlags` override drops `-output-source-map`. To +upload it, add the Backtrace script at the end of `android/app/build.gradle`: -`apply from: "$rootDir/../node_modules/@backtrace/react-native/android/upload-sourcemaps.gradle"` +```gradle +apply from: "$rootDir/../node_modules/@backtrace/react-native/android/upload-sourcemaps.gradle" +``` -Once you import the gradle task, you can simply add it to your flow for any build/assemble tasks. +Each release variant then uploads its own source map after `assemble` or `bundle` packages it. Debug variants upload +nothing. A failed upload fails the build. An existing `finalizedBy("uploadSourceMapsToBacktrace")` hook keeps working. + +To build a release without uploading, set `backtraceUploadSourceMaps=false` in `gradle.properties` or pass it on the +command line: -```gradle -tasks.matching { - it.name.startsWith("assemble") || it.name.startsWith("build") -}.configureEach { task -> - task.finalizedBy("uploadSourceMapsToBacktrace") -} +``` +./gradlew assembleRelease -PbacktraceUploadSourceMaps=false ``` -**On iOS.** +**On iOS:** -Modify the code in the `Bundle React Native code and images` step in the `Build Phases` of your xcode project setting. In the end of the script, you can include the code below, to upload source maps directly to Backtrace after generating the applicaiton. +In Xcode, select the app target, open `Build Phases` and expand `Bundle React Native code and images`. Add the lines +marked below around the phase's existing script. React Native writes a source map only when `SOURCEMAP_FILE` is +exported before its bundling line. ```bash +set -e +# added for Backtrace project_directory="$(pwd)/.." -# enable source map support export SOURCEMAP_FILE="$project_directory/main.jsbundle.map" -... +# existing lines of the phase, unchanged +WITH_ENVIRONMENT="$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh" +REACT_NATIVE_XCODE="$REACT_NATIVE_PATH/scripts/react-native-xcode.sh" + +/bin/sh -c "\"$WITH_ENVIRONMENT\" \"$REACT_NATIVE_XCODE\"" +# end of the existing lines -# upload source maps to Backtrace +# added for Backtrace source_map_upload="$project_directory/node_modules/@backtrace/react-native/scripts/ios-sourcemap-upload.sh" backtrace_js_config="$project_directory/.backtracejsrc" -/bin/sh -c "$source_map_upload $SOURCEMAP_FILE $CONFIGURATION_BUILD_DIR/.backtrace-sourcemap-id $backtrace_js_config $project_directory" - +/bin/bash "$source_map_upload" "$SOURCEMAP_FILE" "$CONFIGURATION_BUILD_DIR/.backtrace-sourcemap-id" "$backtrace_js_config" "$project_directory" ``` The Backtrace serializer writes the debug id file to `$CONFIGURATION_BUILD_DIR`. Under Product > Archive that folder differs from `$TARGET_BUILD_DIR`, and an archive built with `$TARGET_BUILD_DIR` uploads no source map. + +The full setup is in the [React Native Integration Guide](https://docs.saucelabs.com/error-reporting/language-integrations/react-native/#upload-source-maps). diff --git a/examples/sdk/reactNative/android/app/build.gradle b/examples/sdk/reactNative/android/app/build.gradle index bbb3d27a..4bb8a76a 100644 --- a/examples/sdk/reactNative/android/app/build.gradle +++ b/examples/sdk/reactNative/android/app/build.gradle @@ -133,9 +133,3 @@ dependencies { implementation jscFlavor } } - -tasks.matching { - it.name.startsWith("assemble") || it.name.startsWith("build") -}.configureEach { task -> - task.finalizedBy("uploadSourceMapsToBacktrace") -} diff --git a/examples/sdk/reactNative/ios/reactNative.xcodeproj/project.pbxproj b/examples/sdk/reactNative/ios/reactNative.xcodeproj/project.pbxproj index 667bd71d..4020916f 100644 --- a/examples/sdk/reactNative/ios/reactNative.xcodeproj/project.pbxproj +++ b/examples/sdk/reactNative/ios/reactNative.xcodeproj/project.pbxproj @@ -269,7 +269,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "set -e\nproject_directory=\"$(pwd)/..\"\n# enable source map support\nexport SOURCEMAP_FILE=\"$project_directory/main.jsbundle.map\"\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n\n# upload source maps to Backtrace\nsource_map_upload=\"$project_directory/node_modules/@backtrace/react-native/scripts/ios-sourcemap-upload.sh\"\nbacktrace_js_config=\"$project_directory/.backtracejsrc\"\n\n/bin/sh -c \"$source_map_upload $SOURCEMAP_FILE $CONFIGURATION_BUILD_DIR/.backtrace-sourcemap-id $backtrace_js_config $project_directory\" \n\n"; + shellScript = "set -e\nproject_directory=\"$(pwd)/..\"\n# enable source map support\nexport SOURCEMAP_FILE=\"$project_directory/main.jsbundle.map\"\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n\n# upload source maps to Backtrace\nsource_map_upload=\"$project_directory/node_modules/@backtrace/react-native/scripts/ios-sourcemap-upload.sh\"\nbacktrace_js_config=\"$project_directory/.backtracejsrc\"\n\n/bin/bash \"$source_map_upload\" \"$SOURCEMAP_FILE\" \"$CONFIGURATION_BUILD_DIR/.backtrace-sourcemap-id\" \"$backtrace_js_config\" \"$project_directory\"\n\n"; }; 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; diff --git a/packages/react-native/README.md b/packages/react-native/README.md index 44871f83..d535b8e8 100644 --- a/packages/react-native/README.md +++ b/packages/react-native/README.md @@ -3,16 +3,18 @@ [Backtrace](https://backtrace.io) captures and reports handled and unhandled exceptions in your production software so you can manage application quality through the complete product lifecycle. -The [@backtrace/react-native](#) SDK connects your JavaScript application to Backtrace. The basic integration is quick -and easy, after which you can explore the rich set of Backtrace features. +The [@backtrace/react-native](https://www.npmjs.com/package/@backtrace/react-native) SDK connects your JavaScript +application to Backtrace. This README covers setup and configuration. Symbolication is covered in the +[React Native Integration Guide](https://docs.saucelabs.com/error-reporting/language-integrations/react-native/). ## Table of Contents +1. [Supported Versions](#supported-versions) +1. [Supported Platforms](#supported-platforms) 1. [Basic Integration - Reporting your first errors](#basic-integration) - - [Install the package](#install-the-package) + - [Install the Package](#install-the-package) - [Integrate the SDK](#integrate-the-sdk) - - [Upload source maps](#upload-source-maps) - - [Deobfuscate ProGuard and R8 builds (Android)](#deobfuscate-proguard-and-r8-builds-android) + - [Upload Source Maps](#upload-source-maps) 1. [Error Reporting Features](#error-reporting-features) - [Attributes](#attributes) - [File Attachments](#file-attachments) @@ -21,25 +23,39 @@ and easy, after which you can explore the rich set of Backtrace features. - [Metrics Configuration](#metrics-configuration) - [Metrics Usage](#metrics-usage) - [ANR Detection](#anr-detection) - - [Offline Database support](#offline-database-support) + - [Offline Database Support](#offline-database-support) - [Database Configuration](#database-configuration) - - [Native crash support](#native-crash-support) - - [Manual database operations](#manual-database-operations) + - [Native Crash Support](#native-crash-support) + - [Manual Database Operations](#manual-database-operations) 1. [Advanced SDK Features](#advanced-sdk-features) - - [BacktraceClient Options](#backtraceclient) - - [Manually send an error](#manually-send-an-error) - - [Modify/skip error reports](#modifyskip-error-reports) + - [`BacktraceClient` Options](#backtraceclient) + - [Manually Send an Error](#manually-send-an-error) + - [Modify/Skip Error Reports](#modifyskip-error-reports) + - [Error Boundary](#error-boundary) - [SDK Method Overrides](#sdk-method-overrides) +## Supported Versions + +- React Native 0.72+, verified up to 0.87 +- React 18+ +- Hermes JavaScript engine (JavaScriptCore is not supported) +- New Architecture and Legacy Architecture (React Native 0.82 and later are New Architecture only) + +## Supported Platforms + +- iOS: minimum iOS version follows React Native (15.1 for React Native 0.76 and above). +- Android: minimum SDK version follows React Native (24 for React Native 0.76 and above). Native crash capture on + arm64-v8a, armeabi-v7a and x86_64. + ## Basic Integration -### Install the package +### Install the Package ``` $ npm install @backtrace/react-native ``` -On iOS, do not forget about installing cocoa pods +On iOS, install the pods after adding the package: ``` $ cd ios @@ -48,16 +64,17 @@ $ pod install ### Integrate the SDK -Add the following code to your application before all other scripts to report react-native errors to Backtrace. +Add the following code at the top of `index.js`, before `AppRegistry.registerComponent`. The SDK starts before other +application code runs. -```ts -// Import the BacktraceClient from @backtrace/react-native with your favorite package manager. -import { BacktraceClient, BacktraceConfiguration } from '@backtrace/react-native'; +```js +// Import the BacktraceClient from @backtrace/react-native +import { BacktraceClient } from '@backtrace/react-native'; // Configure client options -const options: BacktraceConfiguration = { +const options = { // Submission url - // is the subdomain of your Backtrace instance (.backtrace.io) + // is the subdomain of your Backtrace instance (.sp.backtrace.io) // can be found in Project Settings/Submission tokens url: 'https://submit.backtrace.io///json', database: { @@ -77,77 +94,39 @@ const client = BacktraceClient.initialize(options); client.send(new Error('Something broke!')); ``` -### Upload source maps +### Upload Source Maps Client-side error reports are based on minified code. Upload source maps and source code to resolve minified code to -your original source identifiers. - -Install the build-time tools in the application when using Backtrace's source-map scripts: - -```sh -npm install --save-dev @backtrace/sourcemap-tools@^0.2.5 @backtrace/javascript-cli -``` - -The serializer also needs Metro (`>=0.76.0`). Use the Metro version compatible with the application's React Native version; -React Native applications normally already provide it. Metro and `@backtrace/sourcemap-tools` are optional peers, installing the SDK alone does not install these tools. -The CLI package supplies the `backtrace-js` executable used by the iOS upload script; the Android upload task also uses `@backtrace/javascript-cli`. - -Configure `@backtrace/react-native/scripts/processSourceMap` as the Metro custom serializer before enabling the upload scripts. -See the [example application's source-map setup](https://github.com/backtrace-labs/backtrace-javascript/tree/main/examples/sdk/reactNative#source-maps) for Metro, Android and iOS configuration (these tools are optional for basic runtime error reporting). - -[(Source Map feature documentation)](https://docs.saucelabs.com/error-reporting/platform-integrations/source-map/) - -### Deobfuscate ProGuard and R8 builds (Android) +your original source identifiers. Source map symbolication applies to release builds. Debug builds run +unminified JavaScript from Metro and need no source maps. -Minified release builds obfuscate Java class and method names. Backtrace deobfuscates unhandled Java exception and -ANR reports with the mapping file uploaded under the report's `symbolication_id`. The package ships the keep rules -the native crash reporter needs. No ProGuard rules have to be added to the app. - -Generate a UUID for the build, pass it to the client, and upload that build's `mapping.txt` under the same id: - -```ts -const options: BacktraceConfiguration = { - url: 'https://submit.backtrace.io///json', - proguard: { - enable: true, - symbolicationId: '', - }, -}; -``` - -``` -curl --data-binary @android/app/build/outputs/mapping/release/mapping.txt -X POST -H "Expect:" "https://submit.backtrace.io///proguard?symbolication_id=" -``` - -JavaScript reports keep using source maps and native crash reports keep using native symbols. See -[Working with ProGuard](https://docs.saucelabs.com/error-reporting/platform-integrations/android/proguard-deobfuscation/) -for the full flow. +For the full React Native setup (Metro serializer, Android gradle task, iOS build phase), see +[Upload source maps in the React Native Integration Guide](https://docs.saucelabs.com/error-reporting/language-integrations/react-native/#upload-source-maps). ## Error Reporting Features ### Attributes Custom attributes are key-value pairs that can be added to your error reports. They are used in report aggregation, -sorting and filtering, can provide better contextual data for an error, and much more. They are foundational to many of -the advanced Backtrace features detailed in -[Error Reporting documentation](https://docs.saucelabs.com/error-reporting/getting-started/). By default attributes such -as application name and version are populated automatically. If Backtrace cannot find them, you need to provide them -manually via userAttributes attributes. +sorting and filtering, can provide better contextual data for an error, and much more. See +[Indexing Attributes](https://docs.saucelabs.com/error-reporting/project-setup/attributes/) for how they are indexed and used across +Backtrace. By default, attributes such as application name and version are populated automatically. If Backtrace cannot find them, you need to provide them +manually via the `userAttributes` option. -There are several places where attributes can be added, modified or deleted. +Attributes can be added, modified or deleted in several places. -#### Attach attributes object to BacktraceClient +#### Attach an Attributes Object to BacktraceClient -It is possible to include an attributes object during [BacktraceClient](#backtraceclient) initialization. This list of +It is possible to include an attributes object during [`BacktraceClient`](#backtraceclient) initialization. This list of attributes will be included with every error report, referred to as global attributes. ```ts -// Create an attributes object that can be modified throughout runtime +// Attributes sent with every report. Change them at runtime with client.addAttribute const attributes: Record = { release: 'PROD', }; -// BacktraceClientOptions +// Client options const options: BacktraceConfiguration = { url: 'https://submit.backtrace.io///json', @@ -159,16 +138,18 @@ const options: BacktraceConfiguration = { const client = BacktraceClient.initialize(options); ``` -You can also include attributes that will be resolved when creating a report: +Attributes given as a function are evaluated each time a report is created: ```ts -// BacktraceClientOptions +import { AppState } from 'react-native'; + +// Client options const options: BacktraceConfiguration = { url: 'https://submit.backtrace.io///json', - // Attach the attributes object + // Evaluated for every report userAttributes: () => ({ - attribute: getAttributeValue(), + 'app.state': AppState.currentState, }), }; @@ -176,9 +157,9 @@ const options: BacktraceConfiguration = { const client = BacktraceClient.initialize(options); ``` -#### Add attributes during application runtime +#### Add Attributes During Application Runtime -Global attributes can be set during the runtime once specific data has be loaded (e.g. a user has logged in). +Global attributes can also be set at runtime, for example after a user logs in. ```ts const client = BacktraceClient.initialize(options); @@ -189,20 +170,20 @@ client.addAttribute({ }) ``` -You can also add attributes that will be resolved when creating a report: +A function passed to `addAttribute` is evaluated each time a report is created: ```ts const client = BacktraceClient.initialize(options); ... client.addAttribute(() => ({ - "clientID": resolveCurrentClientId() -})) + 'session.uptime.seconds': Math.round(performance.now() / 1000), +})); ``` -#### Add attributes to an error report +#### Add Attributes to an Error Report -The attributes list of a BacktraceReport object can be directly modified. +The attributes list of a `BacktraceReport` object can be directly modified. ```ts const report: BacktraceReport = new BacktraceReport('My error message', { myReportKey: 'myValue' }); @@ -213,19 +194,16 @@ report.attributes['myReportKey'] = 'New value'; ### File Attachments -Files can be attached to error reports. This can be done when initalizing the BacktraceClient, updating the -BacktraceClient, or dynamically for specific reports. When including attachments in BacktraceClient, all files will be -uploaded with each report. +Files can be attached to every report through the client options or `client.addAttachment`, or to a single report +when it is created. See [File Attachments](https://docs.saucelabs.com/error-reporting/platform-integrations/file-attachments/). ```ts -// Import attachment types from @backtrace/react-native -import { BacktraceStringAttachment, BacktraceUint8ArrayAttachment } from "@backtrace/react-native"; +import { BacktraceClient, BacktraceReport, BacktraceStringAttachment } from "@backtrace/react-native"; -// BacktraceStringAttachment should be used for text object like a log file, for example +// BacktraceStringAttachment is for text content such as a log file const stringAttachment = new BacktraceStringAttachment("logfile.txt", "This is the start of my log") - -// BacktraceClientOptions +// Client options const options = { url: "https://submit.backtrace.io///json", @@ -235,18 +213,30 @@ const options = { const client = BacktraceClient.initialize(options); -// Later decide to add an attachment to all reports -client.addAttachment(stringAttachment) +// Later decide to add another attachment to all reports +client.addAttachment(new BacktraceStringAttachment("session.txt", "Added after initialization")) // After catching an exception and generating a report try { throw new Error("Caught exception!") } catch (error) { - const report = const report = new BacktraceReport(error, {}, [stringAttachment]) + const report = new BacktraceReport(error as Error, {}, [ + new BacktraceStringAttachment("error-context.txt", "Only on this report"), + ]); client.send(report); } ``` +JavaScript reports sent in the same session accept every attachment type. Native crash reports and reports sent on a +later launch include file attachments only. Use `BacktraceFileAttachment` for those: + +```ts +import { BacktraceClient, BacktraceFileAttachment, ReactNativeFileSystem } from '@backtrace/react-native'; + +const logPath = `${BacktraceClient.applicationDataPath}/app.log`; +client.addAttachment(new BacktraceFileAttachment(new ReactNativeFileSystem(), logPath, 'app.log')); +``` + --- ### Breadcrumbs @@ -254,24 +244,28 @@ try { Breadcrumbs are snippets of chronological data tracing runtime events. This SDK records a number of events by default, and manual breadcrumbs can also be added. -[(Breadcrumbs feature documentation)](https://docs.saucelabs.com/error-reporting/web-console/debug/#breadcrumbs) +See [Breadcrumbs in the web console](https://docs.saucelabs.com/error-reporting/web-console/debug/#breadcrumbs). #### Breadcrumbs Configuration -| Option Name | Type | Description | Default | Required? | -| -------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ------------------------ | -| `enable` | Boolean | Determines if the breadcrumbs support is enabled. By default the value is set to true. | `true` |
  • - [ ]
| -| `logLevel` | BreadcrumbLogLevel | Specifies which log level severity to include. By default all logs are included. | All Logs |
  • - [ ]
| -| `eventType` | BreadcrumbType | Specifies which breadcrumb type to include. By default all types are included. | All Types |
  • - [ ]
| -| `maximumBreadcrumbs` | Number | Specifies maximum number of breadcrumbs stored by the library. By default, only 100 breadcrumbs will be stored. | `100` |
  • - [ ]
| -| `intercept` | (breadcrumb: RawBreadcrumb) => RawBreadcrumb \| undefined; | Inspects breadcrumb and allows to modify it. If the undefined value is being returned from the method, no breadcrumb will be added to the breadcrumb storage. | All Breadcrumbs |
  • - [ ]
| +| Option Name | Type | Description | Default | +| -------------------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| `enable` | Boolean | Enables breadcrumb collection. | `true` | +| `logLevel` | BreadcrumbLogLevel | Bitmask of the log levels to include. Combine `BreadcrumbLogLevel` values with `\|`. A single value selects only that level. | All Logs | +| `eventType` | BreadcrumbType | Bitmask of the breadcrumb types to include. Combine `BreadcrumbType` values with `\|`. | All Types | +| `maximumBreadcrumbs` | Number | Maximum number of breadcrumbs stored. With the offline database enabled, between half this many and this many are kept. | `100` | +| `maximumAttributesDepth` | Number \| false | Maximum depth of nested objects in breadcrumb attributes. `false` removes the limit. | `2` | +| `maximumBreadcrumbMessageLength` | Number \| false | Maximum length of a breadcrumb message. `false` removes the limit. | `255` | +| `maximumBreadcrumbSize` | Number \| false | Maximum size of a single breadcrumb in bytes. Larger breadcrumbs are dropped. `false` removes the limit. | `65536` | +| `maximumTotalBreadcrumbsSize` | Number \| false | Maximum total size of stored breadcrumbs in bytes. With the offline database enabled, between half this size and this size is kept. `false` removes the limit. | `1048576` | +| `intercept` | (breadcrumb: RawBreadcrumb) => RawBreadcrumb \| undefined | Inspects and can modify each breadcrumb before it is stored. Return `undefined` to drop it. | | ```ts import { BacktraceClient, BacktraceConfiguration } from '@backtrace/react-native'; -// BacktraceClientOptions +// Client options const options: BacktraceConfiguration = { - // ignoring all but breadcrumbs config for simplicity + url: SUBMISSION_URL, breadcrumbs: { // breadcrumbs configuration }, @@ -283,15 +277,21 @@ const client = BacktraceClient.initialize(options); #### Default Breadcrumbs -| Type | Description | -| ------- | ------------------------------------------------------------------------ | -| Console | Adds a breadcrumb every time console log is being used by the developer. | +| Type | Description | +| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Console | Adds a breadcrumb for every `console.log`, `console.warn`, `console.error`, `console.debug` and `console.trace` call. `console.info` and other methods are not recorded. | +| HTTP | Adds a breadcrumb for every `fetch` and `XMLHttpRequest` request, with the method, the full URL and the status. | +| Application state | Adds a breadcrumb when the application becomes active or moves to the background, when it becomes inactive on iOS, and on blur and focus on Android. | +| Memory warning | Adds a warning breadcrumb when iOS reports low memory. Not available on Android. | +| Dimension change | Adds a breadcrumb when the window or screen size changes. | +| Report | Adds a breadcrumb with the message of each report passed to `send`, including reports that `skipReport` drops. | #### Intercepting Breadcrumbs If PII or other information needs to be filtered from a breadcrumb, you can use the intercept function to skip or filter -out the sensitive information. Any RawBreadcrumb returned will be used for the breadcrumb. If undefined is returned, no -breadcrumb will be added. +out the sensitive information. Any `RawBreadcrumb` returned will be used for the breadcrumb. If undefined is returned, no +breadcrumb will be added. HTTP breadcrumbs record the full request URL, query string included, and the SDK's own +submission requests carry the submission token in the URL. #### Manual Breadcrumbs @@ -300,7 +300,7 @@ your own. ```ts client.breadcrumbs?.info('This is a manual breadcrumb.', { - customAttr: 'wow!', + customAttr: 'value', }); ``` @@ -308,18 +308,18 @@ client.breadcrumbs?.info('This is a manual breadcrumb.', { ### Application Stability Metrics -The Backtrace react-native SDK has the ability to send usage Metrics to be viewable in the Backtrace UI. +The SDK can send application stability metrics, viewable in the Backtrace UI. -[(Stability Metrics feature documentation)](https://docs.saucelabs.com/error-reporting/project-setup/stability-metrics/) +See [Stability Metrics](https://docs.saucelabs.com/error-reporting/project-setup/stability-metrics/). #### Metrics Configuration -| Option Name | Type | Description | Default | Required? | -| ---------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ------------------------ | -| `metricsSubmissionUrl` | String | Metrics server hostname. By default the value is set to https://events.backtrace.io. | `https://events.backtrace.io` |
  • - [ ]
| -| `enable` | Boolean | Determines if the metrics support is enabled. By default the value is set to true. | `true` |
  • - [ ]
| -| `autoSendInterval` | Number | Indicates how often crash free metrics are sent to Backtrace. The interval is a value in ms. By default, session events are sent on application startup/finish, and every 30 minutes while the application is running. If the value is set to 0. The auto send mode is disabled. In this situation the application needs to maintain send mode manually. | On application startup/finish |
  • - [ ]
| -| `size` | Number | Indicates how many events the metrics storage can store before auto submission. | `50` |
  • - [ ]
| +| Option Name | Type | Description | Default | +| ---------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | +| `metricsSubmissionUrl` | String | Metrics server hostname. | `https://events.backtrace.io` | +| `enable` | Boolean | Enables stability metrics. | `true` | +| `autoSendInterval` | Number | Interval in ms between metrics submissions. Session events are sent at startup and then on this interval. Nothing is sent at exit. With `0`, events go out only from `client.metrics.send()` or when a queue reaches `size`. | `1800000` | +| `size` | Number | Maximum events stored before automatic submission. | `50` | #### Metrics Usage @@ -332,19 +332,15 @@ client.metrics?.send(); ### ANR Detection -The Backtrace react-native SDK can detect Application Not Responding errors on Android and report them with the -`Hang` error type. The report carries the stack traces of every thread, with the blocked main thread as the -faulting one. +The SDK can detect Application Not Responding (ANR) errors on Android, where the main thread stays blocked for longer +than a set time (default 5 seconds), and reports them as `Hang` errors. Two detection methods are available: -Two detection mechanisms are available: - -- `BacktraceAnrType.Threshold` monitors the main thread from a separate thread. If the main thread remains - unresponsive for longer than the configured timeout (default: 5 seconds), an ANR is reported immediately, while - the application is still hung. It works on all supported Android versions. -- `BacktraceAnrType.ApplicationExit` retrieves the ANRs the system recorded for previous runs of the process from - `ApplicationExitInfo` and reports them on the next application start, using the thread dump the system captured - when it declared the ANR. The full dump is also attached as `anr-stacktrace.txt`. It requires API 30 or above - and survives the process kill. Reported records are remembered, so each ANR is reported once. +- `BacktraceAnrType.Threshold` watches the main thread from a background thread and reports while the application is + still hung. It works on all supported Android versions. The check runs once per `timeout` period, and a hang + shorter than two periods can go unreported. +- `BacktraceAnrType.ApplicationExit` reads the ANRs Android recorded for earlier runs of the application (Android 11, + API 30, and above) and reports them on the next start. The thread dump Android captured is attached as + `anr-stacktrace.txt`. Each ANR is reported once. ```ts import { BacktraceAnrType, BacktraceConfiguration } from '@backtrace/react-native'; @@ -358,122 +354,129 @@ const options: BacktraceConfiguration = { }; ``` -| Option Name | Type | Description | Default | Required? | -| ----------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------------ | -| `enable` | Boolean | Determines if ANR detection is enabled. | `false` |
  • - [ ]
| -| `type` | `BacktraceAnrType` | Detection mechanism: `Threshold` or `ApplicationExit`. | `Threshold` |
  • - [ ]
| -| `timeout` | Number | Time in milliseconds the main thread stays blocked before an ANR is reported. Applies to the `Threshold` type only. | `5000` |
  • - [ ]
| -| `disableWhenDebuggerAttached` | Boolean | When true, detection is disabled while a debugger is attached. Applies to the `Threshold` type only. | `false` |
  • - [ ]
| +| Option Name | Type | Description | Default | +| ----------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `enable` | Boolean | Enables ANR detection. Android only. | `false` | +| `type` | `BacktraceAnrType` | Detection mechanism: `Threshold` or `ApplicationExit`. | `Threshold` | +| `timeout` | Number | Length in milliseconds of one main-thread check period. Applies to the `Threshold` type only. | `5000` | +| `disableWhenDebuggerAttached` | Boolean | When true and a debugger is attached at initialization, detection stays off for that session. Applies to the `Threshold` type only. | `false` | --- -### Offline database support +### Offline Database Support The Backtrace react-native SDK can cache generated reports and crashes to local disk before sending them to Backtrace. -This is recommended; in certain configurations react-native applications can crash before the SDK finishes submitting -data, and under slow internet conditions your application might wait in a closing window until the HTTP submission -finishes. In such an event cached reports will be sent on next application launch. +Enabling it is recommended: an application can crash before the SDK finishes sending, and on a slow network a closing +application may not finish the upload. Cached reports are sent on the next launch. -With offline database support you can: +With the offline database enabled, the SDK: -- cache your reports when the user doesn't have an internet connection or the service is unavailable, -- capture crashes, -- manually decide whether or not to send reports, and when. +- Keeps reports while the device is offline or the service is unavailable. +- Captures crashes. +- Lets you decide whether and when to send reports. -Offline database support is disabled by default. To enable it, please add "enable: true" and the path to the directory -where Backtrace can store crash data. +Offline database support is disabled by default. To enable it, set `enable: true` and the path to the directory where +Backtrace can store crash data. Set `createDatabaseDirectory: true` to let the SDK create the directory. ```ts const client = BacktraceClient.initialize({ - // ignoring all but database config for simplicity + url: SUBMISSION_URL, database: { enable: true, path: `${BacktraceClient.applicationDataPath}/path/to/dir`, + createDatabaseDirectory: true, captureNativeCrashes: true, }, }); - -// manually send and keep the data on connection issue -client.database.send(); -// manually send and remove all data no matter if received success or not. -client.database.flush(); ``` -Backtrace client exposes a method `applicationDataPath` that can help you create the path to your database directory. +`BacktraceClient.applicationDataPath` returns a base path for the database directory: -The helper returns: - -- Android: Path to the files directory available in the application context, -- iOS: Path to the application cache directory +- Android: the files directory of the application context +- iOS: the application cache directory #### Database Configuration -| Option Name | Type | Description | Default | Required? | -| ------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------ | -| `enabled` | Boolean | Enable/disable offline database support. | false |
  • - [x]
| -| `path` | String | Local storage path for crash data. | - |
  • - [x]
| -| `createDatabaseDirectory` | Boolean | Allow the SDK to create the offline database directory. | true | -| `autoSend` | Boolean | Sends reports to the server based on the retry settings. If the value is set to 'false', you can use the Flush or Send methods as an alternative. | true | -| `maximumNumberOfRecords` | Number | The maximum number of reports stored in the offline database. When the limit is reached, the oldest reports are removed. If the value is equal to '0', then no limit is set. | 8 | -| `retryInterval` | Number | The amount of time (in ms) to wait between retries if the database is unable to send a report. | 60 000 | -| `maximumRetries` | Number | The maximum number of retries to attempt if the database is unable to send a report. | 3 | -| `captureNativeCrashes` | Boolean | Capture and symbolicate stack traces for native crashes if the runtime supports this. A crash report is generated, stored locally, and uploaded upon next start. | false | - -#### Native crash support - -The Backtrace React-Native SDK can capture crashes generated in the native layer. Those crashes cannot be captured on -the JavaScript side by using any kind of helpers. In order to collect them, the SDK uses native crash reporting -solutions available in the package. +| Option Name | Type | Description | Default | +| ---------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `enable` | Boolean | Enable/disable offline database support. | `false` | +| `path` | String | Required when the database is enabled. Local storage path for crash data. | | +| `createDatabaseDirectory` | Boolean | Create the `path` directory when it does not exist. With `false`, the directory has to exist before `BacktraceClient.initialize` is called. | `false` | +| `autoSend` | Boolean | Sends stored reports at startup and then every `retryInterval`. With `false`, stored reports wait for `client.database?.send()` or `client.database?.flush()`. New reports are still sent when they are created. | `true` | +| `maximumNumberOfRecords` | Number | The maximum number of reports stored in the offline database. When the limit is reached, the oldest reports are removed. With `0`, stored reports from earlier sessions are deleted at startup. | `8` | +| `retryInterval` | Number | Time in ms between retries when sending stored reports fails. | `60000` | +| `maximumNumberOfAttachmentRecords` | Number | The maximum number of attachments stored in the offline database. When the limit is reached, the oldest attachments are removed. | `10` | +| `maximumRetries` | Number | Send attempts per session. An attempt stops at the first report that fails. Reports that still fail are retried on the next launch. | `3` | +| `maximumOldSessions` | Number | The number of previous sessions whose files, such as breadcrumbs, are kept on disk. | `1` | +| `captureNativeCrashes` | Boolean | Capture crashes in the native layer. Requires `enable: true`. Android sends the report at crash time. iOS sends it on the next launch. See [Native Crash Support](#native-crash-support). | `false` | + +#### Native Crash Support + +The Backtrace React-Native SDK can capture crashes generated in the native layer. JavaScript code cannot observe them. +The SDK collects them with the native crash reporters bundled in the package. + +The native crash reporter uploads attributes and file attachments with the report. Some details differ from +JavaScript reports: + +- Attributes provided as callbacks are not evaluated. Attribute values set at initialization or later with + `addAttribute` are included. +- In-memory attachments (`BacktraceStringAttachment`, `BacktraceUint8ArrayAttachment`) are not included. File + attachments are. +- Breadcrumbs recorded up to the moment of the crash are attached. +- `beforeSend` and `skipReport` do not run for native crashes. +- Uncaught Java exceptions and errors are sent before the process exits or on the next launch. iOS native crashes + are sent on the next launch. +- Android native (NDK) crashes are sent at crash time by a separate crash-handler process. +- On iOS, the SDK also sends an `OOMException` report on the next launch when the previous session ended in the + foreground without a crash. Debugger sessions and OS or application updates are excluded. A forced kill in the + foreground is reported the same way. + +#### Manual Database Operations + +The `BacktraceDatabase` instance, available as `client.database`, sends or discards the stored reports on demand. Use it +when `autoSend` is disabled. `send()` stops at the first report that fails and keeps the rest for a later attempt. +`flush()` sends the reports, then removes all of them, sent or not. `client.database` is `undefined` while the offline +database is disabled. -Attributes and file attachments will be uploaded by native crash reporting solution. Some important details: - -- Dynamic attributes will not be added. -- In-memory attachments such as string attachments, are not supported. -- You cannot apply any managed layer (JavaScript) callbacks to filter or modify crashes before send. -- Java exceptions and iOS crashes will be available after the application restart. All crashes and reports send after - application restart are stored in the database directory. -- On Android, all native crashes from the NDK layer will be sent in the same application session (from a separate - application process). - -#### Manual database operations - -Database support is available in the client options with the BacktraceDatabase object. You can use it to manually -operate on database records. +```ts +client.database?.send(); +client.database?.flush(); +``` ## Advanced SDK Features ### BacktraceClient -BacktraceClient is the main SDK class. Error monitoring starts when this singleton object is instantiated, and it will +`BacktraceClient` is the main SDK class. Error monitoring starts when this singleton object is instantiated, and it will compose and send reports for unhandled errors and unhandled promise rejections. It can also be used to manually send reports from exceptions and rejection handlers. Do not create more than one instance of this object. #### BacktraceClientOptions -The following options are available for the BacktraceClientOptions passed when initializing the BacktraceClient. - -| Option Name | Type | Description | Default | Required? | -| ----------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | ------------------------ | -| `url` | String | Submission URL to send errors to | |
  • - [x]
| -| `token` | String | The submission token for error injestion. This is required only if submitting directly to a Backtrace URL. (uncommon) | |
  • - [ ]
| -| `userAttributes` | Dictionary | Additional attributes that can be filtered and aggregated against in the Backtrace UI. | |
  • - [ ]
| -| `attachments` | BacktraceAttachment[] | Additional files to be sent with error reports. See [File Attachments](#file-attachments) | |
  • - [ ]
| -| `beforeSend` | (data: BacktraceData) => BacktraceData \| undefined | Triggers an event every time an exception in the managed environment occurs, which allows you to skip the report (by returning a null value) or to modify data that library collected before sending the report. You can use the BeforeSend event to extend attributes or JSON object data based on data the application has at the time of exception. See [Modify/skip error reports](#modifyskip-error-reports)) | |
  • - [ ]
| -| `skipReport` | (report: BacktraceReport) => boolean | If you want to ignore specific types of error reports, we recommend that you use the skipReport callback. By using it, based on the data generated in the report, you can decide to filter the report, or send it to Backtrace. | |
  • - [ ]
| -| `captureUnhandledErrors` | Boolean | Enable unhandled errors | `true` |
  • - [ ]
| -| `captureUnhandledPromiseRejections` | Boolean | Enable unhandled promise rejection | `true` |
  • - [ ]
| -| `timeout` | Integer | How long to wait in ms before timing out the connection | `15000` |
  • - [ ]
| -| `ignoreSslCertificate` | Boolean | Ignore SSL Certificate errors | `false` |
  • - [ ]
| -| `rateLimit` | Integer | Limits the number of reports the client will send per minute. If set to '0', there is no limit. If set to a value greater than '0' and the value is reached, the client will not send any reports until the next minute. | `0` |
  • - [ ]
| -| `metrics` | BacktraceMetricsOptions | See [Backtrace Stability Metrics](#application-stability-metrics) | |
  • - [ ]
| -| `breadcrumbs` | BacktraceBreadcrumbsSettings | See [Backtrace Breadcrumbs](#breadcrumbs) | |
  • - [ ]
| -| `database` | BacktraceDatabaseSettings | See [Backtrace Database](#offline-database-support) | |
  • - [ ]
| -| `proguard` | BacktraceProguardConfiguration | See [Deobfuscate ProGuard and R8 builds](#deobfuscate-proguard-and-r8-builds-android) | |
  • - [ ]
| - -### Manually send an error +Pass these options to `BacktraceClient.initialize`. Only `url` is required. + +| Option Name | Type | Description | Default | +| ----------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `url` | String | Required. Submission URL to send errors to. | | +| `token` | String | Submission token for error ingestion. Needed only when submitting directly to a Backtrace URL (uncommon). Native crash and iOS `OOMException` reports ignore it. For those, include the token in `url`. | | +| `userAttributes` | `Record \| (() => Record)` | Additional attributes that can be filtered and aggregated against in the Backtrace UI. A function is evaluated for every report. | | +| `attachments` | BacktraceAttachment[] | Additional files to be sent with error reports. See [File Attachments](#file-attachments) | | +| `beforeSend` | (data: BacktraceData) => BacktraceData \| undefined | Runs before each JavaScript report is sent. Modify the report data, or return `undefined` to skip it. See [Modify/Skip Error Reports](#modifyskip-error-reports) | | +| `skipReport` | (report: BacktraceReport) => boolean | Runs for each JavaScript report. Return `true` to drop it. | | +| `captureUnhandledErrors` | Boolean | Capture uncaught errors | `true` | +| `captureUnhandledPromiseRejections` | Boolean | Capture unhandled promise rejections | `true` | +| `timeout` | Integer | Time in ms before a JavaScript report or metrics request times out. Native crash uploads do not use it. | `15000` | +| `rateLimit` | Integer | Limits the number of reports the client sends in any 60-second window. If set to '0', there is no limit. Reports over the limit are dropped, not queued or stored. | `0` | +| `metrics` | BacktraceMetricsOptions | See [Backtrace Stability Metrics](#application-stability-metrics) | | +| `breadcrumbs` | BacktraceBreadcrumbsSettings | See [Backtrace Breadcrumbs](#breadcrumbs) | | +| `database` | BacktraceDatabaseConfiguration | See [Backtrace Database](#offline-database-support) | | +| `anr` | BacktraceAnrConfiguration | See [ANR Detection](#anr-detection) | | +| `proguard` | BacktraceProguardConfiguration | See [Deobfuscate ProGuard and R8 Builds](https://docs.saucelabs.com/error-reporting/language-integrations/react-native/#deobfuscate-proguard-and-r8-builds) | | + +### Manually Send an Error There are several ways to send an error to Backtrace. For more details on the definition of `client.send()` see -[Methods](#methods) below. +methods below. ```ts // send as a string @@ -489,39 +492,66 @@ await client.send(new BacktraceReport('This is a report with a string!')); await client.send(new BacktraceReport(new Error('This is a report with a string!'))); ``` -### Modify/skip error reports +### Modify/Skip Error Reports -A BeforeSend event is triggered when an exception in the managed environment occurs to which you can attach a handler. -You can use the BeforeSend event to scrub PII, or extend attributes or JSON object data based on data your application -has at the time of exception. A report can be skipped sompletely by returning a null value. +The `beforeSend` callback runs before every report is sent. Use it to scrub PII or to extend attributes with data the +application has at the time of the exception. Return `undefined` to skip the report. ```ts const client = BacktraceClient.initialize({ url: SUBMISSION_URL, beforeSend: (data: BacktraceData) => { - // skip the report by returning a null from the callback + // skip the report by returning undefined from the callback if (!shouldSendReportToBacktrace(data)) { return undefined; } // apply custom attribute - data.attributes['new-attribute"] = 'apply-data-in-callback'; + data.attributes['new-attribute'] = 'apply-data-in-callback'; return data; }, }); ``` +### Error Boundary + +Render errors that no error boundary catches are reported as fatal unhandled exceptions. `ErrorBoundary` reports +the errors in its subtree and keeps the app running with a fallback. Initialize `BacktraceClient` before the boundary +renders, then wrap your component tree: + +```tsx +import { ErrorBoundary } from '@backtrace/react-native'; +import { Text } from 'react-native'; + +export default function App() { + return ( + Something went wrong.}> + + + ); +} +``` + +The report includes the React component stack as a separate `component-stack` thread and sets the +`errorboundary.name` attribute (default `main`). `fallback` is an element, or a function that takes the error and +returns an element. Without a valid fallback, the boundary renders nothing after an error. + ### SDK Method Overrides -BacktraceClient.builder is used to override default BacktraceClient methods. File and http operation overrides, for +`BacktraceClient.builder` is used to override default `BacktraceClient` methods. File and http operation overrides, for example, can be used to implement custom encryption for data at rest or in motion. > Do not use these operations to modify the data objects. See [Modify/skip error reports](#modifyskip-error-reports) for > the correct method to modify a report before sending it to Backtrace. ```ts +import type { BacktraceAttributeProvider, BacktraceRequestHandler } from '@backtrace/sdk-core'; + const client = BacktraceClient.builder(options) .useRequestHandler(requestHandler) .useBreadcrumbSubscriber(breadcrumbSubscriber) .addAttributeProvider(attributeProvider) .build(); ``` + +The types come from `@backtrace/sdk-core`, a dependency of `@backtrace/react-native` (with pnpm, add it to your app's +dependencies).