Skip to content

[Feat][SDK-347] ANR report #323

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

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0161c77
feat: Update android min sdk from 16 to 21 and target/compile sdk fro…
christianbuon Dec 8, 2024
69df96d
feat: Add ANR detectors
christianbuon Jan 13, 2025
6ae33b7
feat: send non main threads as extra information for api >= 30
christianbuon Jan 26, 2025
bc3f27a
refactor: remove unnecessary code
christianbuon Jan 26, 2025
b8cb8f0
refactor: don't launch thread if listener is null
christianbuon Jan 26, 2025
957a0d8
refactor: remove unnecessary attribute for Line
christianbuon Jan 26, 2025
91ab622
refactor: do not create watchdog or run it if anr listener is null
christianbuon Feb 2, 2025
a0082ff
test: add Tests for Watchdog
christianbuon Feb 2, 2025
c69563e
test: remove unused imports
christianbuon Feb 2, 2025
c3ed238
test: set parameters as private
christianbuon Feb 10, 2025
dcac7ab
test: Add tests for HistoricalAnrDetector
christianbuon Feb 10, 2025
61dfa33
refactor: Initialize ThreadParser as expected
christianbuon Feb 10, 2025
6c0848e
feat: Add AndroidConfiguration to tun on/off ANR detectors implementa…
christianbuon Feb 16, 2025
64c5f4e
Merge branch 'master' into feat/SDK-347/anr-report
christianbuon May 18, 2025
da109a7
refactor: use RollbarThread to send ANR information
christianbuon May 26, 2025
c1a5df0
feat(HistoricalAnrDetector): save last anr timestamp
christianbuon May 28, 2025
a349bf3
build: downgrade compile and target sdk to 30, to test CI
christianbuon May 29, 2025
c8e8c77
test: delete tests to validate if CI completes
christianbuon May 29, 2025
1c23f8b
fix: lint
christianbuon May 29, 2025
5c94db7
fix(RollbarThrowableWrapper): lint
christianbuon May 29, 2025
978c831
fix(RollbarThrowableWrapper): lint
christianbuon May 29, 2025
7000260
chore: add new method information in revapi file
christianbuon May 29, 2025
073a470
build(android-example): migrate old libs to AndroidX
christianbuon May 29, 2025
257f738
build: update compile and target sdk to 32
christianbuon Jun 1, 2025
d044fa3
build: downgrade compile and target sdk to 31
christianbuon Jun 1, 2025
ceac2ac
Revert "test: delete tests to validate if CI completes"
christianbuon Jun 1, 2025
6feb4fc
refactor: set HistoricalAnrDetector File for Anr timestamps by constr…
christianbuon Jun 12, 2025
20e0876
build: downgrade compile and target sdk to 30
christianbuon Jun 12, 2025
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
5 changes: 5 additions & 0 deletions .palantir/revapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@ acceptedBreaks:
justification: "This is a binary compatible change, which could only break custom\
\ implementations of our config interfaces, but those interfaces are not meant\
\ to be implemented by users"
- code: "java.method.addedToInterface"
new: "method java.util.List<com.rollbar.api.payload.data.body.RollbarThread> com.rollbar.notifier.wrapper.ThrowableWrapper::getRollbarThreads()"
justification: "This is a binary compatible change, which could only break custom\
\ implementations of our config interfaces, but those interfaces are not meant\
\ to be implemented by users"
10 changes: 5 additions & 5 deletions examples/rollbar-android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ buildscript {
apply plugin: 'com.android.application'

android {
compileSdkVersion 27
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.rollbar.example.android"
minSdkVersion 16
minSdkVersion 21
// FIXME: Pending further discussion
//noinspection ExpiredTargetSdkVersion
targetSdkVersion 27
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Expand All @@ -38,9 +38,9 @@ dependencies {
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation 'com.android.support:design:27.1.1'
implementation 'com.google.android.material:material:1.0.0'
implementation 'org.slf4j:slf4j-android:1.7.25'
testImplementation group: 'junit', name: 'junit', version: '4.12'
}
3 changes: 2 additions & 1 deletion examples/rollbar-android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package com.rollbar.example.android;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.rollbar.android.Rollbar;

public class MainActivity extends AppCompatActivity {
Expand Down
50 changes: 25 additions & 25 deletions examples/rollbar-android/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.rollbar.example.android.MainActivity">

<com.google.android.material.appbar.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.rollbar.example.android.MainActivity">
android:theme="@style/AppTheme.AppBarOverlay">

<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>

</android.support.design.widget.AppBarLayout>
</com.google.android.material.appbar.AppBarLayout>

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

<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email"/>

</android.support.design.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
VERSION_NAME=2.0.0
GROUP=com.rollbar

android.useAndroidX=true

POM_DESCRIPTION=For connecting your applications built on the JVM to Rollbar for Error Reporting
POM_URL=https://github.com/rollbar/rollbar-java
POM_SCM_URL=https://github.com/rollbar/rollbar-java
Expand Down
6 changes: 3 additions & 3 deletions rollbar-android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ apply from: "$rootDir/gradle/release.gradle"
apply from: "$rootDir/gradle/android.quality.gradle"

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

defaultConfig {
minSdkVersion 16
minSdkVersion 21
// FIXME: Pending further discussion
//noinspection ExpiredTargetSdkVersion
targetSdkVersion 27
targetSdkVersion 30
consumerProguardFiles 'proguard-rules.pro'
manifestPlaceholders = [notifierVersion: VERSION_NAME]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.rollbar.android;

import com.rollbar.android.anr.AnrConfiguration;

public class AndroidConfiguration {
private final AnrConfiguration anrConfiguration;

AndroidConfiguration(Builder builder) {
anrConfiguration = builder.anrConfiguration;
}

public AnrConfiguration getAnrConfiguration() {
return anrConfiguration;
}


public static final class Builder {
private AnrConfiguration anrConfiguration;

Builder() {
anrConfiguration = new AnrConfiguration.Builder().build();
}

/**
* The ANR configuration, if this field is null, no ANR would be captured
* @param anrConfiguration the ANR configuration
* @return the builder instance
*/
public Builder setAnrConfiguration(AnrConfiguration anrConfiguration) {
this.anrConfiguration = anrConfiguration;
return this;
}

public AndroidConfiguration build() {
return new AndroidConfiguration(this);
}
}
}
126 changes: 124 additions & 2 deletions rollbar-android/src/main/java/com/rollbar/android/Rollbar.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@
import android.os.Bundle;

import android.util.Log;

import com.rollbar.android.anr.AnrDetector;
import com.rollbar.android.anr.AnrDetectorFactory;
import com.rollbar.android.anr.AnrException;
import com.rollbar.android.notifier.sender.ConnectionAwareSenderFailureStrategy;
import com.rollbar.android.provider.ClientProvider;
import com.rollbar.api.payload.data.TelemetryType;
import com.rollbar.api.payload.data.body.RollbarThread;
import com.rollbar.notifier.config.ConfigProvider;
import com.rollbar.notifier.uncaughtexception.RollbarUncaughtExceptionHandler;
import com.rollbar.android.provider.NotifierProvider;
Expand All @@ -24,12 +29,18 @@
import com.rollbar.notifier.sender.SyncSender;
import com.rollbar.notifier.sender.queue.DiskQueue;
import com.rollbar.notifier.util.ObjectsUtils;
import com.rollbar.notifier.wrapper.RollbarThrowableWrapper;
import com.rollbar.notifier.wrapper.ThrowableWrapper;

import org.slf4j.LoggerFactory;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

Expand Down Expand Up @@ -71,6 +82,28 @@ public static Rollbar init(Context context) {
return init(context, null, null);
}

/**
* Initialize the singleton instance of Rollbar.
* Defaults to reading the access token from the manifest, handling uncaught exceptions, and setting
* the environment to production.
*
* @param context Android context to use.
* @param androidConfiguration configuration for Android features.
* @return the managed instance of Rollbar.
*/
public static Rollbar init(Context context, AndroidConfiguration androidConfiguration) {
return init(
context,
null,
DEFAULT_ENVIRONMENT,
DEFAULT_REGISTER_EXCEPTION_HANDLER,
DEFAULT_INCLUDE_LOGCAT,
DEFAULT_CONFIG_PROVIDER,
DEFAULT_SUSPEND_WHEN_NETWORK_IS_UNAVAILABLE,
androidConfiguration
);
}

/**
* Initialize the singleton instance of Rollbar.
*
Expand Down Expand Up @@ -155,18 +188,71 @@ public static Rollbar init(Context context, String accessToken, String environme
public static Rollbar init(Context context, String accessToken, String environment,
boolean registerExceptionHandler, boolean includeLogcat,
ConfigProvider provider, boolean suspendWhenNetworkIsUnavailable) {
return init(
context,
accessToken,
environment,
registerExceptionHandler,
includeLogcat,
provider,
suspendWhenNetworkIsUnavailable,
makeDefaultAndroidConfiguration()
);
}

/**
* Initialize the singleton instance of Rollbar.
*
* @param context Android context to use.
* @param accessToken a Rollbar access token with at least post_client_item scope
* @param environment the environment to set for items
* @param registerExceptionHandler whether or not to handle uncaught exceptions.
* @param includeLogcat whether or not to include logcat output with items
* @param provider a configuration provider that can be used to customize the configuration further.
* @param suspendWhenNetworkIsUnavailable if true, sending occurrences will be suspended while the network is unavailable
* @param androidConfiguration configuration for Android features
* @return the managed instance of Rollbar.
*/
public static Rollbar init(
Context context,
String accessToken,
String environment,
boolean registerExceptionHandler,
boolean includeLogcat,
ConfigProvider provider,
boolean suspendWhenNetworkIsUnavailable,
AndroidConfiguration androidConfiguration
) {
if (isInit()) {
Log.w(TAG, "Rollbar.init() called when it was already initialized.");
// This is likely an activity that was destroyed and recreated, so we need to update it
notifier.updateContext(context);
} else {
notifier = new Rollbar(context, accessToken, environment, registerExceptionHandler,
includeLogcat, provider, DEFAULT_CAPTURE_IP, DEFAULT_MAX_LOGCAT_SIZE,
suspendWhenNetworkIsUnavailable);
includeLogcat, provider, DEFAULT_CAPTURE_IP, DEFAULT_MAX_LOGCAT_SIZE,
suspendWhenNetworkIsUnavailable);
if (androidConfiguration != null) {
initAnrDetector(context, androidConfiguration);
}
}

return notifier;
}

private static void initAnrDetector(
Context context,
AndroidConfiguration androidConfiguration
) {
AnrDetector anrDetector = AnrDetectorFactory.create(
context,
LoggerFactory.getLogger(AnrDetectorFactory.class),
androidConfiguration.getAnrConfiguration(),
error -> reportANR(error));
if (anrDetector != null) {
anrDetector.init();
}
}

private void updateContext(Context context) {
if (this.senderFailureStrategy != null) {
this.senderFailureStrategy.updateContext(context);
Expand Down Expand Up @@ -836,6 +922,24 @@ public void log(Throwable error, Map<String, Object> custom, Level level) {
log(error, custom, null, level);
}

/**
* Record an error or message with extra data at the level specified. At least one of `error` or
* `description` must be non-null. If error is null, `description` will be sent as a message. If
* error is non-null, description will be sent as the description of the error. Custom data will
* be attached to message if the error is null. Custom data will extend whatever {@link
* Config#custom} returns.
*
* @param error the error (if any).
* @param custom the custom data (if any).
* @param description the description of the error, or the message to send.
* @param level the level to send it at.
* @param isUncaught whether this data comes from an uncaught exception.
*/
public void log(ThrowableWrapper error, Map<String, Object> custom, String description,
Level level, boolean isUncaught) {
rollbar.log(error, custom, description, level, isUncaught);
}

/**
* Log an error at level specified.
*
Expand Down Expand Up @@ -1086,4 +1190,22 @@ private static void ensureInit(Runnable runnable) {
}
}

private static void reportANR(AnrException error){
List<RollbarThread> rollbarThreads = error.getThreads();

ThrowableWrapper throwableWrapper;

if (rollbarThreads == null) {
throwableWrapper = new RollbarThrowableWrapper(error);
} else {
throwableWrapper = new RollbarThrowableWrapper(error, rollbarThreads);
}

notifier.log(throwableWrapper, new HashMap<>(), "ANR", Level.CRITICAL, false);
}

private static AndroidConfiguration makeDefaultAndroidConfiguration() {
return new AndroidConfiguration.Builder().build();
}

}
Loading