diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/RageshakeConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/RageshakeConfig.kt index 38dcd67c9e..6dd2143d95 100644 --- a/appconfig/src/main/kotlin/io/element/android/appconfig/RageshakeConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/RageshakeConfig.kt @@ -30,4 +30,9 @@ object RageshakeConfig { * The maximum size of a single log file. */ const val MAX_LOG_CONTENT_SIZE = 100 * 1024 * 1024L + + /** + * The maximum number of log lines a rageshake can contain. + */ + const val MAX_LOG_LINES_SIZE = 1_000_000 } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index 109afe1df7..b4e7faa01e 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -135,6 +135,10 @@ class DefaultBugReporter( // enumerate files to delete val bugReportFiles: MutableList = ArrayList() var response: Response? = null + + // Start at something like 1000 lines to have some 'buffer' in case unexpected lines were added + var totalLogLines = 1000L + try { var serverError: String? = null withContext(coroutineDispatchers.io) { @@ -147,23 +151,12 @@ class DefaultBugReporter( } } val gzippedFiles = mutableListOf() - var filesTooBig = 0 - if (withDevicesLogs) { - val files = getLogFiles().sortedByDescending { it.lastModified() } - val filesBySize = files.groupBy { - it.length() < RageshakeConfig.MAX_LOG_CONTENT_SIZE - } - filesBySize[true].orEmpty().mapNotNullTo(gzippedFiles) { file -> - when { - file.extension == "gz" -> file - else -> compressFile(file) - } - } - filesTooBig = filesBySize[false].orEmpty().size - } + var filesTooBig = emptyList() + if (withCrashLogs || withDevicesLogs) { saveLogCat() ?.takeIf { it.length() < RageshakeConfig.MAX_LOG_CONTENT_SIZE } + ?.takeIf { countLogLines(it) + totalLogLines < RageshakeConfig.MAX_LOG_LINES_SIZE } ?.let { logCatFile -> compressFile(logCatFile).also { logCatFile.safeDelete() @@ -197,9 +190,7 @@ class DefaultBugReporter( .addFormDataPart("label", buildMeta.versionName) .addFormDataPart("label", buildMeta.flavorDescription) .addFormDataPart("branch_name", buildMeta.gitBranchName) - if (filesTooBig > 0) { - builder.addFormDataPart("omitted_logs", filesTooBig.toString()) - } + userId?.let { matrixClientProvider.getOrNull(it)?.let { client -> val curveKey = client.encryptionService.deviceCurve25519() @@ -210,15 +201,57 @@ class DefaultBugReporter( if (sendPushRules) { client.notificationSettingsService.getRawPushRules().getOrNull()?.let { pushRules -> - builder.addFormDataPart( - name = "file", - filename = "push_rules.json", - body = pushRules.toByteArray().toRequestBody(MimeTypes.Json.toMediaTypeOrNull()) - ) + val logLines = pushRules.lineSequence().count() + + if (totalLogLines + logLines < RageshakeConfig.MAX_LOG_LINES_SIZE) { + builder.addFormDataPart( + name = "file", + filename = "push_rules.json", + body = pushRules.toByteArray().toRequestBody(MimeTypes.Json.toMediaTypeOrNull()) + ) + } else { + Timber.w("Could not upload push rules because it would exceed the max log lines size") + } } } } } + + if (withDevicesLogs) { + val files = getLogFiles().sortedByDescending { it.lastModified() } + val filesBySize = files.groupBy { + it.length() < RageshakeConfig.MAX_LOG_CONTENT_SIZE + }.toMutableMap() + + filesBySize[true].orEmpty().mapNotNullTo(gzippedFiles) { file -> + val logLines = countLogLines(file) + totalLogLines += logLines + + when { + totalLogLines > RageshakeConfig.MAX_LOG_LINES_SIZE -> { + // Add it to the list of omitted files too + (filesBySize.getOrPut(false) { mutableListOf() } as MutableList).add(file) + + Timber.e( + "Could not upload file ${file.name} because it would exceed the max log lines size " + + "($totalLogLines/${RageshakeConfig.MAX_LOG_LINES_SIZE}" + ) + + totalLogLines -= logLines + + null + } + file.extension == "gz" -> file + else -> compressFile(file) + } + } + filesTooBig = filesBySize[false].orEmpty().map { it.name } + } + + if (filesTooBig.isNotEmpty()) { + builder.addFormDataPart("omitted_logs", filesTooBig.toString()) + } + if (crashCallStack.isNotEmpty() && withCrashLogs) { builder.addFormDataPart("label", "crash") } @@ -453,4 +486,8 @@ class DefaultBugReporter( Timber.e(e, "getLogCatContent fails") } } + + private fun countLogLines(file: File): Int { + return file.reader().useLines { it.count() } + } } diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt index 41e136a53e..c503e497f7 100755 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt @@ -536,6 +536,6 @@ class DefaultBugReporterTest { } companion object { - private const val EXPECTED_NUMBER_OF_PROGRESS_VALUE = 18 + private const val EXPECTED_NUMBER_OF_PROGRESS_VALUE = 17 } }