Merge pull request #8805 from nextcloud/stacktrace

Add stacktrace directly to github issue
This commit is contained in:
Álvaro Brey 2022-02-24 13:38:04 +01:00 committed by GitHub
commit 5b964994d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 59 deletions

View file

@ -30,8 +30,6 @@ import android.content.Intent
import android.os.Build import android.os.Build
import com.owncloud.android.BuildConfig import com.owncloud.android.BuildConfig
import com.owncloud.android.R import com.owncloud.android.R
import java.io.PrintWriter
import java.io.StringWriter
class ExceptionHandler( class ExceptionHandler(
private val context: Context, private val context: Context,
@ -40,15 +38,14 @@ class ExceptionHandler(
companion object { companion object {
private const val LINE_SEPARATOR = "\n" private const val LINE_SEPARATOR = "\n"
private const val EXCEPTION_FORMAT_MAX_RECURSIVITY = 10
} }
override fun uncaughtException(thread: Thread, exception: Throwable) { override fun uncaughtException(thread: Thread, exception: Throwable) {
@Suppress("TooGenericExceptionCaught") // this is exactly what we want here @Suppress("TooGenericExceptionCaught") // this is exactly what we want here
try { try {
val stackTrace = StringWriter() val errorReport = generateErrorReport(formatException(thread, exception))
exception.printStackTrace(PrintWriter(stackTrace))
val errorReport = generateErrorReport(stackTrace.toString())
val intent = Intent(context, ShowErrorActivity::class.java) val intent = Intent(context, ShowErrorActivity::class.java)
intent.putExtra(ShowErrorActivity.EXTRA_ERROR_TEXT, errorReport) intent.putExtra(ShowErrorActivity.EXTRA_ERROR_TEXT, errorReport)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
@ -63,55 +60,60 @@ class ExceptionHandler(
} }
} }
private fun formatException(thread: Thread, exception: Throwable): String {
fun formatExceptionRecursive(thread: Thread, exception: Throwable, count: Int = 0): String {
if (count > EXCEPTION_FORMAT_MAX_RECURSIVITY) {
return "Max number of recursive exception causes exceeded!"
}
// print exception
val stringBuilder = StringBuilder()
val stackTrace = exception.stackTrace
stringBuilder.appendLine("Exception in thread \"${thread.name}\" $exception")
// print available stacktrace
for (element in stackTrace) {
stringBuilder.appendLine(" at $element")
}
// print cause recursively
exception.cause?.let {
stringBuilder.append("Caused by: ")
stringBuilder.append(formatExceptionRecursive(thread, it, count + 1))
}
return stringBuilder.toString()
}
return formatExceptionRecursive(thread, exception, 0)
}
private fun generateErrorReport(stackTrace: String): String { private fun generateErrorReport(stackTrace: String): String {
val buildNumber = context.resources.getString(R.string.buildNumber) val buildNumber = context.resources.getString(R.string.buildNumber)
var buildNumberString = "" val buildNumberString = when {
if (buildNumber.isNotEmpty()) { buildNumber.isNotEmpty() -> " (build #$buildNumber)"
buildNumberString = " (build #$buildNumber)" else -> ""
} }
return "************ CAUSE OF ERROR ************\n\n" + return """
stackTrace + |### Cause of error
"\n************ APP INFORMATION ************" + |```java
LINE_SEPARATOR + ${stackTrace.prependIndent("|")}
"ID: " + |```
BuildConfig.APPLICATION_ID + |
LINE_SEPARATOR + |### App information
"Version: " + |* ID: `${BuildConfig.APPLICATION_ID}`
BuildConfig.VERSION_CODE + |* Version: `${BuildConfig.VERSION_CODE}$buildNumberString`
buildNumberString + |* Build flavor: `${BuildConfig.FLAVOR}`
LINE_SEPARATOR + |
"Build flavor: " + |### Device information
BuildConfig.FLAVOR + |* Brand: `${Build.BRAND}`
LINE_SEPARATOR + |* Device: `${Build.DEVICE}`
"\n************ DEVICE INFORMATION ************" + |* Model: `${Build.MODEL}`
LINE_SEPARATOR + |* Id: `${Build.ID}`
"Brand: " + |* Product: `${Build.PRODUCT}`
Build.BRAND + |
LINE_SEPARATOR + |### Firmware
"Device: " + |* SDK: `${Build.VERSION.SDK_INT}`
Build.DEVICE + |* Release: `${Build.VERSION.RELEASE}`
LINE_SEPARATOR + |* Incremental: `${Build.VERSION.INCREMENTAL}`
"Model: " + """.trimMargin("|")
Build.MODEL +
LINE_SEPARATOR +
"Id: " +
Build.ID +
LINE_SEPARATOR +
"Product: " +
Build.PRODUCT +
LINE_SEPARATOR +
"\n************ FIRMWARE ************" +
LINE_SEPARATOR +
"SDK: " +
Build.VERSION.SDK_INT +
LINE_SEPARATOR +
"Release: " +
Build.VERSION.RELEASE +
LINE_SEPARATOR +
"Incremental: " +
Build.VERSION.INCREMENTAL +
LINE_SEPARATOR
} }
} }

View file

@ -31,6 +31,7 @@ import com.owncloud.android.R
import com.owncloud.android.databinding.ActivityShowErrorBinding import com.owncloud.android.databinding.ActivityShowErrorBinding
import com.owncloud.android.utils.ClipboardUtil import com.owncloud.android.utils.ClipboardUtil
import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.DisplayUtils
import java.net.URLEncoder
class ShowErrorActivity : AppCompatActivity() { class ShowErrorActivity : AppCompatActivity() {
private lateinit var binding: ActivityShowErrorBinding private lateinit var binding: ActivityShowErrorBinding
@ -66,7 +67,12 @@ class ShowErrorActivity : AppCompatActivity() {
ClipboardUtil.copyToClipboard(this, binding.textViewError.text.toString(), false) ClipboardUtil.copyToClipboard(this, binding.textViewError.text.toString(), false)
val issueLink = getString(R.string.report_issue_link) val issueLink = getString(R.string.report_issue_link)
if (issueLink.isNotEmpty()) { if (issueLink.isNotEmpty()) {
val uriUrl = Uri.parse(issueLink) val uriUrl = Uri.parse(
String.format(
issueLink,
URLEncoder.encode(binding.textViewError.text.toString())
)
)
val intent = Intent(Intent.ACTION_VIEW, uriUrl) val intent = Intent(Intent.ACTION_VIEW, uriUrl)
DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available) DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available)
} }
@ -80,7 +86,9 @@ class ShowErrorActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.error_share -> { onClickedShare(); true } R.id.error_share -> {
onClickedShare(); true
}
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
} }
} }

View file

@ -231,9 +231,8 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector {
// we don't want to handle crashes occurring inside crash reporter activity/process; // we don't want to handle crashes occurring inside crash reporter activity/process;
// let the platform deal with those // let the platform deal with those
final boolean isCrashReportingProcess = getAppProcessName().endsWith(":crash"); final boolean isCrashReportingProcess = getAppProcessName().endsWith(":crash");
final boolean useExceptionHandler = !appInfo.isDebugBuild();
if (!isCrashReportingProcess && useExceptionHandler) { if (!isCrashReportingProcess) {
Thread.UncaughtExceptionHandler defaultPlatformHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.UncaughtExceptionHandler defaultPlatformHandler = Thread.getDefaultUncaughtExceptionHandler();
final ExceptionHandler crashReporter = new ExceptionHandler(this, final ExceptionHandler crashReporter = new ExceptionHandler(this,
defaultPlatformHandler); defaultPlatformHandler);

View file

@ -58,7 +58,7 @@ public final class StringUtils {
matcher.group(), matcher.group(),
String.format(Locale.getDefault(), "<font color='%d'><b>%s</b></font>", color, String.format(Locale.getDefault(), "<font color='%d'><b>%s</b></font>", color,
matcher.group()) matcher.group())
); );
matcher.appendReplacement(stringBuffer, Matcher.quoteReplacement(replacement)); matcher.appendReplacement(stringBuffer, Matcher.quoteReplacement(replacement));
} }
matcher.appendTail(stringBuffer); matcher.appendTail(stringBuffer);
@ -70,9 +70,9 @@ public final class StringUtils {
} }
public static public static
@NonNull String removePrefix(@NonNull String s, @NonNull String prefix) @NonNull
{ String removePrefix(@NonNull String s, @NonNull String prefix) {
if (s.startsWith(prefix)){ if (s.startsWith(prefix)) {
return s.substring(prefix.length()); return s.substring(prefix.length());
} }
return s; return s;

View file

@ -97,7 +97,7 @@
<string name="help_link" translatable="false">https://help.nextcloud.com/c/clients/android</string> <string name="help_link" translatable="false">https://help.nextcloud.com/c/clients/android</string>
<string name="translation_link" translatable="false">https://www.transifex.com/nextcloud/nextcloud/android/</string> <string name="translation_link" translatable="false">https://www.transifex.com/nextcloud/nextcloud/android/</string>
<string name="contributing_link" translatable="false">https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md</string> <string name="contributing_link" translatable="false">https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md</string>
<string name="report_issue_link" translatable="false">https://github.com/nextcloud/android/issues/new/choose</string> <string name="report_issue_link" translatable="false">https://github.com/nextcloud/android/issues/new?labels=bug&amp;body=%1$s</string>
<!-- login data links --> <!-- login data links -->
<string name="login_data_own_scheme" translatable="false">nc</string> <string name="login_data_own_scheme" translatable="false">nc</string>