mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-10-31 08:15:02 +00:00
Fix bug reports failing to send after the app has crashed
The whole crash call stack was appended to the bug report description, and the bug report server copies that description verbatim into the GitHub issue it opens. GitHub refuses an issue body over 65536 characters, so the server's save step failed and answered "Could not save report". The stored crash data is only cleared after a successful send, so every retry failed the same way. Truncate the description before sending it. The user's own text and the GitHub issue link come first, so only the tail of the crash stack is dropped. The uncaught exception handler already writes the full stack through Timber, so it stays available in the attached log files. The 40 KB budget leaves room for the other reported fields, mainly the list of omitted log file names, which is not otherwise bounded.
This commit is contained in:
@@ -35,4 +35,11 @@ object RageshakeConfig {
|
||||
* The maximum number of log lines a rageshake can contain.
|
||||
*/
|
||||
const val MAX_LOG_LINES_SIZE = 1_000_000
|
||||
|
||||
/**
|
||||
* The maximum size of the description. The bug report server copies it verbatim into a GitHub issue body,
|
||||
* which GitHub rejects above 65536 characters. The remaining budget is left to the other reported fields,
|
||||
* mainly the list of the omitted log file names.
|
||||
*/
|
||||
const val MAX_DESCRIPTION_SIZE = 40 * 1024
|
||||
}
|
||||
|
||||
+10
-1
@@ -152,7 +152,7 @@ class DefaultBugReporter(
|
||||
append("\n\n\n\n--------------------------------- crash call stack ---------------------------------\n")
|
||||
append(crashCallStack)
|
||||
}
|
||||
}
|
||||
}.truncateDescription()
|
||||
val gzippedFiles = mutableListOf<File>()
|
||||
var filesTooBig = emptyList<String>()
|
||||
|
||||
@@ -494,4 +494,13 @@ class DefaultBugReporter(
|
||||
private fun countLogLines(file: File): Int {
|
||||
return file.reader().useLines { it.count() }
|
||||
}
|
||||
|
||||
private fun String.truncateDescription(): String {
|
||||
return if (length <= RageshakeConfig.MAX_DESCRIPTION_SIZE) {
|
||||
this
|
||||
} else {
|
||||
take(RageshakeConfig.MAX_DESCRIPTION_SIZE) +
|
||||
"\n\n--------------------------------- truncated, see the attached logs ---------------------------------"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+64
@@ -309,6 +309,68 @@ class DefaultBugReporterTest : RobolectricTest() {
|
||||
assertThat(foundValues["label"]).isEqualTo("crash")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test sendBugReport truncates a description which is too long`() = runTest {
|
||||
val server = MockWebServer()
|
||||
server.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(200)
|
||||
)
|
||||
server.start()
|
||||
val hugeCrashCallStack = "a".repeat(RageshakeConfig.MAX_DESCRIPTION_SIZE * 2)
|
||||
val sut = createDefaultBugReporter(
|
||||
server = server,
|
||||
crashDataStore = FakeCrashDataStore(hugeCrashCallStack, true),
|
||||
)
|
||||
|
||||
sut.sendBugReport(
|
||||
withDevicesLogs = true,
|
||||
withCrashLogs = true,
|
||||
withScreenshot = true,
|
||||
problemDescription = "a bug occurred",
|
||||
canContact = true,
|
||||
listener = NoopBugReporterListener(),
|
||||
)
|
||||
val request = server.takeRequest()
|
||||
|
||||
val text = collectValuesFromFormData(request)["text"]!!
|
||||
assertThat(text).startsWith("a bug occurred")
|
||||
assertThat(text).contains("truncated")
|
||||
assertThat(text.length).isAtMost(RageshakeConfig.MAX_DESCRIPTION_SIZE + TRUNCATION_MARKER_MAX_SIZE)
|
||||
assertThat(text.length).isLessThan(GITHUB_MAX_ISSUE_BODY_SIZE)
|
||||
server.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test sendBugReport does not truncate a description which fits`() = runTest {
|
||||
val server = MockWebServer()
|
||||
server.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(200)
|
||||
)
|
||||
server.start()
|
||||
val sut = createDefaultBugReporter(
|
||||
server = server,
|
||||
crashDataStore = FakeCrashDataStore("I did crash", true),
|
||||
)
|
||||
|
||||
sut.sendBugReport(
|
||||
withDevicesLogs = true,
|
||||
withCrashLogs = true,
|
||||
withScreenshot = true,
|
||||
problemDescription = "a bug occurred",
|
||||
canContact = true,
|
||||
listener = NoopBugReporterListener(),
|
||||
)
|
||||
val request = server.takeRequest()
|
||||
|
||||
val text = collectValuesFromFormData(request)["text"]!!
|
||||
assertThat(text).startsWith("a bug occurred")
|
||||
assertThat(text).endsWith("I did crash")
|
||||
assertThat(text).doesNotContain("truncated")
|
||||
server.shutdown()
|
||||
}
|
||||
|
||||
private fun collectValuesFromFormData(request: RecordedRequest): HashMap<String, String> {
|
||||
val boundary = request.headers["Content-Type"]!!.split("=").last()
|
||||
val foundValues = HashMap<String, String>()
|
||||
@@ -535,5 +597,7 @@ class DefaultBugReporterTest : RobolectricTest() {
|
||||
|
||||
companion object {
|
||||
private const val EXPECTED_NUMBER_OF_PROGRESS_VALUE = 18
|
||||
private const val GITHUB_MAX_ISSUE_BODY_SIZE = 65536
|
||||
private const val TRUNCATION_MARKER_MAX_SIZE = 200
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user