diff --git a/android/build.gradle b/android/build.gradle index 402c794864..8cf376a54b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -21,5 +21,5 @@ android { dependencies { compile 'com.facebook.react:react-native:+' - compile 'io.sentry:sentry-android:1.0.0-beta' + compile 'io.sentry:sentry-android:1.0.0-beta2' } \ No newline at end of file diff --git a/android/src/main/java/io/sentry/RNSentryExceptionsManagerModule.java b/android/src/main/java/io/sentry/RNSentryExceptionsManagerModule.java index 04f35cb5b9..b3f05774ad 100644 --- a/android/src/main/java/io/sentry/RNSentryExceptionsManagerModule.java +++ b/android/src/main/java/io/sentry/RNSentryExceptionsManagerModule.java @@ -126,7 +126,7 @@ public static void convertAndCaptureReactNativeException(String title, ReadableN EventBuilder eventBuilder = new EventBuilder() .withLevel(Event.Level.FATAL) .withSentryInterface(new ExceptionInterface(exceptions)); - Sentry.capture(eventBuilder); + Sentry.capture(RNSentryModule.buildEvent(eventBuilder)); } private static SentryStackTraceElement[] convertToNativeStacktrace(ReadableNativeArray stack) { diff --git a/android/src/main/java/io/sentry/RNSentryModule.java b/android/src/main/java/io/sentry/RNSentryModule.java index c8b20d01f2..dd1e16a978 100644 --- a/android/src/main/java/io/sentry/RNSentryModule.java +++ b/android/src/main/java/io/sentry/RNSentryModule.java @@ -1,5 +1,10 @@ package io.sentry; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.util.Log; + import com.facebook.react.ReactApplication; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Promise; @@ -10,6 +15,7 @@ import com.facebook.react.bridge.ReadableNativeArray; import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.WritableNativeMap; import java.util.HashMap; import java.util.Map; @@ -31,14 +37,18 @@ public class RNSentryModule extends ReactContextBaseJavaModule { private final ReactApplicationContext reactContext; private final ReactApplication reactApplication; + private static AndroidEventBuilderHelper androidHelper; + private static PackageInfo packageInfo; final static Logger logger = Logger.getLogger("react-native-sentry"); - private ReadableMap extra; - private ReadableMap tags; + private static WritableNativeMap extra; + private static ReadableMap tags; public RNSentryModule(ReactApplicationContext reactContext, ReactApplication reactApplication) { super(reactContext); this.reactContext = reactContext; this.reactApplication = reactApplication; + RNSentryModule.extra = new WritableNativeMap(); + RNSentryModule.packageInfo = getPackageInfo(reactContext); } public ReactApplication getReactApplication() { @@ -60,6 +70,7 @@ public Map getConstants() { @ReactMethod public void startWithDsnString(String dsnString) { SentryClient sentryClient = Sentry.init(dsnString, new AndroidSentryClientFactory(this.getReactApplicationContext())); + androidHelper = new AndroidEventBuilderHelper(this.getReactApplicationContext()); sentryClient.addEventSendCallback(new EventSendCallback() { @Override public void onFailure(Event event, Exception exception) { @@ -84,12 +95,18 @@ public void setLogLevel(int level) { @ReactMethod public void setExtra(ReadableMap extra) { - this.extra = extra; + RNSentryModule.extra.merge(extra); + } + + @ReactMethod + public void addExtra(String key, String value) { + RNSentryModule.extra.putString(key, value); + logger.info(String.format("addExtra '%s' '%s'", key, value)); } @ReactMethod public void setTags(ReadableMap tags) { - this.tags = tags; + RNSentryModule.tags = tags; } @ReactMethod @@ -128,7 +145,6 @@ public void captureBreadcrumb(ReadableMap breadcrumb) { public void captureEvent(ReadableMap event) { ReadableNativeMap castEvent = (ReadableNativeMap)event; if (event.hasKey("message")) { - AndroidEventBuilderHelper helper = new AndroidEventBuilderHelper(this.getReactApplicationContext()); EventBuilder eventBuilder = new EventBuilder() .withMessage(event.getString("message")) .withLogger(event.getString("logger")) @@ -144,34 +160,19 @@ public void captureEvent(ReadableMap event) { eventBuilder.withSentryInterface(userInterface); } - helper.helpBuildingEvent(eventBuilder); - - if (this.extra != null) { - for (Map.Entry entry : ((ReadableNativeMap)this.extra).toHashMap().entrySet()) { - eventBuilder.withExtra(entry.getKey(), entry.getValue()); - } - } - if (castEvent.hasKey("extra")) { for (Map.Entry entry : castEvent.getMap("extra").toHashMap().entrySet()) { eventBuilder.withExtra(entry.getKey(), entry.getValue()); } } - if (this.tags != null) { - for (Map.Entry entry : ((ReadableNativeMap)this.tags).toHashMap().entrySet()) { - eventBuilder.withExtra(entry.getKey(), entry.getValue()); - } - } - if (castEvent.hasKey("tags")) { for (Map.Entry entry : castEvent.getMap("tags").toHashMap().entrySet()) { eventBuilder.withTag(entry.getKey(), entry.getValue().toString()); } } - Event builtEvent = eventBuilder.build(); - Sentry.capture(builtEvent); + Sentry.capture(buildEvent(eventBuilder)); } else { RNSentryExceptionsManagerModule.lastReceivedException = event; if (this.getReactApplication().getReactNativeHost().getUseDeveloperSupport() == true) { @@ -186,8 +187,8 @@ public void captureEvent(ReadableMap event) { @ReactMethod public void clearContext() { Sentry.clearContext(); - this.extra = null; - this.tags = null; + RNSentryModule.extra = new WritableNativeMap(); + RNSentryModule.tags = null; } @ReactMethod @@ -197,6 +198,61 @@ public void activateStacktraceMerging(Promise promise) { promise.reject("Sentry", "Stacktrace merging not yet implemented"); } + public static Event buildEvent(EventBuilder eventBuilder) { + androidHelper.helpBuildingEvent(eventBuilder); + + setRelease(eventBuilder); + stripInternalSentry(eventBuilder); + + if (extra != null) { + for (Map.Entry entry : extra.toHashMap().entrySet()) { + if (entry.getValue() != null) { + eventBuilder.withExtra(entry.getKey(), entry.getValue()); + logger.info(String.format("addExtra '%s' '%s'", entry.getKey(), entry.getValue())); + } + } + } + if (tags != null) { + for (Map.Entry entry : ((ReadableNativeMap)tags).toHashMap().entrySet()) { + eventBuilder.withExtra(entry.getKey(), entry.getValue()); + } + } + + return eventBuilder.build(); + } + + private static void stripInternalSentry(EventBuilder eventBuilder) { + if (extra != null) { + for (Map.Entry entry : extra.toHashMap().entrySet()) { + if (entry.getKey().startsWith("__sentry")) { + extra.putNull(entry.getKey()); + } + } + } + } + + private static void setRelease(EventBuilder eventBuilder) { + if (extra.hasKey("__sentry_version")) { + eventBuilder.withRelease(packageInfo.packageName + "-" + extra.getString("__sentry_version")); + eventBuilder.withDist(null); + } + if (extra.hasKey("__sentry_release")) { + eventBuilder.withRelease(extra.getString("__sentry_release")); + } + if (extra.hasKey("__sentry_dist")) { + eventBuilder.withDist(extra.getString("__sentry_dist")); + } + } + + private static PackageInfo getPackageInfo(Context ctx) { + try { + return ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0); + } catch (PackageManager.NameNotFoundException e) { + logger.info("Error getting package info."); + return null; + } + } + private Breadcrumb.Level breadcrumbLevel(String level) { switch (level) { case "critical": diff --git a/docs/codepush.rst b/docs/codepush.rst new file mode 100644 index 0000000000..b20e90cb96 --- /dev/null +++ b/docs/codepush.rst @@ -0,0 +1,18 @@ +Using Sentry with CodePush +-------------------------- + +If you want to use sentry together with codepush you have to send us the codepush version:: + +.. sourcecode:: javascript + + import CodePush from "react-native-code-push"; + + CodePush.getUpdateMetadata().then((update) => { + if (update) { + Sentry.setVersion('codepush:' + update.label); + } + }); + +Put this somewhere in you code where you already use codepush. This makes sure that we can +associate crashes with the right sourcemaps. +``Sentry.setVersion`` sets the the release to ``bundle_id-version`` this works for iOS aswell as Android. diff --git a/docs/index.rst b/docs/index.rst index 0be7887663..dd1291e7a8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -100,6 +100,7 @@ Deep Dive config expo + codepush sourcemaps cocoapods manual-setup diff --git a/examples/ReactNativeExample/package.json b/examples/ReactNativeExample/package.json index 5de1421768..c361419936 100644 --- a/examples/ReactNativeExample/package.json +++ b/examples/ReactNativeExample/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "react": "^16.0.0-alpha.6", - "react-native": "^0.44.1", + "react-native": "^0.44.2", "react-native-sentry": "file:../../" } } diff --git a/ios/Sentry b/ios/Sentry index e8ed69af8c..315df59eca 160000 --- a/ios/Sentry +++ b/ios/Sentry @@ -1 +1 @@ -Subproject commit e8ed69af8cf4d1d56f7bbd918d6eb342214cae13 +Subproject commit 315df59ecabc0c6a2240bb17cbd9f688fb352e71 diff --git a/lib/Sentry.js b/lib/Sentry.js index a1e7d5b40c..eea7595f0a 100644 --- a/lib/Sentry.js +++ b/lib/Sentry.js @@ -166,6 +166,10 @@ export class Sentry { Sentry._setInternalOption('dist', dist); } + static setVersion(version) { + Sentry._setInternalOption('version', version); + } + // Private helpers static _setInternalOption(key, value) { @@ -301,6 +305,7 @@ class RavenClient { data.dist = Sentry.options.internal['dist']; } }); + Raven.config(dsn, this.options).install(); if (options.logLevel >= SentryLog.Debug) { Raven.debug = true; diff --git a/package.json b/package.json index baaf8a2fed..af944d3b06 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "glob": "7.1.1", "inquirer": "3.0.6", "raven-js": "^3.15.0", - "sentry-cli-binary": "^1.11.0", + "sentry-cli-binary": "^1.12.0", "xcode": "0.9.3" }, "rnpm": {