Skip to content

Commit 0070c47

Browse files
authored
[Feat][SDK-347] ANR report (#323)
* feat: Update android min sdk from 16 to 21 and target/compile sdk from 27 to 33 * feat: Add ANR detectors * feat: send non main threads as extra information for api >= 30 * refactor: remove unnecessary code * refactor: don't launch thread if listener is null * refactor: remove unnecessary attribute for Line * refactor: do not create watchdog or run it if anr listener is null * test: add Tests for Watchdog * test: remove unused imports * test: set parameters as private * test: Add tests for HistoricalAnrDetector * refactor: Initialize ThreadParser as expected * feat: Add AndroidConfiguration to tun on/off ANR detectors implementations * refactor: use RollbarThread to send ANR information * feat(HistoricalAnrDetector): save last anr timestamp * build: downgrade compile and target sdk to 30, to test CI * test: delete tests to validate if CI completes * fix: lint * fix(RollbarThrowableWrapper): lint * fix(RollbarThrowableWrapper): lint * chore: add new method information in revapi file * build(android-example): migrate old libs to AndroidX * build: update compile and target sdk to 32 * build: downgrade compile and target sdk to 31 * Revert "test: delete tests to validate if CI completes" This reverts commit c8e8c77. * refactor: set HistoricalAnrDetector File for Anr timestamps by constructor to improve testing * build: downgrade compile and target sdk to 30 * refactor: remove LockReason * refactor: simplify getStackTraceElements method * refactor: remove Line and Lines, use a List<String> instead * refactor: remove LockReason regex patterns * refactor: improve test coverage --------- Co-authored-by: chris <[email protected]>
1 parent b88ad18 commit 0070c47

File tree

32 files changed

+1905
-94
lines changed

32 files changed

+1905
-94
lines changed

.palantir/revapi.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,8 @@ acceptedBreaks:
4545
justification: "This is a binary compatible change, which could only break custom\
4646
\ implementations of our config interfaces, but those interfaces are not meant\
4747
\ to be implemented by users"
48+
- code: "java.method.addedToInterface"
49+
new: "method java.util.List<com.rollbar.api.payload.data.body.RollbarThread> com.rollbar.notifier.wrapper.ThrowableWrapper::getRollbarThreads()"
50+
justification: "This is a binary compatible change, which could only break custom\
51+
\ implementations of our config interfaces, but those interfaces are not meant\
52+
\ to be implemented by users"

examples/rollbar-android/build.gradle

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ buildscript {
1111
apply plugin: 'com.android.application'
1212

1313
android {
14-
compileSdkVersion 27
14+
compileSdkVersion 30
1515
buildToolsVersion "30.0.3"
1616
defaultConfig {
1717
applicationId "com.rollbar.example.android"
18-
minSdkVersion 16
18+
minSdkVersion 21
1919
// FIXME: Pending further discussion
2020
//noinspection ExpiredTargetSdkVersion
21-
targetSdkVersion 27
21+
targetSdkVersion 30
2222
versionCode 1
2323
versionName "1.0"
2424
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -38,9 +38,9 @@ dependencies {
3838
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
3939
exclude group: 'com.android.support', module: 'support-annotations'
4040
})
41-
implementation 'com.android.support:appcompat-v7:27.1.1'
41+
implementation 'androidx.appcompat:appcompat:1.0.2'
4242
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
43-
implementation 'com.android.support:design:27.1.1'
43+
implementation 'com.google.android.material:material:1.0.0'
4444
implementation 'org.slf4j:slf4j-android:1.7.25'
4545
testImplementation group: 'junit', name: 'junit', version: '4.12'
4646
}

examples/rollbar-android/src/main/AndroidManifest.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
android:theme="@style/AppTheme">
1717
<activity
1818
android:name=".MainActivity"
19-
android:label="@string/app_name"
19+
android:exported="true"
20+
android:label="@string/app_name"
2021
android:theme="@style/AppTheme.NoActionBar">
2122
<intent-filter>
2223
<action android:name="android.intent.action.MAIN"/>

examples/rollbar-android/src/main/java/com/rollbar/example/android/MainActivity.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package com.rollbar.example.android;
22

33
import android.os.Bundle;
4-
import android.support.design.widget.FloatingActionButton;
5-
import android.support.design.widget.Snackbar;
6-
import android.support.v7.app.AppCompatActivity;
7-
import android.support.v7.widget.Toolbar;
84
import android.util.Log;
95
import android.view.View;
106
import android.view.Menu;
117
import android.view.MenuItem;
8+
9+
import androidx.appcompat.app.AppCompatActivity;
10+
import androidx.appcompat.widget.Toolbar;
11+
12+
import com.google.android.material.floatingactionbutton.FloatingActionButton;
13+
import com.google.android.material.snackbar.Snackbar;
1214
import com.rollbar.android.Rollbar;
1315

1416
public class MainActivity extends AppCompatActivity {
Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,34 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<android.support.design.widget.CoordinatorLayout
3-
xmlns:android="http://schemas.android.com/apk/res/android"
4-
xmlns:app="http://schemas.android.com/apk/res-auto"
5-
xmlns:tools="http://schemas.android.com/tools"
2+
<androidx.coordinatorlayout.widget.CoordinatorLayout
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
xmlns:app="http://schemas.android.com/apk/res-auto"
5+
xmlns:tools="http://schemas.android.com/tools"
6+
android:layout_width="match_parent"
7+
android:layout_height="match_parent"
8+
tools:context="com.rollbar.example.android.MainActivity">
9+
10+
<com.google.android.material.appbar.AppBarLayout
11+
android:layout_height="wrap_content"
612
android:layout_width="match_parent"
7-
android:layout_height="match_parent"
8-
tools:context="com.rollbar.example.android.MainActivity">
13+
android:theme="@style/AppTheme.AppBarOverlay">
914

10-
<android.support.design.widget.AppBarLayout
11-
android:layout_height="wrap_content"
15+
<androidx.appcompat.widget.Toolbar
16+
android:id="@+id/toolbar"
1217
android:layout_width="match_parent"
13-
android:theme="@style/AppTheme.AppBarOverlay">
14-
15-
<android.support.v7.widget.Toolbar
16-
android:id="@+id/toolbar"
17-
android:layout_width="match_parent"
18-
android:layout_height="?attr/actionBarSize"
19-
android:background="?attr/colorPrimary"
20-
app:popupTheme="@style/AppTheme.PopupOverlay"/>
18+
android:layout_height="?attr/actionBarSize"
19+
android:background="?attr/colorPrimary"
20+
app:popupTheme="@style/AppTheme.PopupOverlay"/>
2121

22-
</android.support.design.widget.AppBarLayout>
22+
</com.google.android.material.appbar.AppBarLayout>
2323

2424
<include layout="@layout/content_main"/>
2525

26-
<android.support.design.widget.FloatingActionButton
27-
android:id="@+id/fab"
28-
android:layout_width="wrap_content"
29-
android:layout_height="wrap_content"
30-
android:layout_gravity="bottom|end"
31-
android:layout_margin="@dimen/fab_margin"
32-
app:srcCompat="@android:drawable/ic_dialog_email"/>
26+
<com.google.android.material.floatingactionbutton.FloatingActionButton
27+
android:id="@+id/fab"
28+
android:layout_width="wrap_content"
29+
android:layout_height="wrap_content"
30+
android:layout_gravity="bottom|end"
31+
android:layout_margin="@dimen/fab_margin"
32+
app:srcCompat="@android:drawable/ic_dialog_email"/>
3333

34-
</android.support.design.widget.CoordinatorLayout>
34+
</androidx.coordinatorlayout.widget.CoordinatorLayout>

gradle.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
VERSION_NAME=2.0.0
22
GROUP=com.rollbar
33

4+
android.useAndroidX=true
5+
46
POM_DESCRIPTION=For connecting your applications built on the JVM to Rollbar for Error Reporting
57
POM_URL=https://github.com/rollbar/rollbar-java
68
POM_SCM_URL=https://github.com/rollbar/rollbar-java

rollbar-android/build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ apply from: "$rootDir/gradle/release.gradle"
1818
apply from: "$rootDir/gradle/android.quality.gradle"
1919

2020
android {
21-
compileSdkVersion 27
21+
compileSdkVersion 30
2222
buildToolsVersion '30.0.3' // Going above here requires bumping the AGP to version 4+
2323

2424
defaultConfig {
25-
minSdkVersion 16
25+
minSdkVersion 21
2626
// FIXME: Pending further discussion
2727
//noinspection ExpiredTargetSdkVersion
28-
targetSdkVersion 27
28+
targetSdkVersion 30
2929
consumerProguardFiles 'proguard-rules.pro'
3030
manifestPlaceholders = [notifierVersion: VERSION_NAME]
3131
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.rollbar.android;
2+
3+
import com.rollbar.android.anr.AnrConfiguration;
4+
5+
public class AndroidConfiguration {
6+
private final AnrConfiguration anrConfiguration;
7+
8+
AndroidConfiguration(Builder builder) {
9+
anrConfiguration = builder.anrConfiguration;
10+
}
11+
12+
public AnrConfiguration getAnrConfiguration() {
13+
return anrConfiguration;
14+
}
15+
16+
17+
public static final class Builder {
18+
private AnrConfiguration anrConfiguration;
19+
20+
Builder() {
21+
anrConfiguration = new AnrConfiguration.Builder().build();
22+
}
23+
24+
/**
25+
* The ANR configuration, if this field is null, no ANR would be captured
26+
* @param anrConfiguration the ANR configuration
27+
* @return the builder instance
28+
*/
29+
public Builder setAnrConfiguration(AnrConfiguration anrConfiguration) {
30+
this.anrConfiguration = anrConfiguration;
31+
return this;
32+
}
33+
34+
public AndroidConfiguration build() {
35+
return new AndroidConfiguration(this);
36+
}
37+
}
38+
}

rollbar-android/src/main/java/com/rollbar/android/Rollbar.java

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@
1010
import android.os.Bundle;
1111

1212
import android.util.Log;
13+
14+
import com.rollbar.android.anr.AnrDetector;
15+
import com.rollbar.android.anr.AnrDetectorFactory;
16+
import com.rollbar.android.anr.AnrException;
1317
import com.rollbar.android.notifier.sender.ConnectionAwareSenderFailureStrategy;
1418
import com.rollbar.android.provider.ClientProvider;
1519
import com.rollbar.api.payload.data.TelemetryType;
20+
import com.rollbar.api.payload.data.body.RollbarThread;
1621
import com.rollbar.notifier.config.ConfigProvider;
1722
import com.rollbar.notifier.uncaughtexception.RollbarUncaughtExceptionHandler;
1823
import com.rollbar.android.provider.NotifierProvider;
@@ -24,12 +29,18 @@
2429
import com.rollbar.notifier.sender.SyncSender;
2530
import com.rollbar.notifier.sender.queue.DiskQueue;
2631
import com.rollbar.notifier.util.ObjectsUtils;
32+
import com.rollbar.notifier.wrapper.RollbarThrowableWrapper;
33+
import com.rollbar.notifier.wrapper.ThrowableWrapper;
34+
35+
import org.slf4j.LoggerFactory;
2736

2837
import java.io.Closeable;
2938
import java.io.File;
3039
import java.io.IOException;
3140
import java.lang.Thread.UncaughtExceptionHandler;
3241
import java.util.Collections;
42+
import java.util.HashMap;
43+
import java.util.List;
3344
import java.util.Map;
3445
import java.util.concurrent.TimeUnit;
3546

@@ -71,6 +82,28 @@ public static Rollbar init(Context context) {
7182
return init(context, null, null);
7283
}
7384

85+
/**
86+
* Initialize the singleton instance of Rollbar.
87+
* Defaults to reading the access token from the manifest, handling uncaught exceptions, and setting
88+
* the environment to production.
89+
*
90+
* @param context Android context to use.
91+
* @param androidConfiguration configuration for Android features.
92+
* @return the managed instance of Rollbar.
93+
*/
94+
public static Rollbar init(Context context, AndroidConfiguration androidConfiguration) {
95+
return init(
96+
context,
97+
null,
98+
DEFAULT_ENVIRONMENT,
99+
DEFAULT_REGISTER_EXCEPTION_HANDLER,
100+
DEFAULT_INCLUDE_LOGCAT,
101+
DEFAULT_CONFIG_PROVIDER,
102+
DEFAULT_SUSPEND_WHEN_NETWORK_IS_UNAVAILABLE,
103+
androidConfiguration
104+
);
105+
}
106+
74107
/**
75108
* Initialize the singleton instance of Rollbar.
76109
*
@@ -155,18 +188,71 @@ public static Rollbar init(Context context, String accessToken, String environme
155188
public static Rollbar init(Context context, String accessToken, String environment,
156189
boolean registerExceptionHandler, boolean includeLogcat,
157190
ConfigProvider provider, boolean suspendWhenNetworkIsUnavailable) {
191+
return init(
192+
context,
193+
accessToken,
194+
environment,
195+
registerExceptionHandler,
196+
includeLogcat,
197+
provider,
198+
suspendWhenNetworkIsUnavailable,
199+
makeDefaultAndroidConfiguration()
200+
);
201+
}
202+
203+
/**
204+
* Initialize the singleton instance of Rollbar.
205+
*
206+
* @param context Android context to use.
207+
* @param accessToken a Rollbar access token with at least post_client_item scope
208+
* @param environment the environment to set for items
209+
* @param registerExceptionHandler whether or not to handle uncaught exceptions.
210+
* @param includeLogcat whether or not to include logcat output with items
211+
* @param provider a configuration provider that can be used to customize the configuration further.
212+
* @param suspendWhenNetworkIsUnavailable if true, sending occurrences will be suspended while the network is unavailable
213+
* @param androidConfiguration configuration for Android features
214+
* @return the managed instance of Rollbar.
215+
*/
216+
public static Rollbar init(
217+
Context context,
218+
String accessToken,
219+
String environment,
220+
boolean registerExceptionHandler,
221+
boolean includeLogcat,
222+
ConfigProvider provider,
223+
boolean suspendWhenNetworkIsUnavailable,
224+
AndroidConfiguration androidConfiguration
225+
) {
158226
if (isInit()) {
159227
Log.w(TAG, "Rollbar.init() called when it was already initialized.");
160228
// This is likely an activity that was destroyed and recreated, so we need to update it
161229
notifier.updateContext(context);
162230
} else {
163231
notifier = new Rollbar(context, accessToken, environment, registerExceptionHandler,
164-
includeLogcat, provider, DEFAULT_CAPTURE_IP, DEFAULT_MAX_LOGCAT_SIZE,
165-
suspendWhenNetworkIsUnavailable);
232+
includeLogcat, provider, DEFAULT_CAPTURE_IP, DEFAULT_MAX_LOGCAT_SIZE,
233+
suspendWhenNetworkIsUnavailable);
234+
if (androidConfiguration != null) {
235+
initAnrDetector(context, androidConfiguration);
236+
}
166237
}
238+
167239
return notifier;
168240
}
169241

242+
private static void initAnrDetector(
243+
Context context,
244+
AndroidConfiguration androidConfiguration
245+
) {
246+
AnrDetector anrDetector = AnrDetectorFactory.create(
247+
context,
248+
LoggerFactory.getLogger(AnrDetectorFactory.class),
249+
androidConfiguration.getAnrConfiguration(),
250+
error -> reportANR(error));
251+
if (anrDetector != null) {
252+
anrDetector.init();
253+
}
254+
}
255+
170256
private void updateContext(Context context) {
171257
if (this.senderFailureStrategy != null) {
172258
this.senderFailureStrategy.updateContext(context);
@@ -836,6 +922,24 @@ public void log(Throwable error, Map<String, Object> custom, Level level) {
836922
log(error, custom, null, level);
837923
}
838924

925+
/**
926+
* Record an error or message with extra data at the level specified. At least one of `error` or
927+
* `description` must be non-null. If error is null, `description` will be sent as a message. If
928+
* error is non-null, description will be sent as the description of the error. Custom data will
929+
* be attached to message if the error is null. Custom data will extend whatever {@link
930+
* Config#custom} returns.
931+
*
932+
* @param error the error (if any).
933+
* @param custom the custom data (if any).
934+
* @param description the description of the error, or the message to send.
935+
* @param level the level to send it at.
936+
* @param isUncaught whether this data comes from an uncaught exception.
937+
*/
938+
public void log(ThrowableWrapper error, Map<String, Object> custom, String description,
939+
Level level, boolean isUncaught) {
940+
rollbar.log(error, custom, description, level, isUncaught);
941+
}
942+
839943
/**
840944
* Log an error at level specified.
841945
*
@@ -1086,4 +1190,22 @@ private static void ensureInit(Runnable runnable) {
10861190
}
10871191
}
10881192

1193+
private static void reportANR(AnrException error){
1194+
List<RollbarThread> rollbarThreads = error.getThreads();
1195+
1196+
ThrowableWrapper throwableWrapper;
1197+
1198+
if (rollbarThreads == null) {
1199+
throwableWrapper = new RollbarThrowableWrapper(error);
1200+
} else {
1201+
throwableWrapper = new RollbarThrowableWrapper(error, rollbarThreads);
1202+
}
1203+
1204+
notifier.log(throwableWrapper, new HashMap<>(), "ANR", Level.CRITICAL, false);
1205+
}
1206+
1207+
private static AndroidConfiguration makeDefaultAndroidConfiguration() {
1208+
return new AndroidConfiguration.Builder().build();
1209+
}
1210+
10891211
}

0 commit comments

Comments
 (0)