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 performance monitoring #606

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2224,3 +2224,34 @@ Alternatively, a different folder can be specified by setting the `MLS_ASSETS_FO
For a form named `example_form` the generated assets will follow the following naming convention : `example_form.json` and `example_form.properties`.

The properties file can then be copied over to the `resources` folder of your Android project under `src/main`. The placeholder-injected JsonForm will typically be copied over to the `assets` folder of your Android project (although not mandatory).

## How to use Sentry for OpenSRP Client Performance Monitoring

This section explains how to setup performance monitoring with Sentry on opensrp clients if you have added opensrp-native-forms library as a dependency. Instructions are given in this article on how to initialize Sentry in the opensrp client apps and how to check for the performance data or transactions on the Sentry dashboard.

> ### Note
>
> * You need to have already setup a [project](https://docs.sentry.io/product/sentry-basics/integrate-frontend/create-new-project/) and dashboard with the necessary access permissions, and
> * are able to obtain the dsn of this project through `[Project] > Settings > Client Keys (DSN)`. This will be required later for configuration and logging.

### Instructions

1. Add and update opensrp-native-forms library to version above v3.0.4-SNAPSHOT

2. Sync and rebuild project to ensure new dependencies are loaded. Sentry would come as dependency to opensrp-native-forms and would load up automatically. Run your application, and confirm no logs are sent to the sentry dashboard

3. There are a couple of configurations that would need to be setup first for logs to. show up to the dashboard. These are:
* DSN (Data Source Name); you can find your DSN in your Sentry project settings by navigating to `[Project] > Settings > Client Keys (DSN)` in sentry.io.

* Environment; unique name that would be used to tag logs

In your app’s Application class, add the following code snippet in onCreate with the placeholder values replaced . Run your application once more

```Java
SentryAndroid.init(this, options -> {
options.setEnvironment("YOUR_ENVIRONMENT_NAME");
options.setDsn("YOUR_DSN_NAME_LINK");
})
```

4. Logs in performance monitoring should show up in the Sentry dashboard. Use the Environment tag set from the previous step to filter environment.
20 changes: 20 additions & 0 deletions android-json-form-wizard/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ android {
multiDexEnabled true
testInstrumentationRunner "com.android.test.runner.MultiDexTestRunner"

javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath = true
}
}

multiDexKeepProguard file('multidex-config.pro')
}

buildTypes {
Expand All @@ -58,6 +65,14 @@ android {
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
}
configurations.all {
resolutionStrategy.force 'com.android.support:design:28.0.0'
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}


Expand Down Expand Up @@ -137,6 +152,8 @@ dependencies {
implementation "org.greenrobot:eventbus:3.2.0"
implementation 'androidx.multidex:multidex:2.0.1'

api 'io.sentry:sentry-android:5.0.1'

// PowerMock
def powerMockVersion = '2.0.9'
testImplementation "org.powermock:powermock-module-junit4:$powerMockVersion"
Expand Down Expand Up @@ -210,3 +227,6 @@ task javadoc(type: Javadoc) {
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
classpath += configurations.compile
}



2 changes: 2 additions & 0 deletions android-json-form-wizard/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
android:theme="@style/NativeFormsAppTheme"
tools:replace="android:theme">

<meta-data android:name="io.sentry.auto-init" android:value="false" />
<activity
android:name=".activities.JsonFormBarcodeScanActivity"
android:configChanges="keyboardHidden|orientation"
Expand All @@ -36,5 +37,6 @@
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />

</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
Expand All @@ -27,6 +29,10 @@

public class DatePickerDialog extends DialogFragment {
private DatePicker datePicker;

private Button okButton;

private Button cancelButton;
private android.app.DatePickerDialog.OnDateSetListener onDateSetListener;
private DialogInterface.OnShowListener onShowListener;
private Date date;
Expand Down Expand Up @@ -67,9 +73,6 @@ public void setOnShowListener(DialogInterface.OnShowListener onShowListener_) {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup dialogView = (ViewGroup) inflater.inflate(R.layout.native_form_dialog_date_picker, container, false);

Button cancelButton;
Button okButton;

setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
Expand Down Expand Up @@ -179,4 +182,14 @@ public DatePicker getDatePicker() {
public void setNumericDatePicker(boolean numericDatePicker) {
isNumericDatePicker = numericDatePicker;
}

@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public Button getOkButton(){
return okButton;
}

@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public Button getCancelButton(){
return cancelButton;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1139,28 +1139,30 @@ public JSONArray getSecondaryValues(JSONObject jsonObject, String type) {
*/
public JSONArray getFormFields(String stepName, Context context) {
Activity activity = (Activity) context;
JsonApi jsonApi = (JsonApi) activity;
JSONArray fields = new JSONArray();
JSONObject mJSONObject = jsonApi.getmJSONObject();
if (mJSONObject != null) {
JSONObject parentJson = jsonApi.getStep(stepName);
try {
if (parentJson.has(JsonFormConstants.SECTIONS) && parentJson
.get(JsonFormConstants.SECTIONS) instanceof JSONArray) {
JSONArray sections = parentJson.getJSONArray(JsonFormConstants.SECTIONS);
for (int i = 0; i < sections.length(); i++) {
JSONObject sectionJson = sections.getJSONObject(i);
if (sectionJson.has(JsonFormConstants.FIELDS)) {
fields = concatArray(fields, sectionJson.getJSONArray(JsonFormConstants.FIELDS));
if (activity instanceof JsonApi) {
JsonApi jsonApi = (JsonApi) activity;
JSONObject mJSONObject = jsonApi.getmJSONObject();
if (mJSONObject != null) {
JSONObject parentJson = jsonApi.getStep(stepName);
try {
if (parentJson.has(JsonFormConstants.SECTIONS) && parentJson
.get(JsonFormConstants.SECTIONS) instanceof JSONArray) {
JSONArray sections = parentJson.getJSONArray(JsonFormConstants.SECTIONS);
for (int i = 0; i < sections.length(); i++) {
JSONObject sectionJson = sections.getJSONObject(i);
if (sectionJson.has(JsonFormConstants.FIELDS)) {
fields = concatArray(fields, sectionJson.getJSONArray(JsonFormConstants.FIELDS));
}
}
}
} else if (parentJson.has(JsonFormConstants.FIELDS) && parentJson
.get(JsonFormConstants.FIELDS) instanceof JSONArray) {
fields = parentJson.getJSONArray(JsonFormConstants.FIELDS);
} else if (parentJson.has(JsonFormConstants.FIELDS) && parentJson
.get(JsonFormConstants.FIELDS) instanceof JSONArray) {
fields = parentJson.getJSONArray(JsonFormConstants.FIELDS);

}
} catch (JSONException e) {
Timber.e(e);
}
} catch (JSONException e) {
Timber.e(e);
}
}
return fields;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
*/
public class NativeRadioButtonFactory implements FormWidgetFactory {

private static final String TAG = NativeRadioButtonFactory.class.getCanonicalName();
public static final String TAG = NativeRadioButtonFactory.class.getCanonicalName();
private final FormUtils formUtils = new FormUtils();
private final CustomTextViewClickListener customTextViewClickListener = new CustomTextViewClickListener();
private RadioButton radioButton;
Expand Down
2 changes: 2 additions & 0 deletions android-json-form-wizard/src/multidex-config.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-keep class io.sentry.android.core.SentryAndroidOptions
-keep class io.sentry.android.ndk.SentryNdk
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.vijay.jsonwizard.presenters;

import android.app.Activity;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.MediaStore;
import androidx.appcompat.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
Expand Down Expand Up @@ -48,6 +48,7 @@
import static com.vijay.jsonwizard.presenters.JsonFormFragmentPresenter.RESULT_LOAD_IMG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
Expand Down Expand Up @@ -159,10 +160,10 @@ public void testOnClickShouldSOpenPictureTakingActivity() {
}

@Test
public void testOnClickShouldDisplayDatePickerDialog() {
public void testOnClickShouldDisplayDatePickerDialog() throws InterruptedException {
LinearLayout view = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.native_form_compound_button_parent, null);
CustomTextView customTextView = new CustomTextView(context);
Activity activity = Robolectric.buildActivity(AppCompatActivity.class).create().get();
Activity activity = Robolectric.buildActivity(Activity.class).create().get();
RadioButton radioButton = new RadioButton(context);
view.setTag(R.id.specify_textview, customTextView);
view.setTag(R.id.native_radio_button, radioButton);
Expand All @@ -175,9 +176,17 @@ public void testOnClickShouldDisplayDatePickerDialog() {
view.setTag(R.id.specify_widget, JsonFormConstants.DATE_PICKER);
view.setTag(R.id.option_json_object, new JSONObject());
formFragmentPresenter.onClick(view);
DatePickerDialog dialogFragment = (DatePickerDialog) activity.getFragmentManager()
.findFragmentByTag(NativeRadioButtonFactory.class.getCanonicalName());
assertNotNull(view);
Thread.sleep(3000);
DatePickerDialog dialogFragment = null;
FragmentManager fragmentManager = activity.getFragmentManager();
if (fragmentManager != null){
dialogFragment = (DatePickerDialog) fragmentManager.findFragmentByTag(NativeRadioButtonFactory.TAG);
}
assertNotNull(dialogFragment);

View okButton = dialogFragment.getOkButton();
okButton.performClick();
assertTrue(radioButton.getText().length() > 0);
}

}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION_NAME=3.1.1-SNAPSHOT
VERSION_NAME=3.2.0-SNAPSHOT
VERSION_CODE=1
GROUP=org.smartregister
POM_SETTING_DESCRIPTION=OpenSRP Client Native Form Json Wizard
Expand Down
13 changes: 13 additions & 0 deletions sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ android {
versionCode 1
versionName "1.0"
multiDexEnabled true

buildConfigField("String", "SENTRY_DSN", "\"" + getProps("sentry.dsn") + "\"")
}

buildTypes {
Expand Down Expand Up @@ -60,3 +62,14 @@ dependencies {

implementation 'org.smartregister:opensrp-client-utils:0.0.6-SNAPSHOT'
}

// get property from local.properties
def getProps(String propName) {
def propsFile = rootProject.file('local.properties')
if (propsFile.exists()) {
def props = new Properties()
props.load(new FileInputStream(propsFile))
return props[propName]
}
return "";
}
1 change: 1 addition & 0 deletions sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:name=".MainApplication"
android:theme="@style/NativeFormsAppTheme">
<activity
android:name=".MainActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import org.json.JSONArray;
import org.json.JSONObject;

import io.sentry.ITransaction;
import io.sentry.Sentry;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int REQUEST_CODE_GET_JSON = 1234;
private static final String TAG = MainActivity.class.getCanonicalName();
Expand Down Expand Up @@ -97,6 +100,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {

public void startForm(int jsonFormActivityRequestCode, String formName, String entityId, boolean translate) throws Exception {

ITransaction transaction = Sentry.startTransaction("#startForm", String.format("formName: %s", formName.toUpperCase()));

final String STEP1 = "step1";
final String FIELDS = "fields";
final String KEY = "key";
Expand Down Expand Up @@ -255,7 +260,7 @@ public void startForm(int jsonFormActivityRequestCode, String formName, String e
break;
}
}

transaction.finish();
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.smartregister.nativeform;

import android.app.Application;

import io.sentry.android.core.SentryAndroid;

public class MainApplication extends Application {

@Override
public void onCreate() {
super.onCreate();

//noinspection ConstantConditions
if (!BuildConfig.SENTRY_DSN.trim().isEmpty()) {
SentryAndroid.init(this, options -> {
options.setEnvironment("opensrp-native-form-sample");
options.setDsn(BuildConfig.SENTRY_DSN.trim());
});
}

}
}