Feature/fga/rust sdk tracing (#1036)

* Align TracingConfiguration with iOS

* Create TracingTree from rust sdk

* tracing: create a working configuration with RustTracingTree

* Tracing: WIP implementation of new api

* Tracing: clean up

* Tracing: use the latest api

* Tracing: some more clean up

* Remove generated logcat file after compressing it

---------

Co-authored-by: ganfra <francoisg@element.io>
Co-authored-by: Jorge Martín <jorgem@element.io>
This commit is contained in:
ganfra 2023-08-09 12:18:49 +02:00 committed by GitHub
parent f15817447c
commit 3f1d241b48
19 changed files with 455 additions and 162 deletions

View file

@ -16,6 +16,8 @@
package io.element.android.features.rageshake.api.reporter
import java.io.File
interface BugReporter {
/**
* Send a bug report.
@ -43,4 +45,14 @@ interface BugReporter {
customFields: Map<String, String>? = null,
listener: BugReporterListener?
)
/**
* Clean the log files if needed to avoid wasting disk space.
*/
fun cleanLogDirectoryIfNeeded()
/**
* Provide the log directory.
*/
fun logDirectory(): File
}

View file

@ -32,6 +32,7 @@ anvil {
dependencies {
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
implementation(projects.services.toolbox.api)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.network)

View file

@ -18,6 +18,7 @@ package io.element.android.features.rageshake.impl.reporter
import android.content.Context
import android.os.Build
import android.text.format.DateUtils.DAY_IN_MILLIS
import androidx.core.net.toFile
import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding
@ -27,10 +28,10 @@ import io.element.android.features.rageshake.api.reporter.BugReporterListener
import io.element.android.features.rageshake.api.reporter.ReportType
import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder
import io.element.android.features.rageshake.impl.R
import io.element.android.features.rageshake.impl.logs.VectorFileLogger
import io.element.android.libraries.androidutils.file.compressFile
import io.element.android.libraries.androidutils.file.safeDelete
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.core.extensions.toOnOff
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.mimetype.MimeTypes
@ -38,7 +39,10 @@ import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.Call
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@ -65,6 +69,8 @@ class DefaultBugReporter @Inject constructor(
@ApplicationContext private val context: Context,
private val screenshotHolder: ScreenshotHolder,
private val crashDataStore: CrashDataStore,
private val coroutineScope: CoroutineScope,
private val systemClock: SystemClock,
private val coroutineDispatchers: CoroutineDispatchers,
private val okHttpClient: Provider<OkHttpClient>,
private val userAgentProvider: UserAgentProvider,
@ -87,6 +93,7 @@ class DefaultBugReporter @Inject constructor(
// filenames
private const val LOG_CAT_ERROR_FILENAME = "logcatError.log"
private const val LOG_CAT_FILENAME = "logcat.log"
private const val LOG_DIRECTORY_NAME = "logs"
// private const val KEY_REQUESTS_FILENAME = "keyRequests.log"
private const val BUFFER_SIZE = 1024 * 1024 * 50
@ -158,9 +165,8 @@ class DefaultBugReporter @Inject constructor(
val gzippedFiles = ArrayList<File>()
val vectorFileLogger = VectorFileLogger.getFromTimber()
if (withDevicesLogs && vectorFileLogger != null) {
val files = vectorFileLogger.getLogFiles()
if (withDevicesLogs) {
val files = getLogFiles()
files.mapNotNullTo(gzippedFiles) { f ->
if (!mIsCancelled) {
compressFile(f)
@ -168,6 +174,7 @@ class DefaultBugReporter @Inject constructor(
null
}
}
files.deleteAllExceptMostRecent()
}
if (!mIsCancelled && (withCrashLogs || withDevicesLogs)) {
@ -458,6 +465,54 @@ class DefaultBugReporter @Inject constructor(
)
}
override fun logDirectory(): File {
return File(context.cacheDir, LOG_DIRECTORY_NAME)
}
override fun cleanLogDirectoryIfNeeded() {
coroutineScope.launch(coroutineDispatchers.io) {
// delete the log files older than 1 day, except the most recent one
deleteOldLogFiles(systemClock.epochMillis() - DAY_IN_MILLIS)
}
}
/**
* @return the files on the log directory.
*/
private fun getLogFiles(): List<File> {
return tryOrNull(
onError = { Timber.e(it, "## getLogFiles() failed") }
) {
val logDirectory = logDirectory()
logDirectory.listFiles()?.toList()
}.orEmpty()
}
/**
* Delete the log files older than the given time except the most recent one.
* @param time the time in ms
*/
private fun deleteOldLogFiles(time: Long) {
val logFiles = getLogFiles()
val oldLogFiles = logFiles.filter { it.lastModified() < time }
oldLogFiles.deleteAllExceptMostRecent()
}
/**
* Delete all the log files except the most recent one.
*
*/
private fun List<File>.deleteAllExceptMostRecent() {
if (size > 1) {
val mostRecentFile = maxByOrNull { it.lastModified() }
forEach { file ->
if (file != mostRecentFile) {
file.safeDelete()
}
}
}
}
// ==============================================================================================================
// Logcat management
// ==============================================================================================================
@ -485,6 +540,10 @@ class DefaultBugReporter @Inject constructor(
Timber.e(error, "## saveLogCat() : fail to write logcat OOM")
} catch (e: Exception) {
Timber.e(e, "## saveLogCat() : fail to write logcat")
} finally {
if (logCatErrFile.exists()) {
logCatErrFile.safeDelete()
}
}
return null

View file

@ -21,6 +21,7 @@ import io.element.android.features.rageshake.api.reporter.BugReporterListener
import io.element.android.features.rageshake.api.reporter.ReportType
import io.element.android.libraries.matrix.test.A_FAILURE_REASON
import kotlinx.coroutines.delay
import java.io.File
class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Success) : BugReporter {
override suspend fun sendBugReport(
@ -55,6 +56,14 @@ class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Succes
delay(100)
listener?.onUploadSucceed(null)
}
override fun cleanLogDirectoryIfNeeded() {
// No op
}
override fun logDirectory(): File {
return File("fake")
}
}
enum class FakeBugReporterMode {