Skip to content

Commit 8a163fc

Browse files
authored
Merge pull request #5132 from element-hq/feature/bma/rageshakeConfig
Let enterprise build store the logs in a dedicated subfolder
2 parents a9f54a9 + ea6e328 commit 8a163fc

File tree

13 files changed

+325
-75
lines changed

13 files changed

+325
-75
lines changed

app/src/main/kotlin/io/element/android/x/initializer/PlatformInitializer.kt

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@ package io.element.android.x.initializer
1010
import android.content.Context
1111
import android.system.Os
1212
import androidx.startup.Initializer
13-
import io.element.android.features.rageshake.api.reporter.BugReporter
13+
import io.element.android.features.rageshake.api.logs.createWriteToFilesConfiguration
1414
import io.element.android.libraries.architecture.bindings
1515
import io.element.android.libraries.featureflag.api.FeatureFlags
1616
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
17-
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
1817
import io.element.android.x.di.AppBindings
1918
import kotlinx.coroutines.flow.first
2019
import kotlinx.coroutines.runBlocking
@@ -34,7 +33,7 @@ class PlatformInitializer : Initializer<Unit> {
3433
val logLevel = runBlocking { preferencesStore.getTracingLogLevelFlow().first() }
3534
val tracingConfiguration = TracingConfiguration(
3635
writesToLogcat = runBlocking { featureFlagService.isFeatureEnabled(FeatureFlags.PrintLogsToLogcat) },
37-
writesToFilesConfiguration = defaultWriteToDiskConfiguration(bugReporter),
36+
writesToFilesConfiguration = bugReporter.createWriteToFilesConfiguration(),
3837
logLevel = logLevel,
3938
extraTargets = listOf(ELEMENT_X_TARGET),
4039
traceLogPacks = runBlocking { preferencesStore.getTracingLogPacksFlow().first() },
@@ -45,14 +44,5 @@ class PlatformInitializer : Initializer<Unit> {
4544
Os.setenv("RUST_BACKTRACE", "1", true)
4645
}
4746

48-
private fun defaultWriteToDiskConfiguration(bugReporter: BugReporter): WriteToFilesConfiguration.Enabled {
49-
return WriteToFilesConfiguration.Enabled(
50-
directory = bugReporter.logDirectory().absolutePath,
51-
filenamePrefix = "logs",
52-
// Keep a maximum of 1 week of log files.
53-
numberOfFiles = 7 * 24,
54-
)
55-
}
56-
5747
override fun dependencies(): List<Class<out Initializer<*>>> = mutableListOf()
5848
}

appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import io.element.android.appnav.root.RootView
3636
import io.element.android.features.enterprise.api.EnterpriseService
3737
import io.element.android.features.login.api.LoginParams
3838
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
39+
import io.element.android.features.rageshake.api.reporter.BugReporter
3940
import io.element.android.features.signedout.api.SignedOutEntryPoint
4041
import io.element.android.features.viewfolder.api.ViewFolderEntryPoint
4142
import io.element.android.libraries.architecture.BackstackView
@@ -73,6 +74,7 @@ class RootFlowNode @AssistedInject constructor(
7374
private val signedOutEntryPoint: SignedOutEntryPoint,
7475
private val intentResolver: IntentResolver,
7576
private val oidcActionFlow: OidcActionFlow,
77+
private val bugReporter: BugReporter,
7678
) : BaseFlowNode<RootFlowNode.NavTarget>(
7779
backstack = BackStack(
7880
initialElement = NavTarget.SplashScreen,
@@ -123,6 +125,7 @@ class RootFlowNode @AssistedInject constructor(
123125

124126
private fun switchToNotLoggedInFlow(params: LoginParams?) {
125127
matrixSessionCache.removeAll()
128+
bugReporter.setLogDirectorySubfolder(null)
126129
backstack.safeRoot(NavTarget.NotLoggedInFlow(params))
127130
}
128131

appnav/src/main/kotlin/io/element/android/appnav/di/MatrixSessionCache.kt

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,7 @@ class MatrixSessionCache @Inject constructor(
4242

4343
init {
4444
authenticationService.listenToNewMatrixClients { matrixClient ->
45-
val syncOrchestrator = syncOrchestratorFactory.create(matrixClient)
46-
sessionIdsToMatrixSession[matrixClient.sessionId] = InMemoryMatrixSession(
47-
matrixClient = matrixClient,
48-
syncOrchestrator = syncOrchestrator,
49-
)
50-
syncOrchestrator.start()
45+
onNewMatrixClient(matrixClient)
5146
}
5247
}
5348

@@ -105,17 +100,21 @@ class MatrixSessionCache @Inject constructor(
105100
Timber.d("Restore matrix session: $sessionId")
106101
return authenticationService.restoreSession(sessionId)
107102
.onSuccess { matrixClient ->
108-
val syncOrchestrator = syncOrchestratorFactory.create(matrixClient)
109-
sessionIdsToMatrixSession[matrixClient.sessionId] = InMemoryMatrixSession(
110-
matrixClient = matrixClient,
111-
syncOrchestrator = syncOrchestrator,
112-
)
113-
syncOrchestrator.start()
103+
onNewMatrixClient(matrixClient)
114104
}
115105
.onFailure {
116106
Timber.e(it, "Fail to restore session")
117107
}
118108
}
109+
110+
private fun onNewMatrixClient(matrixClient: MatrixClient) {
111+
val syncOrchestrator = syncOrchestratorFactory.create(matrixClient)
112+
sessionIdsToMatrixSession[matrixClient.sessionId] = InMemoryMatrixSession(
113+
matrixClient = matrixClient,
114+
syncOrchestrator = syncOrchestrator,
115+
)
116+
syncOrchestrator.start()
117+
}
119118
}
120119

121120
private data class InMemoryMatrixSession(

features/rageshake/api/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ dependencies {
1616
implementation(projects.libraries.architecture)
1717
implementation(projects.libraries.designsystem)
1818
implementation(projects.libraries.androidutils)
19+
implementation(projects.libraries.matrix.api)
1920
implementation(projects.libraries.uiStrings)
2021
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.rageshake.api.logs
9+
10+
import io.element.android.features.rageshake.api.reporter.BugReporter
11+
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
12+
13+
fun BugReporter.createWriteToFilesConfiguration(): WriteToFilesConfiguration {
14+
return WriteToFilesConfiguration.Enabled(
15+
directory = logDirectory().absolutePath,
16+
filenamePrefix = "logs",
17+
// Keep a maximum of 1 week of log files.
18+
numberOfFiles = 7 * 24,
19+
)
20+
}

features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ interface BugReporter {
3434
*/
3535
fun logDirectory(): File
3636

37+
/**
38+
* Set the subfolder name for the log directory.
39+
* This will create a subfolder in the log directory with the given name.
40+
* It will also configure the Rust SDK to use this subfolder for its logs.
41+
* If the name is null, the log files will be stored in the base folder for the logs.
42+
*/
43+
fun setLogDirectorySubfolder(subfolderName: String?)
44+
3745
/**
3846
* Set the current tracing log level.
3947
*/

features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import androidx.core.net.toFile
1313
import androidx.core.net.toUri
1414
import com.squareup.anvil.annotations.ContributesBinding
1515
import io.element.android.appconfig.RageshakeConfig
16+
import io.element.android.features.rageshake.api.logs.createWriteToFilesConfiguration
1617
import io.element.android.features.rageshake.api.reporter.BugReporter
1718
import io.element.android.features.rageshake.api.reporter.BugReporterListener
1819
import io.element.android.features.rageshake.impl.crash.CrashDataStore
@@ -28,11 +29,14 @@ import io.element.android.libraries.di.ApplicationContext
2829
import io.element.android.libraries.di.SingleIn
2930
import io.element.android.libraries.matrix.api.MatrixClientProvider
3031
import io.element.android.libraries.matrix.api.SdkMetadata
32+
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
3133
import io.element.android.libraries.matrix.api.core.UserId
34+
import io.element.android.libraries.matrix.api.tracing.TracingService
3235
import io.element.android.libraries.network.useragent.UserAgentProvider
3336
import io.element.android.libraries.sessionstorage.api.SessionStore
3437
import kotlinx.coroutines.CancellationException
3538
import kotlinx.coroutines.flow.first
39+
import kotlinx.coroutines.runBlocking
3640
import kotlinx.coroutines.withContext
3741
import okhttp3.MediaType.Companion.toMediaTypeOrNull
3842
import okhttp3.OkHttpClient
@@ -71,6 +75,8 @@ class DefaultBugReporter @Inject constructor(
7175
private val bugReporterUrlProvider: BugReporterUrlProvider,
7276
private val sdkMetadata: SdkMetadata,
7377
private val matrixClientProvider: MatrixClientProvider,
78+
private val tracingService: TracingService,
79+
matrixAuthenticationService: MatrixAuthenticationService,
7480
) : BugReporter {
7581
companion object {
7682
// filenames
@@ -81,7 +87,24 @@ class DefaultBugReporter @Inject constructor(
8187
private val logcatCommandDebug = arrayOf("logcat", "-d", "-v", "threadtime", "*:*")
8288
private var currentTracingLogLevel: String? = null
8389

84-
private val logCatErrFile = File(logDirectory().absolutePath, LOG_CAT_FILENAME)
90+
private val logCatErrFile: File
91+
get() = File(logDirectory(), LOG_CAT_FILENAME)
92+
private val baseLogDirectory = File(context.cacheDir, LOG_DIRECTORY_NAME)
93+
private var currentLogDirectory: File = baseLogDirectory
94+
95+
init {
96+
if (buildMeta.isEnterpriseBuild) {
97+
val logSubfolder = runBlocking {
98+
sessionStore.getLatestSession()
99+
}?.userId?.substringAfter(":")
100+
setCurrentLogDirectory(logSubfolder)
101+
matrixAuthenticationService.listenToNewMatrixClients {
102+
// When a new Matrix client is created, we update the tracing configuration to write
103+
// the files in a dedicated subfolders.
104+
setLogDirectorySubfolder(it.userIdServerName())
105+
}
106+
}
107+
}
85108

86109
override suspend fun sendBugReport(
87110
withDevicesLogs: Boolean,
@@ -286,16 +309,44 @@ class DefaultBugReporter @Inject constructor(
286309
}
287310

288311
override fun logDirectory(): File {
289-
return File(context.cacheDir, LOG_DIRECTORY_NAME).apply {
312+
return currentLogDirectory.apply {
290313
mkdirs()
291314
}
292315
}
293316

317+
override fun setLogDirectorySubfolder(subfolderName: String?) {
318+
if (buildMeta.isEnterpriseBuild) {
319+
setCurrentLogDirectory(subfolderName)
320+
tracingService.updateWriteToFilesConfiguration(createWriteToFilesConfiguration())
321+
}
322+
}
323+
324+
private fun setCurrentLogDirectory(subfolderName: String?) {
325+
currentLogDirectory = if (subfolderName == null) {
326+
baseLogDirectory
327+
} else {
328+
File(baseLogDirectory, subfolderName)
329+
}
330+
}
331+
294332
suspend fun deleteAllFiles(predicate: (File) -> Boolean) {
295333
withContext(coroutineDispatchers.io) {
296-
getLogFiles()
297-
.filter(predicate)
298-
.forEach { it.safeDelete() }
334+
deleteAllFilesRecursive(baseLogDirectory, predicate)
335+
}
336+
}
337+
338+
private fun deleteAllFilesRecursive(
339+
directory: File,
340+
predicate: (File) -> Boolean,
341+
) {
342+
directory.listFiles()?.forEach { file ->
343+
if (file.isDirectory) {
344+
deleteAllFilesRecursive(file, predicate)
345+
} else {
346+
if (predicate(file)) {
347+
file.safeDelete()
348+
}
349+
}
299350
}
300351
}
301352

@@ -325,11 +376,12 @@ class DefaultBugReporter @Inject constructor(
325376
* @return the file if the operation succeeds
326377
*/
327378
override fun saveLogCat() {
328-
if (logCatErrFile.exists()) {
329-
logCatErrFile.safeDelete()
379+
val file = logCatErrFile
380+
if (file.exists()) {
381+
file.safeDelete()
330382
}
331383
try {
332-
logCatErrFile.writer().use {
384+
file.writer().use {
333385
getLogCatError(it)
334386
}
335387
} catch (error: OutOfMemoryError) {

features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ class FakeBugReporter(val mode: Mode = Mode.Success) : BugReporter {
5353
return File("fake")
5454
}
5555

56+
override fun setLogDirectorySubfolder(subfolderName: String?) {
57+
// No op
58+
}
59+
5660
override fun setCurrentTracingLogLevel(logLevel: String) {
5761
// No op
5862
}

0 commit comments

Comments
 (0)