From 8601068cc4be892873d8ae4ca0e9e7d1b4cc92cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Mon, 26 Sep 2022 13:16:18 +0200 Subject: [PATCH 01/11] feat(sdk): Add ClientReports --- src/js/client.ts | 42 +++++++++++++++++++++++++++++++++++++--- src/js/sdk.tsx | 1 + src/js/utils/envelope.ts | 3 +++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/js/client.ts b/src/js/client.ts index 26e45fddd3..2388816a47 100644 --- a/src/js/client.ts +++ b/src/js/client.ts @@ -3,19 +3,23 @@ import { BrowserTransportOptions } from '@sentry/browser/types/transports/types' import { FetchImpl } from '@sentry/browser/types/transports/utils'; import { BaseClient } from '@sentry/core'; import { + ClientReportEnvelope, + ClientReportItem, + Envelope, Event, EventHint, SeverityLevel, Transport, UserFeedback, } from '@sentry/types'; +import { dateTimestampInSeconds } from '@sentry/utils'; // @ts-ignore LogBox introduced in RN 0.63 import { Alert, LogBox, YellowBox } from 'react-native'; import { defaultSdkInfo } from './integrations/sdkinfo'; import { ReactNativeClientOptions } from './options'; import { NativeTransport } from './transports/native'; -import { createUserFeedbackEnvelope } from './utils/envelope'; +import { createUserFeedbackEnvelope, items } from './utils/envelope'; import { NATIVE } from './wrapper'; /** @@ -113,11 +117,22 @@ export class ReactNativeClient extends BaseClient { }, ); this._sendEnvelope(envelope); + super._sendEnvelope(envelope); } /** - * Starts native client with dsn and options - */ + * @inheritdoc + */ + protected _sendEnvelope(envelope: Envelope): void { + if (this._options.sendClientReports) { + this._attachClientReportTo(envelope as ClientReportEnvelope); + } + super._sendEnvelope(envelope); + } + + /** + * Starts native client with dsn and options + */ private async _initNativeSdk(): Promise { let didCallNativeInit = false; @@ -144,4 +159,25 @@ export class ReactNativeClient extends BaseClient { ); } } + + /** + * + */ + private _attachClientReportTo(envelope: ClientReportEnvelope): Envelope { + const outcomes = this._clearOutcomes(); + + if (outcomes.length > 0) { + const clientReportItem: ClientReportItem = [ + { type: 'client_report' }, + { + timestamp: dateTimestampInSeconds(), + discarded_events: outcomes, + }, + ]; + + envelope[items].push(clientReportItem); + } + + return envelope; + } } diff --git a/src/js/sdk.tsx b/src/js/sdk.tsx index 7e95b90d71..754acfc772 100644 --- a/src/js/sdk.tsx +++ b/src/js/sdk.tsx @@ -41,6 +41,7 @@ const DEFAULT_OPTIONS: ReactNativeOptions = { transportOptions: { textEncoder: makeUtf8TextEncoder(), }, + sendClientReports: true, }; /** diff --git a/src/js/utils/envelope.ts b/src/js/utils/envelope.ts index d0efa9e988..b8be42f40d 100644 --- a/src/js/utils/envelope.ts +++ b/src/js/utils/envelope.ts @@ -9,6 +9,9 @@ import { } from '@sentry/types'; import { createEnvelope, dsnToString } from '@sentry/utils'; +export const header = 0; +export const items = 1; + /** * Creates an envelope from a user feedback. */ From dafd40909c6eb7200fa16cfd02ff6e7015b027ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Mon, 26 Sep 2022 13:20:19 +0200 Subject: [PATCH 02/11] Fix --- src/js/client.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/js/client.ts b/src/js/client.ts index 2388816a47..aa2155d4a9 100644 --- a/src/js/client.ts +++ b/src/js/client.ts @@ -117,7 +117,6 @@ export class ReactNativeClient extends BaseClient { }, ); this._sendEnvelope(envelope); - super._sendEnvelope(envelope); } /** @@ -163,7 +162,7 @@ export class ReactNativeClient extends BaseClient { /** * */ - private _attachClientReportTo(envelope: ClientReportEnvelope): Envelope { + private _attachClientReportTo(envelope: ClientReportEnvelope): void { const outcomes = this._clearOutcomes(); if (outcomes.length > 0) { @@ -177,7 +176,5 @@ export class ReactNativeClient extends BaseClient { envelope[items].push(clientReportItem); } - - return envelope; } } From cdb38bafcf2092353e5aec8a3020f599d2bd1ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Mon, 26 Sep 2022 13:26:26 +0200 Subject: [PATCH 03/11] Add changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19e9b74c44..a6ef12b2b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Features + +- Add ClientReports ([#2496](https://github.com/getsentry/sentry-react-native/pull/2496)) + ## 4.5.0 ### Features From 49ff7d7c30cde1562abd2241aba665112941e8c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Mon, 26 Sep 2022 18:47:38 +0200 Subject: [PATCH 04/11] Add outcomes buffer --- src/js/client.ts | 35 ++++++++-- src/js/sdk.tsx | 1 - test/client.test.ts | 153 +++++++++++++++++++++++++++++++++++++++++++- test/testutils.ts | 13 ++++ 4 files changed, 193 insertions(+), 9 deletions(-) diff --git a/src/js/client.ts b/src/js/client.ts index aa2155d4a9..a2d808435c 100644 --- a/src/js/client.ts +++ b/src/js/client.ts @@ -8,11 +8,12 @@ import { Envelope, Event, EventHint, + Outcome, SeverityLevel, Transport, UserFeedback, } from '@sentry/types'; -import { dateTimestampInSeconds } from '@sentry/utils'; +import { dateTimestampInSeconds, logger, SentryError } from '@sentry/utils'; // @ts-ignore LogBox introduced in RN 0.63 import { Alert, LogBox, YellowBox } from 'react-native'; @@ -30,6 +31,8 @@ import { NATIVE } from './wrapper'; */ export class ReactNativeClient extends BaseClient { + private _outcomesBuffer: Outcome[] = []; + private readonly _browserClient: BrowserClient; /** @@ -123,10 +126,30 @@ export class ReactNativeClient extends BaseClient { * @inheritdoc */ protected _sendEnvelope(envelope: Envelope): void { + const outcomes = this._clearOutcomes(); + this._outcomesBuffer.push(...outcomes); + if (this._options.sendClientReports) { - this._attachClientReportTo(envelope as ClientReportEnvelope); + this._attachClientReportTo(this._outcomesBuffer, envelope as ClientReportEnvelope); + } + + let shouldClearOutcomesBuffer = true; + if (this._transport && this._dsn) { + this._transport.send(envelope) + .then(null, reason => { + logger.error('Error while sending event:', reason); + if (reason instanceof SentryError) { // SentryError is thrown by SyncPromise + shouldClearOutcomesBuffer = false; + // If this is called asynchronously we want the _outcomesBuffer to be cleared + } + }); + } else { + logger.error('Transport disabled'); + } + + if (shouldClearOutcomesBuffer) { + this._outcomesBuffer = []; // if send fails synchronously the _outcomesBuffer will stay intact } - super._sendEnvelope(envelope); } /** @@ -160,11 +183,9 @@ export class ReactNativeClient extends BaseClient { } /** - * + * Attaches a client report from outcomes to the envelope. */ - private _attachClientReportTo(envelope: ClientReportEnvelope): void { - const outcomes = this._clearOutcomes(); - + private _attachClientReportTo(outcomes: Outcome[], envelope: ClientReportEnvelope): void { if (outcomes.length > 0) { const clientReportItem: ClientReportItem = [ { type: 'client_report' }, diff --git a/src/js/sdk.tsx b/src/js/sdk.tsx index 754acfc772..7e95b90d71 100644 --- a/src/js/sdk.tsx +++ b/src/js/sdk.tsx @@ -41,7 +41,6 @@ const DEFAULT_OPTIONS: ReactNativeOptions = { transportOptions: { textEncoder: makeUtf8TextEncoder(), }, - sendClientReports: true, }; /** diff --git a/test/client.test.ts b/test/client.test.ts index ee4ff0b39a..08979b9173 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -1,4 +1,5 @@ -import { Envelope, Transport } from '@sentry/types'; +import { Envelope, Outcome, Transport } from '@sentry/types'; +import { rejectedSyncPromise, SentryError } from '@sentry/utils'; import * as RN from 'react-native'; import { ReactNativeClient } from '../src/js/client'; @@ -14,6 +15,7 @@ import { firstArg, getMockSession, getMockUserFeedback, + getSyncPromiseRejectOnFirstCall, } from './testutils'; const EXAMPLE_DSN = @@ -295,4 +297,153 @@ describe('Tests ReactNativeClient', () => { expect(getSdkInfoFrom(mockTransportSend)).toStrictEqual(expectedSdkInfo); }); }); + + describe('clientReports', () => { + test('does not send client reports if disabled', () => { + const mockTransportSend = jest.fn((_envelope: Envelope) => Promise.resolve()); + const client = new ReactNativeClient({ + ...DEFAULT_OPTIONS, + dsn: EXAMPLE_DSN, + transport: () => ({ + send: mockTransportSend, + flush: jest.fn(), + }), + sendClientReports: false, + } as ReactNativeClientOptions); + + mockDroppedEvent(client); + + client.captureMessage('message_test_value'); + + expectOnlyMessageEventInEnvelope(mockTransportSend); + }); + + test('send client reports on event envelope', () => { + const mockTransportSend = jest.fn((_envelope: Envelope) => Promise.resolve()); + const client = new ReactNativeClient({ + ...DEFAULT_OPTIONS, + dsn: EXAMPLE_DSN, + transport: () => ({ + send: mockTransportSend, + flush: jest.fn(), + }), + sendClientReports: true, + } as ReactNativeClientOptions); + + mockDroppedEvent(client); + + client.captureMessage('message_test_value'); + + expect(mockTransportSend).toBeCalledTimes(1); + expect(mockTransportSend.mock.calls[0][firstArg][envelopeItems][1][envelopeItemHeader]).toEqual( + { type: 'client_report' } + ); + expect(mockTransportSend.mock.calls[0][firstArg][envelopeItems][1][envelopeItemPayload]).toEqual( + expect.objectContaining({ + discarded_events: [ + { + reason: 'before_send', + category: 'error', + quantity: 1, + } + ], + }), + ); + expect((client as unknown as { _outcomesBuffer: Outcome[] })._outcomesBuffer).toEqual([]); + }); + + test('does not send empty client report', () => { + const mockTransportSend = jest.fn((_envelope: Envelope) => Promise.resolve()); + const client = new ReactNativeClient({ + ...DEFAULT_OPTIONS, + dsn: EXAMPLE_DSN, + transport: () => ({ + send: mockTransportSend, + flush: jest.fn(), + }), + sendClientReports: true, + } as ReactNativeClientOptions); + + client.captureMessage('message_test_value'); + + expectOnlyMessageEventInEnvelope(mockTransportSend); + }); + + test('keeps outcomes in case envelope fails to send', () => { + const mockTransportSend = jest.fn((_envelope: Envelope) => + rejectedSyncPromise(new SentryError('Test'))); + const client = new ReactNativeClient({ + ...DEFAULT_OPTIONS, + dsn: EXAMPLE_DSN, + transport: () => ({ + send: mockTransportSend, + flush: jest.fn(), + }), + sendClientReports: true, + } as ReactNativeClientOptions); + + mockDroppedEvent(client); + + client.captureMessage('message_test_value'); + + expect((client as unknown as { _outcomesBuffer: Outcome[] })._outcomesBuffer).toEqual([ + { reason: 'before_send', category: 'error', quantity: 1 }, + ]); + }); + + test('sends buffered client reports on second try', () => { + const mockTransportSend = getSyncPromiseRejectOnFirstCall<[Envelope]>(new SentryError('Test')); + const client = new ReactNativeClient({ + ...DEFAULT_OPTIONS, + dsn: EXAMPLE_DSN, + transport: () => ({ + send: mockTransportSend, + flush: jest.fn(), + }), + sendClientReports: true, + } as ReactNativeClientOptions); + + mockDroppedEvent(client); + client.captureMessage('message_test_value_1'); + mockDroppedEvent(client); + client.captureMessage('message_test_value_2'); + + expect(mockTransportSend).toBeCalledTimes(2); + expect(mockTransportSend.mock.calls[0][firstArg][envelopeItems].length).toEqual(2); + expect(mockTransportSend.mock.calls[0][firstArg][envelopeItems][1][envelopeItemHeader]).toEqual( + { type: 'client_report' } + ); + expect(mockTransportSend.mock.calls[0][firstArg][envelopeItems][1][envelopeItemPayload]).toEqual( + expect.objectContaining({ + discarded_events: [ + { + reason: 'before_send', + category: 'error', + quantity: 1, + }, + { + reason: 'before_send', + category: 'error', + quantity: 1, + } + ], + }), + ); + expect((client as unknown as { _outcomesBuffer: Outcome[] })._outcomesBuffer).toEqual([]); + }); + + function expectOnlyMessageEventInEnvelope(transportSend: jest.Mock) { + expect(transportSend).toBeCalledTimes(1); + expect(transportSend.mock.calls[0][firstArg][envelopeItems]).toHaveLength(1); + expect(transportSend.mock.calls[0][firstArg][envelopeItems][0][envelopeItemHeader]).toEqual( + expect.objectContaining({ type: 'event' }), + ); + } + + function mockDroppedEvent( + client: ReactNativeClient, + ) { + client.recordDroppedEvent('before_send', 'error'); + } + }); }); diff --git a/test/testutils.ts b/test/testutils.ts index a7b9c93b83..51962daa05 100644 --- a/test/testutils.ts +++ b/test/testutils.ts @@ -1,5 +1,6 @@ import { Transaction } from '@sentry/tracing'; import { Session, UserFeedback } from '@sentry/types'; +import { rejectedSyncPromise } from '@sentry/utils'; import { getBlankTransactionContext } from '../src/js/tracing/utils'; @@ -51,3 +52,15 @@ export const getMockUserFeedback = (): UserFeedback => ({ name: 'name_test_value', event_id: 'event_id_test_value', }); + +export const getSyncPromiseRejectOnFirstCall = (reason: unknown): jest.Mock => { + let shouldSyncReject = true; + return jest.fn((..._args: Y) => { + if (shouldSyncReject) { + shouldSyncReject = false; + return rejectedSyncPromise(reason); + } else { + return Promise.resolve(); + } + }); +}; From 7217b3dcf097648ddabd79b154d2ef6b350d6798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Mon, 26 Sep 2022 19:01:22 +0200 Subject: [PATCH 05/11] Add outcome merge draft --- src/js/client.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/js/client.ts b/src/js/client.ts index a2d808435c..6c303636bb 100644 --- a/src/js/client.ts +++ b/src/js/client.ts @@ -31,7 +31,7 @@ import { NATIVE } from './wrapper'; */ export class ReactNativeClient extends BaseClient { - private _outcomesBuffer: Outcome[] = []; + private _outcomesBuffer: Outcome[]; private readonly _browserClient: BrowserClient; @@ -52,6 +52,8 @@ export class ReactNativeClient extends BaseClient { options._metadata.sdk = options._metadata.sdk || defaultSdkInfo; super(options); + this._outcomesBuffer = []; + // This is a workaround for now using fetch on RN, this is a known issue in react-native and only generates a warning // YellowBox deprecated and replaced with with LogBox in RN 0.63 if (LogBox) { @@ -127,7 +129,7 @@ export class ReactNativeClient extends BaseClient { */ protected _sendEnvelope(envelope: Envelope): void { const outcomes = this._clearOutcomes(); - this._outcomesBuffer.push(...outcomes); + mergerBufferWithOutcomes(this._outcomesBuffer, outcomes); if (this._options.sendClientReports) { this._attachClientReportTo(this._outcomesBuffer, envelope as ClientReportEnvelope); @@ -199,3 +201,10 @@ export class ReactNativeClient extends BaseClient { } } } + +/** + * Merges buffer with new outcomes. + */ +function mergerBufferWithOutcomes(buffer: Outcome[], outcomes: Outcome[]): void { + buffer.push(...outcomes); // TODO: proper merge +} From e65906ba2adce876384ca30bfbb21dedc893e8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Tue, 27 Sep 2022 10:48:33 +0200 Subject: [PATCH 06/11] Add mergeOutcomes function --- src/js/client.ts | 10 ++-------- src/js/utils/outcome.ts | 22 ++++++++++++++++++++++ test/client.test.ts | 7 +------ 3 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 src/js/utils/outcome.ts diff --git a/src/js/client.ts b/src/js/client.ts index 6c303636bb..3b2acc13a1 100644 --- a/src/js/client.ts +++ b/src/js/client.ts @@ -22,6 +22,7 @@ import { ReactNativeClientOptions } from './options'; import { NativeTransport } from './transports/native'; import { createUserFeedbackEnvelope, items } from './utils/envelope'; import { NATIVE } from './wrapper'; +import { mergeOutcomes } from './utils/outcome'; /** * The Sentry React Native SDK Client. @@ -129,7 +130,7 @@ export class ReactNativeClient extends BaseClient { */ protected _sendEnvelope(envelope: Envelope): void { const outcomes = this._clearOutcomes(); - mergerBufferWithOutcomes(this._outcomesBuffer, outcomes); + this._outcomesBuffer = mergeOutcomes(this._outcomesBuffer, outcomes); if (this._options.sendClientReports) { this._attachClientReportTo(this._outcomesBuffer, envelope as ClientReportEnvelope); @@ -201,10 +202,3 @@ export class ReactNativeClient extends BaseClient { } } } - -/** - * Merges buffer with new outcomes. - */ -function mergerBufferWithOutcomes(buffer: Outcome[], outcomes: Outcome[]): void { - buffer.push(...outcomes); // TODO: proper merge -} diff --git a/src/js/utils/outcome.ts b/src/js/utils/outcome.ts new file mode 100644 index 0000000000..7857530b84 --- /dev/null +++ b/src/js/utils/outcome.ts @@ -0,0 +1,22 @@ +import { Outcome } from '@sentry/types'; + +/** + * Merges buffer with new outcomes. + */ +export function mergeOutcomes(...merge: Outcome[][]): Outcome[] { + const map = new Map(); + + const process = (outcome: Outcome) => { + const key = `${outcome.reason}:${outcome.category}`; + const existing = map.get(key); + if (existing) { + existing.quantity += outcome.quantity; + } else { + map.set(key, outcome); + } + }; + + merge.forEach((outcomes) => outcomes.forEach(process)); + + return [...map.values()]; +} diff --git a/test/client.test.ts b/test/client.test.ts index 08979b9173..b744fbf445 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -419,13 +419,8 @@ describe('Tests ReactNativeClient', () => { { reason: 'before_send', category: 'error', - quantity: 1, + quantity: 2, }, - { - reason: 'before_send', - category: 'error', - quantity: 1, - } ], }), ); From e7fbee57e041fae10e36bfdb38df8871c65cafe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Tue, 27 Sep 2022 10:56:09 +0200 Subject: [PATCH 07/11] Fix lint --- src/js/client.ts | 2 +- src/js/utils/outcome.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/client.ts b/src/js/client.ts index 3b2acc13a1..c9fc409f60 100644 --- a/src/js/client.ts +++ b/src/js/client.ts @@ -21,8 +21,8 @@ import { defaultSdkInfo } from './integrations/sdkinfo'; import { ReactNativeClientOptions } from './options'; import { NativeTransport } from './transports/native'; import { createUserFeedbackEnvelope, items } from './utils/envelope'; -import { NATIVE } from './wrapper'; import { mergeOutcomes } from './utils/outcome'; +import { NATIVE } from './wrapper'; /** * The Sentry React Native SDK Client. diff --git a/src/js/utils/outcome.ts b/src/js/utils/outcome.ts index 7857530b84..4f07debae1 100644 --- a/src/js/utils/outcome.ts +++ b/src/js/utils/outcome.ts @@ -6,7 +6,7 @@ import { Outcome } from '@sentry/types'; export function mergeOutcomes(...merge: Outcome[][]): Outcome[] { const map = new Map(); - const process = (outcome: Outcome) => { + const process = (outcome: Outcome): void => { const key = `${outcome.reason}:${outcome.category}`; const existing = map.get(key); if (existing) { From 6396aa5515edfe1cc34432fc3c69fb9194061a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Tue, 27 Sep 2022 11:08:28 +0200 Subject: [PATCH 08/11] Return sendClientReports default --- src/js/sdk.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/sdk.tsx b/src/js/sdk.tsx index 7e95b90d71..754acfc772 100644 --- a/src/js/sdk.tsx +++ b/src/js/sdk.tsx @@ -41,6 +41,7 @@ const DEFAULT_OPTIONS: ReactNativeOptions = { transportOptions: { textEncoder: makeUtf8TextEncoder(), }, + sendClientReports: true, }; /** From 641ddd9d98c8aeca318d55c36e38f0fe41ff98e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Tue, 27 Sep 2022 13:26:55 +0200 Subject: [PATCH 09/11] Fix send error logs --- src/js/client.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/client.ts b/src/js/client.ts index c9fc409f60..2d34d213e5 100644 --- a/src/js/client.ts +++ b/src/js/client.ts @@ -140,10 +140,12 @@ export class ReactNativeClient extends BaseClient { if (this._transport && this._dsn) { this._transport.send(envelope) .then(null, reason => { - logger.error('Error while sending event:', reason); if (reason instanceof SentryError) { // SentryError is thrown by SyncPromise shouldClearOutcomesBuffer = false; // If this is called asynchronously we want the _outcomesBuffer to be cleared + logger.error('SentryError while sending event, keeping outcomes buffer:', reason); + } else { + logger.error('Error while sending event:', reason); } }); } else { From 8b5613e9777b0fe4aec01a9c18d1ae97f1cb0861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Tue, 27 Sep 2022 13:30:57 +0200 Subject: [PATCH 10/11] Add self hosted info to changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6ef12b2b6..9d0d573808 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Add ClientReports ([#2496](https://github.com/getsentry/sentry-react-native/pull/2496)) +### Sentry Self-hosted Compatibility + +- Starting with version `4.6.0` of the `@sentry/react-native` package, [Sentry's self hosted version >= v21.9.0](https://github.com/getsentry/self-hosted/releases) is required or you have to manually disable sending client reports via the `sendClientReports` option. This only applies to self-hosted Sentry. If you are using [sentry.io](https://sentry.io), no action is needed. + ## 4.5.0 ### Features From 4529d273cc88a698373201e7281a3e332c556574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= Date: Tue, 27 Sep 2022 13:48:04 +0200 Subject: [PATCH 11/11] Fix sendClientReports options native sync --- android/src/main/java/io/sentry/react/RNSentryModule.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/src/main/java/io/sentry/react/RNSentryModule.java b/android/src/main/java/io/sentry/react/RNSentryModule.java index 58a1127412..1eb779a26a 100644 --- a/android/src/main/java/io/sentry/react/RNSentryModule.java +++ b/android/src/main/java/io/sentry/react/RNSentryModule.java @@ -99,6 +99,9 @@ public void initNativeSdk(final ReadableMap rnOptions, Promise promise) { // SentryAndroid needs an empty string fallback for the dsn. options.setDsn(""); } + if (rnOptions.hasKey("sendClientReports")) { + options.setSendClientReports(rnOptions.getBoolean("sendClientReports")); + } if (rnOptions.hasKey("maxBreadcrumbs")) { options.setMaxBreadcrumbs(rnOptions.getInt("maxBreadcrumbs")); }