diff --git a/android/build.gradle b/android/build.gradle index 2b22e5a0..a7a154fc 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -51,8 +51,7 @@ repositories { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':capacitor-android') - implementation 'io.sentry:sentry-android:4.3.0' - implementation 'com.google.code.gson:gson:2.8.6' + implementation 'io.sentry:sentry-android:5.0.1' testImplementation "junit:junit:$junitVersion" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" diff --git a/android/src/main/java/io/sentry/capacitor/SentryCapacitor.java b/android/src/main/java/io/sentry/capacitor/SentryCapacitor.java index 69e6fdf4..dc885a4f 100644 --- a/android/src/main/java/io/sentry/capacitor/SentryCapacitor.java +++ b/android/src/main/java/io/sentry/capacitor/SentryCapacitor.java @@ -5,18 +5,19 @@ import com.getcapacitor.Plugin; import com.getcapacitor.PluginCall; import com.getcapacitor.PluginMethod; -import com.google.gson.Gson; import io.sentry.Breadcrumb; +import io.sentry.HubAdapter; import io.sentry.Integration; import io.sentry.Sentry; import io.sentry.SentryLevel; -import io.sentry.SentryOptions; +import io.sentry.SentryEvent; import io.sentry.UncaughtExceptionHandlerIntegration; import io.sentry.android.core.AnrIntegration; import io.sentry.android.core.NdkIntegration; import io.sentry.android.core.SentryAndroid; import io.sentry.protocol.SdkVersion; +import io.sentry.protocol.SentryPackage; import io.sentry.protocol.User; import java.io.File; @@ -24,6 +25,7 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -38,7 +40,6 @@ public class SentryCapacitor extends Plugin { final static Logger logger = Logger.getLogger("capacitor-sentry"); - private SentryOptions sentryOptions; private Context context; private static PackageInfo packageInfo; @@ -99,25 +100,8 @@ public void startWithOptions(final PluginCall capOptions) { options.setBeforeSend( (event, hint) -> { - // Add on the correct event.origin tag. - // it needs to be here so we can determine where it originated from. - SdkVersion sdkVersion = event.getSdk(); - if (sdkVersion != null) { - String sdkName = sdkVersion.getName(); - if (sdkName != null) { - if (sdkName.equals("sentry.javascript.capacitor")) { - event.setTag("event.origin", "javascript"); - } else if (sdkName.equals("sentry.java.android") || sdkName.equals("sentry.native")) { - event.setTag("event.origin", "android"); - - if (sdkName.equals("sentry.native")) { - event.setTag("event.environment", "native"); - } else { - event.setTag("event.environment", "java"); - } - } - } - } + setEventOriginTag(event); + addPackages(event, options.getSdkVersion()); return event; } @@ -137,7 +121,6 @@ public void startWithOptions(final PluginCall capOptions) { } logger.info(String.format("Native Integrations '%s'", options.getIntegrations().toString())); - sentryOptions = options; } ); @@ -206,9 +189,18 @@ public void fetchRelease(PluginCall call) { @PluginMethod public void captureEnvelope(PluginCall call) { - String envelope = call.getString("envelope"); try { - File installation = new File(sentryOptions.getOutboxPath(), UUID.randomUUID().toString()); + String envelope = call.getString("envelope"); + final String outboxPath = HubAdapter.getInstance().getOptions().getOutboxPath(); + + if (outboxPath == null || outboxPath.isEmpty()) { + logger.info("Error when writing envelope, no outbox path is present."); + call.reject("Missing outboxPath"); + return; + } + + final File installation = new File(outboxPath, UUID.randomUUID().toString()); + try (FileOutputStream out = new FileOutputStream(installation)) { out.write(envelope.getBytes(Charset.forName("UTF-8"))); logger.info("Successfully captured envelope."); @@ -216,7 +208,6 @@ public void captureEnvelope(PluginCall call) { JSObject resp = new JSObject(); resp.put("value", envelope); call.resolve(resp); - } catch (Exception e) { logger.info("Error writing envelope."); call.reject(String.valueOf(e)); @@ -229,7 +220,7 @@ public void captureEnvelope(PluginCall call) { return; } } - + @PluginMethod public void getStringBytesLength(PluginCall call) { if (call.getData().has("string")) { @@ -288,12 +279,13 @@ public void addBreadcrumb(final PluginCall breadcrumb) { if (breadcrumb.getData().has("data")) { JSObject data = breadcrumb.getObject("data"); - HashMap mappedData = new Gson().fromJson(data.toString(), HashMap.class); + Iterator it = data.keys(); + + while (it.hasNext()) { + String key = it.next(); + String value = data.getString(key); - for ( HashMap.Entry entry: mappedData.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - breadcrumbInstance.setData(key, value); + breadcrumbInstance.setData(key, value); } } @@ -316,7 +308,7 @@ public void setExtra(PluginCall call) { String key = call.getString("key"); String extra = call.getString("extra"); scope.setExtra(key, extra); - }); + }); } call.resolve(); } @@ -332,4 +324,47 @@ public void setTag(PluginCall call) { } call.resolve(); } + + public void setEventOriginTag(SentryEvent event) { + SdkVersion sdk = event.getSdk(); + if (sdk != null) { + switch (sdk.getName()) { + // If the event is from capacitor js, it gets set there and we do not handle it here. + case "sentry.native": + setEventEnvironmentTag(event, "android", "native"); + break; + case "sentry.java.android": + setEventEnvironmentTag(event, "android", "java"); + break; + default: + break; + } + } + } + + private void setEventEnvironmentTag(SentryEvent event, String origin, String environment) { + event.setTag("event.origin", origin); + event.setTag("event.environment", environment); + } + + public void addPackages(SentryEvent event, SdkVersion sdk) { + SdkVersion eventSdk = event.getSdk(); + if (eventSdk != null && eventSdk.getName().equals("sentry.javascript.capacitor") && sdk != null) { + List sentryPackages = sdk.getPackages(); + if (sentryPackages != null) { + for (SentryPackage sentryPackage : sentryPackages) { + eventSdk.addPackage(sentryPackage.getName(), sentryPackage.getVersion()); + } + } + + List integrations = sdk.getIntegrations(); + if (integrations != null) { + for (String integration : integrations) { + eventSdk.addIntegration(integration); + } + } + + event.setSdk(eventSdk); + } + } } diff --git a/src/wrapper.ts b/src/wrapper.ts index 9ca9673c..b6dcb22b 100644 --- a/src/wrapper.ts +++ b/src/wrapper.ts @@ -15,7 +15,7 @@ export const NATIVE = { * Sending the event over the bridge to native * @param event Event */ - async sendEvent(event: Event): Promise { + async sendEvent(_event: Event): Promise { if (!this.enableNative) { throw this._DisabledNativeError; } @@ -23,8 +23,7 @@ export const NATIVE = { throw this._NativeClientError; } - // Process and convert deprecated levels - event.level = event.level ? this._processLevel(event.level) : undefined; + const event = this._processLevels(_event); const header = { event_id: event.event_id, @@ -259,6 +258,26 @@ export const NATIVE = { return serialized; }, + /** + * Convert js severity level in event.level and event.breadcrumbs to more widely supported levels. + * @param event + * @returns Event with more widely supported Severity level strings + */ + _processLevels(event: Event): Event { + const processed: Event = { + ...event, + level: event.level ? this._processLevel(event.level) : undefined, + breadcrumbs: event.breadcrumbs?.map(breadcrumb => ({ + ...breadcrumb, + level: breadcrumb.level + ? this._processLevel(breadcrumb.level) + : undefined, + })), + }; + + return processed; + }, + /** * Convert js severity level which has critical and log to more widely supported levels. * @param level