Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SentrySerialized to ReactNativeWritable convertor, load device context from sentry-java scope #3170

Merged
merged 35 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5b6a0f9
Add SentrySerialized to ReactNativeWritable convertor, load device co…
krystofwoldrich Jul 10, 2023
0baa9de
Update changelog
krystofwoldrich Jul 10, 2023
a867f8a
Add RNSentryAndroidTester project to test the RNSentry Android module
krystofwoldrich Jul 10, 2023
f1d91dc
Add android CI tests
krystofwoldrich Jul 10, 2023
0a163ea
Add android tests to CI, add basic key value converter tests
krystofwoldrich Jul 10, 2023
da1b6a8
Fix emulator working dir
krystofwoldrich Jul 10, 2023
5ed7083
Add tests
krystofwoldrich Jul 10, 2023
a864fff
Update devicecontext to use both context and contexts fields
krystofwoldrich Jul 11, 2023
35e8aba
tmp: comment out internal to test android tester ci
krystofwoldrich Jul 13, 2023
65211d0
Update converter for long and other big numbers
krystofwoldrich Jul 13, 2023
74b5094
Fix JS tests JS keeps breadcrumbs on android
krystofwoldrich Jul 13, 2023
48b73f8
Update changelog
krystofwoldrich Jul 13, 2023
cc655e3
Revert "tmp: comment out internal to test android tester ci"
krystofwoldrich Jul 13, 2023
9d59cb3
Restore getscope and captureenvelope, lint imports
krystofwoldrich Jul 13, 2023
ccf9a79
Merge remote-tracking branch 'origin/main' into kw-android-new-event-…
krystofwoldrich Jul 18, 2023
f24552e
Use backticks for tests names
krystofwoldrich Jul 18, 2023
3bb450a
Update cocoa tester name
krystofwoldrich Jul 18, 2023
62c3a72
Add null checks
krystofwoldrich Jul 18, 2023
17cd520
fix changelog
krystofwoldrich Jul 18, 2023
f63f2bf
Fix annotations
krystofwoldrich Jul 19, 2023
c8564c6
Update Android SDK, fix ts types
krystofwoldrich Jul 19, 2023
583b86c
Rename cocoa context to contexts to unify the naming
krystofwoldrich Jul 19, 2023
a8a0713
Add android sdk info
krystofwoldrich Jul 19, 2023
0058ec1
Fix node version for e2e tests
krystofwoldrich Jul 19, 2023
011bbe4
Fix node for newer rn versions
krystofwoldrich Jul 19, 2023
cf51653
Fix cocoa tester scheme name
krystofwoldrich Jul 19, 2023
113b6f7
Revert "Use backticks for tests names"
krystofwoldrich Jul 19, 2023
01a4c6f
convert test names to camel case
krystofwoldrich Jul 19, 2023
60f0620
Remove unit tests, we don't have any at the moment
krystofwoldrich Jul 19, 2023
8cc59fa
Merge remote-tracking branch 'origin/main' into kw-android-new-event-…
krystofwoldrich Jul 25, 2023
0b1e855
Add clean emulator step to native android tests
krystofwoldrich Jul 26, 2023
b29c3bb
Run clean emulator with emulator running
krystofwoldrich Jul 26, 2023
48d2ba9
Bump Android SDK to 6.27.0
krystofwoldrich Jul 26, 2023
e29f4dc
Merge remote-tracking branch 'origin/HEAD' into kw-android-new-event-…
krystofwoldrich Jul 26, 2023
fdb07b3
Update changelog
krystofwoldrich Jul 26, 2023
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 @@ -5,6 +5,7 @@
### Features

- Use `android.namespace` for AGP 8 and RN 0.73 ([#3133](https://github.com/getsentry/sentry-react-native/pull/3133))
- Load Android device context before `beforeSend` callback ([#3170](https://github.com/getsentry/sentry-react-native/pull/3170))

### Dependencies

Expand Down
95 changes: 95 additions & 0 deletions android/src/main/java/io/sentry/react/MapConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package io.sentry.react;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;

import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Map;

import io.sentry.ILogger;
import io.sentry.SentryLevel;
import io.sentry.android.core.AndroidLogger;

public class MapConverter {
public static final String NAME = "RNSentry.MapConverter";

private static final ILogger logger = new AndroidLogger(NAME);

public static Object convertToWritable(@Nullable Object serialized) {
if (serialized instanceof ArrayList) {
WritableArray writable = Arguments.createArray();
for (Object item : (ArrayList<?>) serialized) {
addValueToWritableArray(writable, convertToWritable(item));
}
return writable;
} else if (serialized instanceof Map) {
WritableMap writable = Arguments.createMap();
for (Map.Entry<?, ?> entry : ((Map<?, ?>) serialized).entrySet()) {
Object key = entry.getKey();
Object value = entry.getValue();

if (key instanceof String) {
addValueToWritableMap(writable, (String) key, convertToWritable(value));
} else {
logger.log(SentryLevel.ERROR, "Only String keys are supported in Map.", key);
}
}
return writable;
} else if (serialized instanceof Number
|| serialized instanceof Boolean
|| serialized == null
|| serialized instanceof String) {
return serialized;
} else {
logger.log(SentryLevel.ERROR, "Supplied serialized value could not be converted.", serialized);
return null;
}
}

private static void addValueToWritableArray(WritableArray writableArray, Object value) {
if (value == null) {
writableArray.pushNull();
} else if (value instanceof Boolean) {
writableArray.pushBoolean((Boolean) value);
} else if (value instanceof Double) {
writableArray.pushDouble((Double) value);
} else if (value instanceof Integer) {
writableArray.pushInt((Integer) value);
} else if (value instanceof String) {
writableArray.pushString((String) value);
} else if (value instanceof ReadableMap) {
writableArray.pushMap((ReadableMap) value);
} else if (value instanceof ReadableArray) {
writableArray.pushArray((ReadableArray) value);
} else {
logger.log(SentryLevel.ERROR,
"Could not convert object: " + value);
}
}

private static void addValueToWritableMap(WritableMap writableMap, String key, Object value) {
if (value == null) {
writableMap.putNull(key);
} else if (value instanceof Boolean) {
writableMap.putBoolean(key, (Boolean) value);
} else if (value instanceof Double) {
writableMap.putDouble(key, (Double) value);
} else if (value instanceof Integer) {
writableMap.putInt(key, (Integer) value);
} else if (value instanceof String) {
writableMap.putString(key, (String) value);
} else if (value instanceof ReadableArray) {
writableMap.putArray(key, (ReadableArray) value);
} else if (value instanceof ReadableMap) {
writableMap.putMap(key, (ReadableMap) value);
krystofwoldrich marked this conversation as resolved.
Show resolved Hide resolved
} else {
logger.log(SentryLevel.ERROR,
"Could not convert object" + value);
}
}
}
55 changes: 43 additions & 12 deletions android/src/main/java/io/sentry/react/RNSentryModuleImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;

import org.jetbrains.annotations.NotNull;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
Expand All @@ -39,12 +43,18 @@

import io.sentry.Breadcrumb;
import io.sentry.DateUtils;
import io.sentry.EnvelopeReader;
import io.sentry.Hub;
import io.sentry.HubAdapter;
import io.sentry.IEnvelopeReader;
import io.sentry.ILogger;
import io.sentry.ISerializer;
import io.sentry.Integration;
import io.sentry.InternalSentrySdk;
import io.sentry.Scope;
import io.sentry.Sentry;
import io.sentry.SentryDate;
import io.sentry.SentryEnvelope;
import io.sentry.SentryEvent;
import io.sentry.SentryLevel;
import io.sentry.UncaughtExceptionHandlerIntegration;
Expand Down Expand Up @@ -334,20 +344,19 @@ public void captureEnvelope(ReadableArray rawBytes, ReadableMap options, Promise
bytes[i] = (byte) rawBytes.getInt(i);
}

try {
final String outboxPath = HubAdapter.getInstance().getOptions().getOutboxPath();

if (outboxPath == null) {
logger.log(SentryLevel.ERROR,
"Error retrieving outboxPath. Envelope will not be sent. Is the Android SDK initialized?");
final HubAdapter hubAdapter = HubAdapter.getInstance();
IEnvelopeReader envelopeReader = hubAdapter.getOptions().getEnvelopeReader();
try (final InputStream byteStream = new ByteArrayInputStream(bytes)) {
final SentryEnvelope sentryEnvelope = envelopeReader.read(byteStream);
if (sentryEnvelope != null) {
hubAdapter.captureEnvelope(sentryEnvelope);
} else {
File installation = new File(outboxPath, UUID.randomUUID().toString());
try (FileOutputStream out = new FileOutputStream(installation)) {
out.write(bytes);
}
logger.log(SentryLevel.ERROR, "Sentry Envelope Reader returned null after reading envelopes bytes");
promise.resolve(false);
}
} catch (Throwable ignored) {
logger.log(SentryLevel.ERROR, "Error while writing envelope to outbox.");
} catch (IOException e) {
logger.log(SentryLevel.ERROR, "Error while reading envelope bytes");
promise.resolve(false);
}
promise.resolve(true);
}
Expand Down Expand Up @@ -616,6 +625,28 @@ public void disableNativeFramesTracking() {
}
}

public void fetchNativeDeviceContexts(Promise promise) {
// Temp work around until sorted out this API in sentry-java.
// TODO: If the callback isn't executed the promise wouldn't be resolved.
HubAdapter.getInstance().withScope((@NotNull final Scope scope) -> {
final Map<String, Object> serialized = InternalSentrySdk.serializeScope(scope);
final Object deviceContext = MapConverter.convertToWritable(serialized);
promise.resolve(deviceContext);
});
}

public void fetchNativeSdkInfo(Promise promise) {
@Nullable final SdkVersion sdkVersion = HubAdapter.getInstance().getOptions().getSdkVersion();
if (sdkVersion == null) {
promise.resolve(null);
} else {
final WritableMap sdkInfo = new WritableNativeMap();
sdkInfo.putString("name", sdkVersion.getName());
sdkInfo.putString("version", sdkVersion.getVersion());
promise.resolve(sdkInfo);
}
}

private void setEventOriginTag(SentryEvent event) {
SdkVersion sdk = event.getSdk();
if (sdk != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ public void disableNativeFramesTracking() {

@Override
public void fetchNativeDeviceContexts(Promise promise) {
// Not used on android
this.impl.fetchNativeDeviceContexts(promise);
}

@Override
public void fetchNativeSdkInfo(Promise promise) {
// Not used on android
this.impl.fetchNativeSdkInfo(promise);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ public void disableNativeFramesTracking() {

@ReactMethod
public void fetchNativeDeviceContexts(Promise promise) {
// Not used on android
this.impl.fetchNativeDeviceContexts(promise);
}

@ReactMethod
public void fetchNativeSdkInfo(Promise promise) {
// Not used on android
this.impl.fetchNativeSdkInfo(promise);
}
}
2 changes: 1 addition & 1 deletion src/js/NativeRNSentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface Spec extends TurboModule {
closeNativeSdk(): Promise<void>;
disableNativeFramesTracking(): void;
fetchNativeRelease(): Promise<NativeReleaseResponse>;
fetchNativeSdkInfo(): Promise<Package>;
fetchNativeSdkInfo(): Promise<Package | null>;
fetchNativeDeviceContexts(): Promise<NativeDeviceContextsResponse>;
fetchNativeAppStart(): Promise<NativeAppStartResponse | null>;
fetchNativeFrames(): Promise<NativeFramesResponse | null>;
Expand Down
28 changes: 0 additions & 28 deletions src/js/wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ interface SentryNativeWrapper {
_processLevel(level: SeverityLevel): SeverityLevel;
_serializeObject(data: { [key: string]: unknown }): { [key: string]: string };
_isModuleLoaded(module: Spec | undefined): module is Spec;
_getBreadcrumbs(event: Event): Breadcrumb[] | undefined;

isNativeAvailable(): boolean;

Expand Down Expand Up @@ -252,11 +251,6 @@ export const NATIVE: SentryNativeWrapper = {
throw this._NativeClientError;
}

if (this.platform !== 'ios') {
// Only ios uses deviceContexts, return an empty object.
return {};
}

return RNSentry.fetchNativeDeviceContexts();
},

Expand Down Expand Up @@ -514,7 +508,6 @@ export const NATIVE: SentryNativeWrapper = {
// @ts-ignore Android still uses the old message object, without this the serialization of events will break.
event.message = { message: event.message };
}
event.breadcrumbs = this._getBreadcrumbs(event);
}

return [itemHeader, event];
Expand Down Expand Up @@ -582,27 +575,6 @@ export const NATIVE: SentryNativeWrapper = {

_NativeClientError: new SentryError("Native Client is not available, can't start on native."),

/**
* Get breadcrumbs (removes breadcrumbs from handled exceptions on Android)
*
* We do this to avoid duplicate breadcrumbs on Android as sentry-android applies the breadcrumbs
* from the native scope onto every envelope sent through it. This scope will contain the breadcrumbs
* sent through the scope sync feature. This causes duplicate breadcrumbs.
* We then remove the breadcrumbs in all cases but if it is handled == false,
* this is a signal that the app would crash and android would lose the breadcrumbs by the time the app is restarted to read
* the envelope.
*/
_getBreadcrumbs(event: Event): Breadcrumb[] | undefined {
let breadcrumbs: Breadcrumb[] | undefined = event.breadcrumbs;

const hardCrashed = isHardCrash(event);
if (NATIVE.platform === 'android' && event.breadcrumbs && !hardCrashed) {
breadcrumbs = [];
}

return breadcrumbs;
},

enableNative: true,
nativeIsReady: false,
platform: Platform.OS,
Expand Down
Loading