Browse Source

Merge pull request #8805 from nextcloud/stacktrace

Add stacktrace directly to github issue
Álvaro Brey 3 years ago
parent
commit
5b964994d1

+ 52 - 50
src/main/java/com/nextcloud/client/errorhandling/ExceptionHandler.kt

@@ -30,8 +30,6 @@ import android.content.Intent
 import android.os.Build
 import com.owncloud.android.BuildConfig
 import com.owncloud.android.R
-import java.io.PrintWriter
-import java.io.StringWriter
 
 class ExceptionHandler(
     private val context: Context,
@@ -40,15 +38,14 @@ class ExceptionHandler(
 
     companion object {
         private const val LINE_SEPARATOR = "\n"
+        private const val EXCEPTION_FORMAT_MAX_RECURSIVITY = 10
     }
 
     override fun uncaughtException(thread: Thread, exception: Throwable) {
 
         @Suppress("TooGenericExceptionCaught") // this is exactly what we want here
         try {
-            val stackTrace = StringWriter()
-            exception.printStackTrace(PrintWriter(stackTrace))
-            val errorReport = generateErrorReport(stackTrace.toString())
+            val errorReport = generateErrorReport(formatException(thread, exception))
             val intent = Intent(context, ShowErrorActivity::class.java)
             intent.putExtra(ShowErrorActivity.EXTRA_ERROR_TEXT, errorReport)
             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 {
         val buildNumber = context.resources.getString(R.string.buildNumber)
 
-        var buildNumberString = ""
-        if (buildNumber.isNotEmpty()) {
-            buildNumberString = " (build #$buildNumber)"
+        val buildNumberString = when {
+            buildNumber.isNotEmpty() -> " (build #$buildNumber)"
+            else -> ""
         }
 
-        return "************ CAUSE OF ERROR ************\n\n" +
-            stackTrace +
-            "\n************ APP INFORMATION ************" +
-            LINE_SEPARATOR +
-            "ID: " +
-            BuildConfig.APPLICATION_ID +
-            LINE_SEPARATOR +
-            "Version: " +
-            BuildConfig.VERSION_CODE +
-            buildNumberString +
-            LINE_SEPARATOR +
-            "Build flavor: " +
-            BuildConfig.FLAVOR +
-            LINE_SEPARATOR +
-            "\n************ DEVICE INFORMATION ************" +
-            LINE_SEPARATOR +
-            "Brand: " +
-            Build.BRAND +
-            LINE_SEPARATOR +
-            "Device: " +
-            Build.DEVICE +
-            LINE_SEPARATOR +
-            "Model: " +
-            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
+        return """
+            |### Cause of error
+            |```java
+            ${stackTrace.prependIndent("|")}
+            |```
+            |
+            |### App information
+            |* ID: `${BuildConfig.APPLICATION_ID}`
+            |* Version: `${BuildConfig.VERSION_CODE}$buildNumberString`
+            |* Build flavor: `${BuildConfig.FLAVOR}`
+            |
+            |### Device information
+            |* Brand: `${Build.BRAND}`
+            |* Device: `${Build.DEVICE}`
+            |* Model: `${Build.MODEL}`
+            |* Id: `${Build.ID}`
+            |* Product: `${Build.PRODUCT}`
+            |
+            |### Firmware
+            |* SDK: `${Build.VERSION.SDK_INT}`
+            |* Release: `${Build.VERSION.RELEASE}`
+            |* Incremental: `${Build.VERSION.INCREMENTAL}`
+        """.trimMargin("|")
     }
 }

+ 10 - 2
src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt

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

+ 1 - 2
src/main/java/com/owncloud/android/MainApp.java

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

+ 4 - 4
src/main/java/com/owncloud/android/utils/StringUtils.java

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

+ 1 - 1
src/main/res/values/setup.xml

@@ -97,7 +97,7 @@
     <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="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 -->
     <string name="login_data_own_scheme" translatable="false">nc</string>