Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: database logger [WPB-14608] #3227

Merged
merged 7 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package com.wire.kalium.logic.feature.debug

import com.wire.kalium.logic.di.UserStorage
import com.wire.kalium.persistence.db.DBProfile

class ChangeProfilingUseCase(
private val userStorage: UserStorage,
Expand All @@ -26,7 +27,11 @@ class ChangeProfilingUseCase(
* Changes the profiling of the database (cipher_profile) if the profile is specified and the database is encrypted
* @param enabled true to enable profiling, false to disable
*/
operator fun invoke(enabled: Boolean) {
userStorage.database.changeProfiling(enabled)
suspend operator fun invoke(status: DBProfile) {
userStorage.database.debugExtension.changeProfiling(status)
}

suspend operator fun invoke(enabled: Boolean) {
userStorage.database.debugExtension.changeProfiling(if (enabled) DBProfile.ON.Device else DBProfile.Off)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class DebugScope internal constructor(
private val legalHoldHandler: LegalHoldHandler,
private val notificationTokenRepository: NotificationTokenRepository,
private val scope: CoroutineScope,
userStorage: UserStorage,
private val userStorage: UserStorage,
logger: KaliumLogger,
internal val dispatcher: KaliumDispatcher = KaliumDispatcherImpl,
) {
Expand Down Expand Up @@ -227,5 +227,7 @@ class DebugScope internal constructor(
notificationTokenRepository,
)

val changeProfiling: ChangeProfilingUseCase = ChangeProfilingUseCase(userStorage)
val changeProfiling: ChangeProfilingUseCase get() = ChangeProfilingUseCase(userStorage)

val observeDatabaseLoggerState get() = ObserveDatabaseLoggerStateUseCase(userStorage)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Wire
* Copyright (C) 2025 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.logic.feature.debug

import com.wire.kalium.logic.di.UserStorage
import com.wire.kalium.persistence.db.DBProfile
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

/**
* Use case to observe the state of the database logger.
*/
class ObserveDatabaseLoggerStateUseCase(
private val userStorage: UserStorage,
) {
suspend operator fun invoke(): Flow<Boolean> = userStorage.database.debugExtension.getProfilingState().map {
it is DBProfile.ON
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ actual fun userDatabaseBuilder(
dispatcher = dispatcher,
platformDatabaseData = platformDatabaseData,
isEncrypted = isEncryptionEnabled,
cipherProfile = "logcat",
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Wire
* Copyright (C) 2025 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.persistence.db

import app.cash.sqldelight.db.QueryResult
import app.cash.sqldelight.db.SqlDriver
import com.wire.kalium.persistence.dao.MetadataDAO
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

class DebugExtension(
private val sqlDriver: SqlDriver,
private val isEncrypted: Boolean,
private val metaDataDao: MetadataDAO,
) {

suspend fun getProfilingState(): Flow<DBProfile?> =
metaDataDao.valueByKeyFlow(KEY_CIPHER_PROFILE)
.map {
it?.let { DBProfile.fromString(it) }
}

/**
* Changes the profiling of the database (cipher_profile) if the profile is specified and the database is encrypted
* @param enabled true to enable profiling, false to disable
*/
suspend fun changeProfiling(state: DBProfile): Long? =
if (isEncrypted) {
sqlDriver.executeQuery(
identifier = null,
sql = """PRAGMA cipher_profile= '${state.logTarget}';""",
mapper = { cursor ->
cursor.next()
cursor.getLong(0).let { QueryResult.Value<Long?>(it) }
},
parameters = 0,
).value.also {
updateMetadata(state)
}

} else {
error("Cannot change profiling on unencrypted database")
}

private suspend fun updateMetadata(state: DBProfile) {
metaDataDao.insertValue(
value = state.logTarget,
key = KEY_CIPHER_PROFILE
)
}

private companion object {
const val KEY_CIPHER_PROFILE = "cipher_profile"
}
}

sealed interface DBProfile {
val logTarget: String

data object Off : DBProfile {
override val logTarget: String = "off"

override fun toString(): String {
return "off"
}
}

sealed interface ON : DBProfile {
data object Device : ON {
override val logTarget: String = "logcat"
MohamadJaara marked this conversation as resolved.
Show resolved Hide resolved

override fun toString(): String {
return "logcat"
}
}

data class CustomFile(override val logTarget: String) : ON {
override fun toString(): String {
return logTarget
}
}
}

companion object {
fun fromString(value: String): DBProfile = when (value) {
"off" -> Off
"logcat" -> ON.Device
else -> ON.CustomFile(value)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ class UserDatabaseBuilder internal constructor(
private val platformDatabaseData: PlatformDatabaseData,
private val isEncrypted: Boolean,
private val queriesContext: CoroutineContext = KaliumDispatcherImpl.io,
private val cipherProfile: String? = null,
) {

internal val database: UserDatabase = UserDatabase(
Expand Down Expand Up @@ -314,30 +313,18 @@ class UserDatabaseBuilder internal constructor(
queriesContext
)

val debugExtension: DebugExtension
get() = DebugExtension(
sqlDriver = sqlDriver,
metaDataDao = metadataDAO,
isEncrypted = isEncrypted
)

/**
* @return the absolute path of the DB file or null if the DB file does not exist
*/
fun dbFileLocation(): String? = getDatabaseAbsoluteFileLocation(platformDatabaseData, userId)

/**
* Changes the profiling of the database (cipher_profile) if the profile is specified and the database is encrypted
* @param enabled true to enable profiling, false to disable
*/
fun changeProfiling(enabled: Boolean) {
if (isEncrypted && cipherProfile != null) {
val cipherProfileValue = if (enabled) cipherProfile else "off"
sqlDriver.executeQuery(
identifier = null,
sql = "PRAGMA cipher_profile='$cipherProfileValue'",
mapper = {
it.next()
it.getLong(0).let { QueryResult.Value<Long?>(it) }
},
parameters = 0,
)
}
}

/**
* drops DB connection and delete the DB file
*/
Expand Down
Loading