Skip to content

Commit

Permalink
Bump Android SDK & Set Native/Android SDK Name on crashes (#406)
Browse files Browse the repository at this point in the history
* bump android & add native package name

* fix ios code

* change changelog

* add tests for   sdkinfo

* Update android/src/main/java/io/sentry/capacitor/SentryCapacitor.java

* Apply suggestions from code review

Co-authored-by: Kryštof Woldřich <[email protected]>

* add missing cap ref

* fix tst

* requested changes

---------

Co-authored-by: Kryštof Woldřich <[email protected]>
  • Loading branch information
lucas-zimerman and krystofwoldrich authored Jul 27, 2023
1 parent a7b5e2f commit dffdcb8
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 30 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

### Dependencies

- Bump Sentry Android SDK to `6.19.0` ([#406](https://github.com/getsentry/sentry-capacitor/pull/406))
- [changelog](https://github.com/getsentry/sentry-java/releases/tag/6.19.0)
- [diff](https://github.com/getsentry/sentry-java/compare/6.17.0...6.19.0)
- Bump sentry-cocoa SDK to `8.8.0` ([#397](https://github.com/getsentry/sentry-capacitor/pull/397))
- [changelog](https://github.com/getsentry/sentry-cocoa/releases/tag/8.8.0)
- [diff](https://github.com/getsentry/sentry-cocoa/compare/7.27.1...8.8.0)
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ repositories {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':capacitor-android')
implementation 'io.sentry:sentry-android:6.17.0'
implementation 'io.sentry:sentry-android:6.19.0'
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
Expand Down
29 changes: 21 additions & 8 deletions android/src/main/java/io/sentry/capacitor/SentryCapacitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import android.content.Context;
import android.content.pm.PackageInfo;
import android.util.Log;
import com.getcapacitor.JSArray;
import com.getcapacitor.JSObject;
import com.getcapacitor.NativePlugin;
Expand All @@ -16,6 +15,7 @@
import io.sentry.SentryEvent;
import io.sentry.SentryLevel;
import io.sentry.UncaughtExceptionHandlerIntegration;
import io.sentry.android.core.BuildConfig;
import io.sentry.android.core.AnrIntegration;
import io.sentry.android.core.NdkIntegration;
import io.sentry.android.core.SentryAndroid;
Expand All @@ -25,7 +25,6 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
Expand All @@ -37,6 +36,9 @@
@NativePlugin
public class SentryCapacitor extends Plugin {

private static final String NATIVE_SDK_NAME = "sentry.native.android.capacitor";
private static final String ANDROID_SDK_NAME = "sentry.java.android.capacitor";

static final Logger logger = Logger.getLogger("capacitor-sentry");
private Context context;
private static PackageInfo packageInfo;
Expand All @@ -63,11 +65,22 @@ public void initNativeSdk(final PluginCall call) {
SentryAndroid.init(
this.getContext(),
options -> {
SdkVersion sdkVersion = options.getSdkVersion();
if (sdkVersion == null) {
sdkVersion = new SdkVersion(ANDROID_SDK_NAME, BuildConfig.VERSION_NAME);
} else {
sdkVersion.setName(ANDROID_SDK_NAME);
}

if (capOptions.has("debug") && capOptions.getBool("debug")) {
options.setDebug(true);
logger.setLevel(Level.INFO);
}

options.setSentryClientName(sdkVersion.getName() + "/" + sdkVersion.getVersion());
options.setNativeSdkName(NATIVE_SDK_NAME);
options.setSdkVersion(sdkVersion);

String dsn = capOptions.getString("dsn") != null ? capOptions.getString("dsn") : "";
logger.info(String.format("Starting with DSN: '%s'", dsn));
options.setDsn(dsn);
Expand Down Expand Up @@ -343,20 +356,20 @@ public void setEventOriginTag(SentryEvent event) {
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");
case NATIVE_SDK_NAME:
setEventEnvironmentTag(event, "native");
break;
case "sentry.java.android":
setEventEnvironmentTag(event, "android", "java");
case ANDROID_SDK_NAME:
setEventEnvironmentTag(event, "java");
break;
default:
break;
}
}
}

private void setEventEnvironmentTag(SentryEvent event, String origin, String environment) {
event.setTag("event.origin", origin);
private void setEventEnvironmentTag(SentryEvent event, String environment) {
event.setTag("event.origin", "android");
event.setTag("event.environment", environment);
}

Expand Down
26 changes: 21 additions & 5 deletions ios/Plugin/Plugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import Sentry
@objc(SentryCapacitor)
public class SentryCapacitor: CAPPlugin {

private let nativeSdkName = "sentry.cocoa.capacitor";

private var sentryOptions: Options?

// The Cocoa SDK is init. after the notification didBecomeActiveNotification is registered.
Expand Down Expand Up @@ -45,6 +47,9 @@ public class SentryCapacitor: CAPPlugin {
do {
let options = try Options.init(dict: optionsDict)

let sdkVersion = PrivateSentrySDKOnly.getSdkVersionString()
PrivateSentrySDKOnly.setSdkName(nativeSdkName, andVersionString: sdkVersion)

// Note: For now, in sentry-cocoa, beforeSend is not called before captureEnvelope
options.beforeSend = { [weak self] event in
self?.setEventOriginTag(event: event)
Expand Down Expand Up @@ -290,15 +295,26 @@ public class SentryCapacitor: CAPPlugin {
}

private func setEventOriginTag(event: Event) {
guard let sdk = event.sdk, isValidSdk(sdk: sdk), let name = sdk["name"] as? String, name == "sentry.cocoa" else {
guard let sdk = event.sdk, isValidSdk(sdk: sdk), let name = sdk["name"] as? String, name == nativeSdkName else {
return
}
setEventEnvironmentTag(event: event, origin: "ios", environment: "native")
setEventEnvironmentTag(event: event, environment: "native")
}

private func setEventEnvironmentTag(event: Event, origin: String, environment: String) {
event.tags?["event.origin"] = origin
event.tags?["event.environment"] = environment
private func setEventEnvironmentTag(event: Event, environment: String) {
var newTags = [String: String]()

if let tags = event.tags, !tags.isEmpty {
newTags.merge(tags) { (_, new) in new }
}

newTags["event.origin"] = "ios"

if !environment.isEmpty {
newTags["event.environment"] = environment
}

event.tags = newTags
}

private func isValidSdk(sdk: [String: Any]) -> Bool {
Expand Down
31 changes: 15 additions & 16 deletions src/integrations/sdkinfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class SdkInfo implements Integration {
*/
public name: string = SdkInfo.id;

private _nativeSdkInfo: Package | null = null;
private _nativeSdkPackage: Package | null = null;

/**
* @inheritDoc
Expand All @@ -25,9 +25,9 @@ export class SdkInfo implements Integration {
addGlobalEventProcessor(async event => {
// The native SDK info package here is only used on iOS as `beforeSend` is not called on `captureEnvelope`.
// this._nativeSdkInfo should be defined a following time so this call won't always be awaited.
if (NATIVE.platform === 'ios' && this._nativeSdkInfo === null) {
if (NATIVE.platform === 'ios' && this._nativeSdkPackage === null) {
try {
this._nativeSdkInfo = await NATIVE.fetchNativeSdkInfo();
this._nativeSdkPackage = await NATIVE.fetchNativeSdkInfo();
} catch (_) {
// If this fails, go ahead as usual as we would rather have the event be sent with a package missing.
logger.warn(
Expand All @@ -37,19 +37,18 @@ export class SdkInfo implements Integration {
}

event.platform = event.platform || 'javascript';
event.sdk = {
...event.sdk,
name: SDK_NAME,
packages: [
...((event.sdk && event.sdk.packages) || []),
...((this._nativeSdkInfo && [this._nativeSdkInfo]) || []),
{
name: 'npm:@sentry/capacitor',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};
event.sdk = event.sdk || {};
event.sdk.name = event.sdk.name || SDK_NAME;
event.sdk.version = event.sdk.version || SDK_VERSION;
event.sdk.packages = [
// default packages are added by baseclient and should not be added here
...(event.sdk.packages || []),
...((this._nativeSdkPackage && [this._nativeSdkPackage]) || []),
{
name: 'npm:@sentry/capacitor',
version: SDK_VERSION,
},
];

return event;
});
Expand Down
1 change: 1 addition & 0 deletions src/version.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const SDK_PACKAGE_NAME = 'npm:@sentry/capacitor';
export const SDK_NAME = 'sentry.javascript.capacitor';
export const SDK_VERSION = '0.12.1';
88 changes: 88 additions & 0 deletions test/integrations/sdkinfo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import type { Event, EventHint, Package } from '@sentry/types';

import { SDK_NAME, SDK_VERSION } from '../../src/';
import { SdkInfo } from '../../src/integrations';
import { NATIVE } from '../../src/wrapper';

let mockedFetchNativeSdkInfo: jest.Mock<PromiseLike<Package | null>, []>;

const mockPackage = {
name: 'sentry-cocoa',
version: '0.0.1',
};

jest.mock('../../src/wrapper', () => {
const actual = jest.requireActual('../../src/wrapper');

return {
NATIVE: {
...actual.NATIVE,
platform: 'ios',
fetchNativeSdkInfo: () => mockedFetchNativeSdkInfo(),
},
};
});

afterEach(() => {
NATIVE.platform = 'ios';
});

describe('Sdk Info', () => {
it('Adds native package and javascript platform to event on iOS', async () => {
mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(mockPackage);
const mockEvent: Event = {};
const processedEvent = await executeIntegrationFor(mockEvent);

expect(processedEvent?.sdk?.packages).toEqual(expect.arrayContaining([mockPackage]));
expect(processedEvent?.platform === 'javascript');
expect(mockedFetchNativeSdkInfo).toBeCalledTimes(1);
});

it('Adds javascript platform but not native package on Android', async () => {
NATIVE.platform = 'android';
mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(mockPackage);
const mockEvent: Event = {};
const processedEvent = await executeIntegrationFor(mockEvent);

expect(processedEvent?.sdk?.packages).toEqual(expect.not.arrayContaining([mockPackage]));
expect(processedEvent?.platform === 'javascript');
expect(mockedFetchNativeSdkInfo).not.toBeCalled();
});

it('Does not overwrite existing sdk name and version', async () => {
mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(null);
const mockEvent: Event = {
sdk: {
name: 'test-sdk',
version: '1.0.0',
},
};
const processedEvent = await executeIntegrationFor(mockEvent);

expect(processedEvent?.sdk?.name).toEqual('test-sdk');
expect(processedEvent?.sdk?.version).toEqual('1.0.0');
});

it('Does use default sdk name and version', async () => {
mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(null);
const mockEvent: Event = {};
const processedEvent = await executeIntegrationFor(mockEvent);

expect(processedEvent?.sdk?.name).toEqual(SDK_NAME);
expect(processedEvent?.sdk?.version).toEqual(SDK_VERSION);
});
});

function executeIntegrationFor(mockedEvent: Event, mockedHint: EventHint = {}): Promise<Event | null> {
const integration = new SdkInfo();
return new Promise((resolve, reject) => {
integration.setupOnce(async eventProcessor => {
try {
const processedEvent = await eventProcessor(mockedEvent, mockedHint);
resolve(processedEvent);
} catch (e) {
reject(e);
}
});
});
}

0 comments on commit dffdcb8

Please sign in to comment.