Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
4328298
chore: Initialize `RNSentryTimeToDisplay` during native module `init`…
krystofwoldrich Jan 14, 2025
27d76de
ref(ios): Extract Cocoa SDK init into standalone file
krystofwoldrich Jan 14, 2025
5a28c03
fix lint
krystofwoldrich Jan 14, 2025
8b67bf1
rename rnsentrysdk to rnsentrystart
krystofwoldrich Jan 14, 2025
05cef29
rename class name also to rnsentrystart, fix tests
krystofwoldrich Jan 14, 2025
778478f
explicitly import only used classes
krystofwoldrich Jan 14, 2025
3d3d0b4
fix call cocoa start not itself
krystofwoldrich Jan 14, 2025
e2e40fa
also extract didBecomeActive notification
krystofwoldrich Jan 14, 2025
83106a7
add explanation comment to native crash handling
krystofwoldrich Jan 14, 2025
742f28c
explain notification purpose
krystofwoldrich Jan 14, 2025
4897aa3
fix sdk name
krystofwoldrich Jan 14, 2025
d37ebdd
fix native sdk not react native package
krystofwoldrich Jan 14, 2025
13a0b89
fix lint sentry start
krystofwoldrich Jan 14, 2025
d7270b1
add changelog
krystofwoldrich Jan 14, 2025
580590b
feat(experimental): Add native `startWithConfigureOptions` for Apple …
krystofwoldrich Jan 14, 2025
2cc92f2
feat: Read `sentry.options.json` during cocoa init
krystofwoldrich Jan 14, 2025
592b903
Update CHANGELOG.md
krystofwoldrich Jan 15, 2025
a2717bd
remove global processor
krystofwoldrich Jan 15, 2025
5ecd6b2
Merge remote-tracking branch 'origin/kw-add-read-ios-options-file' in…
krystofwoldrich Jan 15, 2025
890133a
Update code docs for start methods
krystofwoldrich Jan 15, 2025
2d021e8
Merge branch 'main' into kw-ref-ios-sdk-init
krystofwoldrich Jan 20, 2025
cfc6293
fix lint
krystofwoldrich Jan 20, 2025
f2a041e
remove unused rnSentry
krystofwoldrich Jan 20, 2025
fc4ff7e
fix changelog
krystofwoldrich Jan 20, 2025
879044f
Merge branch 'kw-ref-ios-sdk-init' into kw-add-react-native-ios-manua…
krystofwoldrich Jan 20, 2025
dec9869
wip! add manual init tests
krystofwoldrich Jan 20, 2025
22e88c7
add tests
krystofwoldrich Jan 21, 2025
4ac4896
Merge branch 'kw-add-react-native-ios-manual-init' into kw-add-read-i…
krystofwoldrich Jan 21, 2025
1baac18
add tests
krystofwoldrich Jan 21, 2025
f92edd9
remove unavailable and unused global events processors import
krystofwoldrich Jan 21, 2025
2662dbd
Merge branch 'kw-add-react-native-ios-manual-init' into kw-add-read-i…
krystofwoldrich Jan 21, 2025
7baad41
fix c-format
krystofwoldrich Jan 21, 2025
1a2aca8
feat: Automatically load `sentry.options.json` file
krystofwoldrich Jan 21, 2025
6d81a10
remove old sentry-cocoa init code
krystofwoldrich Jan 22, 2025
92c32e9
Merge remote-tracking branch 'origin/capture-app-start-errors' into k…
krystofwoldrich Jan 22, 2025
dcfdc6c
Merge remote-tracking branch 'origin/kw-add-read-ios-options-file' in…
krystofwoldrich Jan 22, 2025
7eb1786
fix copy options json only if destination set and file exists
krystofwoldrich Jan 22, 2025
dae191b
Merge branch 'kw-add-read-ios-options-file' into kw-add-options-file-…
krystofwoldrich Jan 22, 2025
0909148
fix types, handle errors, add logs, add support for expo projects
krystofwoldrich Jan 23, 2025
82f2a87
add json options for expo project
krystofwoldrich Jan 23, 2025
21d2880
fix bail todo
krystofwoldrich Jan 23, 2025
539892c
fix lint
krystofwoldrich Jan 23, 2025
c4b2bdd
add tests
krystofwoldrich Jan 23, 2025
93462af
feat(init): Load options from `sentry.options.json` in JS
krystofwoldrich Jan 23, 2025
e7fe741
Merge branch 'capture-app-start-errors' into kw-read-options-in-js
krystofwoldrich Feb 5, 2025
0aca34f
add changelog
krystofwoldrich Feb 5, 2025
a3d0444
disable auto init if loading options from a file by default
krystofwoldrich Feb 5, 2025
80433ee
add console log to inform user about native init
krystofwoldrich Feb 5, 2025
f740d57
Merge branch 'capture-app-start-errors' into kw-read-options-in-js
krystofwoldrich Feb 5, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
- Add experimental version of `init` with optional `OptionsConfiguration<SentryAndroidOptions>` for Android ([#4451](https://github.com/getsentry/sentry-react-native/pull/4451))
- Add initialization using `sentry.options.json` for Apple platforms ([#4447](https://github.com/getsentry/sentry-react-native/pull/4447))
- Add initialization using `sentry.options.json` for Android ([#4451](https://github.com/getsentry/sentry-react-native/pull/4451))
- Merge options from file with `Sentry.init` options in JS ([#4510](https://github.com/getsentry/sentry-react-native/pull/4510))

### Internal

Expand Down
46 changes: 32 additions & 14 deletions packages/core/src/js/sdk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useEncodePolyfill } from './transports/encodePolyfill';
import { DEFAULT_BUFFER_SIZE, makeNativeTransportFactory } from './transports/native';
import { getDefaultEnvironment, isExpoGo, isRunningInMetroDevServer } from './utils/environment';
import { safeFactory, safeTracesSampler } from './utils/safe';
import { RN_GLOBAL_OBJ } from './utils/worldwide';
import { NATIVE } from './wrapper';

const DEFAULT_OPTIONS: ReactNativeOptions = {
Expand Down Expand Up @@ -47,12 +48,17 @@ export function init(passedOptions: ReactNativeOptions): void {
return;
}

const maxQueueSize = passedOptions.maxQueueSize
const userOptions = {
...RN_GLOBAL_OBJ.__SENTRY_OPTIONS__,
...passedOptions,
};

const maxQueueSize = userOptions.maxQueueSize
// eslint-disable-next-line deprecation/deprecation
?? passedOptions.transportOptions?.bufferSize
?? userOptions.transportOptions?.bufferSize
?? DEFAULT_OPTIONS.maxQueueSize;

const enableNative = passedOptions.enableNative === undefined || passedOptions.enableNative
const enableNative = userOptions.enableNative === undefined || userOptions.enableNative
? NATIVE.isNativeAvailable()
: false;

Expand All @@ -75,11 +81,11 @@ export function init(passedOptions: ReactNativeOptions): void {
return `${dsnComponents.protocol}://${dsnComponents.host}${port}`;
};

const userBeforeBreadcrumb = safeFactory(passedOptions.beforeBreadcrumb, { loggerMessage: 'The beforeBreadcrumb threw an error' });
const userBeforeBreadcrumb = safeFactory(userOptions.beforeBreadcrumb, { loggerMessage: 'The beforeBreadcrumb threw an error' });

// Exclude Dev Server and Sentry Dsn request from Breadcrumbs
const devServerUrl = getDevServer()?.url;
const dsn = getURLFromDSN(passedOptions.dsn);
const dsn = getURLFromDSN(userOptions.dsn);
const defaultBeforeBreadcrumb = (breadcrumb: Breadcrumb, _hint?: BreadcrumbHint): Breadcrumb | null => {
const type = breadcrumb.type || '';
const url = typeof breadcrumb.data?.url === 'string' ? breadcrumb.data.url : '';
Expand All @@ -103,26 +109,34 @@ export function init(passedOptions: ReactNativeOptions): void {

const options: ReactNativeClientOptions = {
...DEFAULT_OPTIONS,
...passedOptions,
...userOptions,
enableNative,
enableNativeNagger: shouldEnableNativeNagger(passedOptions.enableNativeNagger),
enableNativeNagger: shouldEnableNativeNagger(userOptions.enableNativeNagger),
// If custom transport factory fails the SDK won't initialize
transport: passedOptions.transport
transport: userOptions.transport
|| makeNativeTransportFactory({
enableNative,
})
|| makeFetchTransport,
transportOptions: {
...DEFAULT_OPTIONS.transportOptions,
...(passedOptions.transportOptions ?? {}),
...(userOptions.transportOptions ?? {}),
bufferSize: maxQueueSize,
},
maxQueueSize,
integrations: [],
stackParser: stackParserFromStackParserOptions(passedOptions.stackParser || defaultStackParser),
stackParser: stackParserFromStackParserOptions(userOptions.stackParser || defaultStackParser),
beforeBreadcrumb: chainedBeforeBreadcrumb,
initialScope: safeFactory(passedOptions.initialScope, { loggerMessage: 'The initialScope threw an error' }),
initialScope: safeFactory(userOptions.initialScope, { loggerMessage: 'The initialScope threw an error' }),
};

if (!('autoInitializeNativeSdk' in userOptions) && RN_GLOBAL_OBJ.__SENTRY_OPTIONS__) {
// We expect users to use the file options only in combination with manual native initialization
// eslint-disable-next-line no-console
console.info('Initializing Sentry JS with the options file. Expecting manual native initialization before JS. Native will not be initialized automatically.');
options.autoInitializeNativeSdk = false;
}

if ('tracesSampler' in options) {
options.tracesSampler = safeTracesSampler(options.tracesSampler);
}
Expand All @@ -131,12 +145,12 @@ export function init(passedOptions: ReactNativeOptions): void {
options.environment = getDefaultEnvironment();
}

const defaultIntegrations: false | Integration[] = passedOptions.defaultIntegrations === undefined
const defaultIntegrations: false | Integration[] = userOptions.defaultIntegrations === undefined
? getDefaultIntegrations(options)
: passedOptions.defaultIntegrations;
: userOptions.defaultIntegrations;

options.integrations = getIntegrationsToSetup({
integrations: safeFactory(passedOptions.integrations, { loggerMessage: 'The integrations threw an error' }),
integrations: safeFactory(userOptions.integrations, { loggerMessage: 'The integrations threw an error' }),
defaultIntegrations,
});
initAndBind(ReactNativeClient, options);
Expand All @@ -145,6 +159,10 @@ export function init(passedOptions: ReactNativeOptions): void {
logger.info('Offline caching, native errors features are not available in Expo Go.');
logger.info('Use EAS Build / Native Release Build to test these features.');
}

if (RN_GLOBAL_OBJ.__SENTRY_OPTIONS__) {
logger.info('Sentry JS initialized with options from the options file.');
}
}

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/js/utils/worldwide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { InternalGlobal } from '@sentry/core';
import { GLOBAL_OBJ } from '@sentry/core';
import type { ErrorUtils } from 'react-native/types';

import type { ReactNativeOptions } from '../options';
import type { ExpoGlobalObject } from './expoglobalobject';

/** Internal Global object interface with common and Sentry specific properties */
Expand All @@ -25,6 +26,7 @@ export interface ReactNativeInternalGlobal extends InternalGlobal {
__BUNDLE_START_TIME__?: number;
nativePerformanceNow?: () => number;
TextEncoder?: TextEncoder;
__SENTRY_OPTIONS__?: ReactNativeOptions;
}

type TextEncoder = {
Expand Down
64 changes: 58 additions & 6 deletions packages/core/test/sdk.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type { BaseTransportOptions, Breadcrumb, BreadcrumbHint, ClientOptions, Integration, Scope } from '@sentry/core';
import type { Breadcrumb, BreadcrumbHint, Integration, Scope } from '@sentry/core';
import { initAndBind, logger } from '@sentry/core';
import { makeFetchTransport } from '@sentry/react';

import { getDevServer } from '../src/js/integrations/debugsymbolicatorutils';
import type { ReactNativeClientOptions } from '../src/js/options';
import { init, withScope } from '../src/js/sdk';
import type { ReactNativeTracingIntegration } from '../src/js/tracing';
import { REACT_NATIVE_TRACING_INTEGRATION_NAME, reactNativeTracingIntegration } from '../src/js/tracing';
import { makeNativeTransport } from '../src/js/transports/native';
import { getDefaultEnvironment, isExpoGo, notWeb } from '../src/js/utils/environment';
import { RN_GLOBAL_OBJ } from '../src/js/utils/worldwide';
import { NATIVE } from './mockWrapper';
import { firstArg, secondArg } from './testutils';

Expand Down Expand Up @@ -109,6 +111,60 @@ describe('Tests the SDK functionality', () => {
});
});

describe('initialization from sentry.options.json', () => {
it('initializes without __SENTRY_OPTIONS__', () => {
delete RN_GLOBAL_OBJ.__SENTRY_OPTIONS__;
init({});
expect(initAndBind).toHaveBeenCalledOnce();
});

it('adds options from __SENTRY_OPTIONS__', () => {
RN_GLOBAL_OBJ.__SENTRY_OPTIONS__ = {
dsn: 'https://[email protected]/value',
};
init({});
expect(usedOptions()?.dsn).toBe('https://[email protected]/value');
});

it('options init override options from __SENTRY_OPTIONS__', () => {
RN_GLOBAL_OBJ.__SENTRY_OPTIONS__ = {
dsn: 'https://[email protected]/file',
};
init({
dsn: 'https://[email protected]/code',
});
expect(usedOptions()?.dsn).toBe('https://[email protected]/code');
});

it('initializing with __SENTRY_OPTIONS__ disabled native auto initialization', () => {
RN_GLOBAL_OBJ.__SENTRY_OPTIONS__ = {};
init({});
expect(usedOptions()?.autoInitializeNativeSdk).toBe(false);
});

it('initializing without __SENTRY_OPTIONS__ enables native auto initialization', () => {
RN_GLOBAL_OBJ.__SENTRY_OPTIONS__ = undefined;
init({});
expect(usedOptions()?.autoInitializeNativeSdk).toBe(true);
});

it('initializing with __SENTRY_OPTIONS__ keeps native auto initialization true if set', () => {
RN_GLOBAL_OBJ.__SENTRY_OPTIONS__ = {};
init({
autoInitializeNativeSdk: true,
});
expect(usedOptions()?.autoInitializeNativeSdk).toBe(true);
});

it('initializing with __SENTRY_OPTIONS__ keeps native auto initialization false if set', () => {
RN_GLOBAL_OBJ.__SENTRY_OPTIONS__ = {};
init({
autoInitializeNativeSdk: false,
});
expect(usedOptions()?.autoInitializeNativeSdk).toBe(false);
});
});

describe('environment', () => {
it('detect development environment', () => {
(getDefaultEnvironment as jest.Mock).mockImplementation(() => 'development');
Expand Down Expand Up @@ -173,7 +229,6 @@ describe('Tests the SDK functionality', () => {
(NATIVE.isNativeAvailable as jest.Mock).mockImplementation(() => false);
init({});
expect(NATIVE.isNativeAvailable).toBeCalled();
// @ts-expect-error enableNative not publicly available here.
expect(usedOptions()?.enableNative).toEqual(false);
expect(usedOptions()?.transport).toEqual(makeFetchTransport);
});
Expand All @@ -182,7 +237,6 @@ describe('Tests the SDK functionality', () => {
(NATIVE.isNativeAvailable as jest.Mock).mockImplementation(() => false);
init({ enableNative: true });
expect(NATIVE.isNativeAvailable).toBeCalled();
// @ts-expect-error enableNative not publicly available here.
expect(usedOptions()?.enableNative).toEqual(false);
expect(usedOptions()?.transport).toEqual(makeFetchTransport);
});
Expand All @@ -191,7 +245,6 @@ describe('Tests the SDK functionality', () => {
(NATIVE.isNativeAvailable as jest.Mock).mockImplementation(() => false);
init({ enableNative: false });
expect(NATIVE.isNativeAvailable).not.toBeCalled();
// @ts-expect-error enableNative not publicly available here.
expect(usedOptions()?.enableNative).toEqual(false);
expect(usedOptions()?.transport).toEqual(makeFetchTransport);
});
Expand All @@ -204,7 +257,6 @@ describe('Tests the SDK functionality', () => {
});
expect(usedOptions()?.transport).toEqual(mockTransport);
expect(NATIVE.isNativeAvailable).toBeCalled();
// @ts-expect-error enableNative not publicly available here.
expect(usedOptions()?.enableNative).toEqual(false);
});
});
Expand Down Expand Up @@ -1058,7 +1110,7 @@ function createMockedIntegration({ name }: { name?: string } = {}): Integration
};
}

function usedOptions(): ClientOptions<BaseTransportOptions> | undefined {
function usedOptions(): ReactNativeClientOptions | undefined {
return (initAndBind as jest.MockedFunction<typeof initAndBind>).mock.calls[0]?.[secondArg];
}

Expand Down
Loading