Store log files in subfolder based on the homeserver domain.
This commit is contained in:
@@ -10,11 +10,10 @@ package io.element.android.x.initializer
|
||||
import android.content.Context
|
||||
import android.system.Os
|
||||
import androidx.startup.Initializer
|
||||
import io.element.android.features.rageshake.api.reporter.BugReporter
|
||||
import io.element.android.features.rageshake.api.logs.createWriteToFilesConfiguration
|
||||
import io.element.android.libraries.architecture.bindings
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
|
||||
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
|
||||
import io.element.android.x.di.AppBindings
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -34,7 +33,7 @@ class PlatformInitializer : Initializer<Unit> {
|
||||
val logLevel = runBlocking { preferencesStore.getTracingLogLevelFlow().first() }
|
||||
val tracingConfiguration = TracingConfiguration(
|
||||
writesToLogcat = runBlocking { featureFlagService.isFeatureEnabled(FeatureFlags.PrintLogsToLogcat) },
|
||||
writesToFilesConfiguration = defaultWriteToDiskConfiguration(bugReporter),
|
||||
writesToFilesConfiguration = bugReporter.createWriteToFilesConfiguration(),
|
||||
logLevel = logLevel,
|
||||
extraTargets = listOf(ELEMENT_X_TARGET),
|
||||
traceLogPacks = runBlocking { preferencesStore.getTracingLogPacksFlow().first() },
|
||||
@@ -45,14 +44,5 @@ class PlatformInitializer : Initializer<Unit> {
|
||||
Os.setenv("RUST_BACKTRACE", "1", true)
|
||||
}
|
||||
|
||||
private fun defaultWriteToDiskConfiguration(bugReporter: BugReporter): WriteToFilesConfiguration.Enabled {
|
||||
return WriteToFilesConfiguration.Enabled(
|
||||
directory = bugReporter.logDirectory().absolutePath,
|
||||
filenamePrefix = "logs",
|
||||
// Keep a maximum of 1 week of log files.
|
||||
numberOfFiles = 7 * 24,
|
||||
)
|
||||
}
|
||||
|
||||
override fun dependencies(): List<Class<out Initializer<*>>> = mutableListOf()
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import io.element.android.appnav.root.RootView
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.features.login.api.LoginParams
|
||||
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
|
||||
import io.element.android.features.rageshake.api.reporter.BugReporter
|
||||
import io.element.android.features.signedout.api.SignedOutEntryPoint
|
||||
import io.element.android.features.viewfolder.api.ViewFolderEntryPoint
|
||||
import io.element.android.libraries.architecture.BackstackView
|
||||
@@ -73,6 +74,7 @@ class RootFlowNode @AssistedInject constructor(
|
||||
private val signedOutEntryPoint: SignedOutEntryPoint,
|
||||
private val intentResolver: IntentResolver,
|
||||
private val oidcActionFlow: OidcActionFlow,
|
||||
private val bugReporter: BugReporter,
|
||||
) : BaseFlowNode<RootFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
initialElement = NavTarget.SplashScreen,
|
||||
@@ -123,6 +125,7 @@ class RootFlowNode @AssistedInject constructor(
|
||||
|
||||
private fun switchToNotLoggedInFlow(params: LoginParams?) {
|
||||
matrixSessionCache.removeAll()
|
||||
bugReporter.setLogDirectorySubfolder(null)
|
||||
backstack.safeRoot(NavTarget.NotLoggedInFlow(params))
|
||||
}
|
||||
|
||||
|
||||
@@ -16,5 +16,6 @@ dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.androidutils)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.rageshake.api.logs
|
||||
|
||||
import io.element.android.features.rageshake.api.reporter.BugReporter
|
||||
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
|
||||
|
||||
fun BugReporter.createWriteToFilesConfiguration(): WriteToFilesConfiguration {
|
||||
return WriteToFilesConfiguration.Enabled(
|
||||
directory = logDirectory().absolutePath,
|
||||
filenamePrefix = "logs",
|
||||
// Keep a maximum of 1 week of log files.
|
||||
numberOfFiles = 7 * 24,
|
||||
)
|
||||
}
|
||||
@@ -34,6 +34,14 @@ interface BugReporter {
|
||||
*/
|
||||
fun logDirectory(): File
|
||||
|
||||
/**
|
||||
* Set the subfolder name for the log directory.
|
||||
* This will create a subfolder in the log directory with the given name.
|
||||
* It will also configure the Rust SDK to use this subfolder for its logs.
|
||||
* If the name is null, the log files will be stored in the base folder for the logs.
|
||||
*/
|
||||
fun setLogDirectorySubfolder(subfolderName: String?)
|
||||
|
||||
/**
|
||||
* Set the current tracing log level.
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.appconfig.RageshakeConfig
|
||||
import io.element.android.features.rageshake.api.logs.createWriteToFilesConfiguration
|
||||
import io.element.android.features.rageshake.api.reporter.BugReporter
|
||||
import io.element.android.features.rageshake.api.reporter.BugReporterListener
|
||||
import io.element.android.features.rageshake.impl.crash.CrashDataStore
|
||||
@@ -28,11 +29,14 @@ import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||
import io.element.android.libraries.matrix.api.SdkMetadata
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.tracing.TracingService
|
||||
import io.element.android.libraries.network.useragent.UserAgentProvider
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
@@ -71,6 +75,8 @@ class DefaultBugReporter @Inject constructor(
|
||||
private val bugReporterUrlProvider: BugReporterUrlProvider,
|
||||
private val sdkMetadata: SdkMetadata,
|
||||
private val matrixClientProvider: MatrixClientProvider,
|
||||
private val tracingService: TracingService,
|
||||
matrixAuthenticationService: MatrixAuthenticationService,
|
||||
) : BugReporter {
|
||||
companion object {
|
||||
// filenames
|
||||
@@ -81,7 +87,21 @@ class DefaultBugReporter @Inject constructor(
|
||||
private val logcatCommandDebug = arrayOf("logcat", "-d", "-v", "threadtime", "*:*")
|
||||
private var currentTracingLogLevel: String? = null
|
||||
|
||||
private val logCatErrFile = File(logDirectory().absolutePath, LOG_CAT_FILENAME)
|
||||
private val logCatErrFile: File
|
||||
get() = File(logDirectory(), LOG_CAT_FILENAME)
|
||||
private val baseLogDirectory = File(context.cacheDir, LOG_DIRECTORY_NAME)
|
||||
private var currentLogDirectory: File = baseLogDirectory
|
||||
|
||||
init {
|
||||
val logSubfolder = runBlocking {
|
||||
sessionStore.getLatestSession()
|
||||
}?.userId?.substringAfter(":")
|
||||
setCurrentLogDirectory(logSubfolder)
|
||||
matrixAuthenticationService.listenToNewMatrixClients {
|
||||
// When a new Matrix client is created, we update the tracing configuration to write to files
|
||||
setLogDirectorySubfolder(it.userIdServerName())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendBugReport(
|
||||
withDevicesLogs: Boolean,
|
||||
@@ -286,11 +306,24 @@ class DefaultBugReporter @Inject constructor(
|
||||
}
|
||||
|
||||
override fun logDirectory(): File {
|
||||
return File(context.cacheDir, LOG_DIRECTORY_NAME).apply {
|
||||
return currentLogDirectory.apply {
|
||||
mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setLogDirectorySubfolder(subfolderName: String?) {
|
||||
setCurrentLogDirectory(subfolderName)
|
||||
tracingService.updateWriteToFilesConfiguration(createWriteToFilesConfiguration())
|
||||
}
|
||||
|
||||
private fun setCurrentLogDirectory(subfolderName: String?) {
|
||||
currentLogDirectory = if (subfolderName == null) {
|
||||
baseLogDirectory
|
||||
} else {
|
||||
File(baseLogDirectory, subfolderName)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteAllFiles(predicate: (File) -> Boolean) {
|
||||
withContext(coroutineDispatchers.io) {
|
||||
getLogFiles()
|
||||
@@ -325,11 +358,12 @@ class DefaultBugReporter @Inject constructor(
|
||||
* @return the file if the operation succeeds
|
||||
*/
|
||||
override fun saveLogCat() {
|
||||
if (logCatErrFile.exists()) {
|
||||
logCatErrFile.safeDelete()
|
||||
val file = logCatErrFile
|
||||
if (file.exists()) {
|
||||
file.safeDelete()
|
||||
}
|
||||
try {
|
||||
logCatErrFile.writer().use {
|
||||
file.writer().use {
|
||||
getLogCatError(it)
|
||||
}
|
||||
} catch (error: OutOfMemoryError) {
|
||||
|
||||
@@ -11,4 +11,6 @@ import timber.log.Timber
|
||||
|
||||
interface TracingService {
|
||||
fun createTimberTree(target: String): Timber.Tree
|
||||
|
||||
fun updateWriteToFilesConfiguration(config: WriteToFilesConfiguration)
|
||||
}
|
||||
|
||||
@@ -71,9 +71,9 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||
private var currentClient: Client? = null
|
||||
private var currentHomeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
|
||||
|
||||
private var newMatrixClientObserver: ((MatrixClient) -> Unit)? = null
|
||||
private val newMatrixClientObservers = mutableListOf<(MatrixClient) -> Unit>()
|
||||
override fun listenToNewMatrixClients(lambda: (MatrixClient) -> Unit) {
|
||||
newMatrixClientObserver = lambda
|
||||
newMatrixClientObservers.add(lambda)
|
||||
}
|
||||
|
||||
private fun rotateSessionPath(): SessionPaths {
|
||||
@@ -155,7 +155,8 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||
passphrase = pendingPassphrase,
|
||||
sessionPaths = currentSessionPaths,
|
||||
)
|
||||
newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client))
|
||||
val matrixClient = rustMatrixClientFactory.create(client)
|
||||
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
||||
sessionStore.storeData(sessionData)
|
||||
|
||||
// Clean up the strong reference held here since it's no longer necessary
|
||||
@@ -246,7 +247,8 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||
pendingOAuthAuthorizationData?.close()
|
||||
pendingOAuthAuthorizationData = null
|
||||
|
||||
newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client))
|
||||
val matrixClient = rustMatrixClientFactory.create(client)
|
||||
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
||||
sessionStore.storeData(sessionData)
|
||||
|
||||
// Clean up the strong reference held here since it's no longer necessary
|
||||
@@ -290,7 +292,8 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||
passphrase = pendingPassphrase,
|
||||
sessionPaths = emptySessionPaths,
|
||||
)
|
||||
newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client))
|
||||
val matrixClient = rustMatrixClientFactory.create(client)
|
||||
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
||||
sessionStore.storeData(sessionData)
|
||||
|
||||
// Clean up the strong reference held here since it's no longer necessary
|
||||
|
||||
@@ -15,6 +15,7 @@ import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
|
||||
import io.element.android.libraries.matrix.api.tracing.TracingService
|
||||
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
|
||||
import org.matrix.rustcomponents.sdk.TracingFileConfiguration
|
||||
import org.matrix.rustcomponents.sdk.reloadTracingFileWriter
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -23,6 +24,12 @@ class RustTracingService @Inject constructor(private val buildMeta: BuildMeta) :
|
||||
override fun createTimberTree(target: String): Timber.Tree {
|
||||
return RustTracingTree(target = target, retrieveFromStackTrace = buildMeta.isDebuggable)
|
||||
}
|
||||
|
||||
override fun updateWriteToFilesConfiguration(config: WriteToFilesConfiguration) {
|
||||
config.toTracingFileConfiguration()?.let {
|
||||
reloadTracingFileWriter(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LogLevel.toRustLogLevel(): org.matrix.rustcomponents.sdk.LogLevel {
|
||||
|
||||
Reference in New Issue
Block a user