mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 03:48:12 +03:00
Add metrics plugin to track device download keys task (#7438)
* Add metrics tracking plugin for download device keys * Add support for multiple metrics plugin * Update copyright license header in matrix-sdk-android * Add tests for MetricExtension * Update changelog * Improve MetricsExtension and reformatting
This commit is contained in:
parent
646cc7d67d
commit
b6746653f1
9 changed files with 236 additions and 18 deletions
1
changelog.d/7438.sdk
Normal file
1
changelog.d/7438.sdk
Normal file
|
@ -0,0 +1 @@
|
|||
Add MetricPlugin interface to implement metrics in SDK clients.
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.api
|
|||
import okhttp3.ConnectionSpec
|
||||
import okhttp3.Interceptor
|
||||
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
||||
import org.matrix.android.sdk.api.metrics.MetricPlugin
|
||||
import java.net.Proxy
|
||||
|
||||
data class MatrixConfiguration(
|
||||
|
@ -74,4 +75,9 @@ data class MatrixConfiguration(
|
|||
* Sync configuration.
|
||||
*/
|
||||
val syncConfig: SyncConfig = SyncConfig(),
|
||||
|
||||
/**
|
||||
* Metrics plugin that can be used to capture metrics from matrix-sdk-android.
|
||||
*/
|
||||
val metricPlugins: List<MetricPlugin> = emptyList()
|
||||
)
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.extensions
|
||||
|
||||
import org.matrix.android.sdk.api.metrics.MetricPlugin
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* Executes the given [block] while measuring the transaction.
|
||||
*/
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun measureMetric(metricMeasurementPlugins: List<MetricPlugin>, block: () -> Unit) {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
try {
|
||||
metricMeasurementPlugins.forEach { plugin -> plugin.startTransaction() } // Start the transaction.
|
||||
block()
|
||||
} catch (throwable: Throwable) {
|
||||
metricMeasurementPlugins.forEach { plugin -> plugin.onError(throwable) } // Capture if there is any exception thrown.
|
||||
throw throwable
|
||||
} finally {
|
||||
metricMeasurementPlugins.forEach { plugin -> plugin.finishTransaction() } // Finally, finish this transaction.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.metrics
|
||||
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import timber.log.Timber
|
||||
|
||||
private val loggerTag = LoggerTag("DownloadKeysMetricsPlugin", LoggerTag.CRYPTO)
|
||||
|
||||
/**
|
||||
* Extension of MetricPlugin for download_device_keys task.
|
||||
*/
|
||||
interface DownloadDeviceKeysMetricsPlugin : MetricPlugin {
|
||||
|
||||
override fun logTransaction(message: String?) {
|
||||
Timber.tag(loggerTag.value).v("## downloadDeviceKeysMetricPlugin() : $message")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.metrics
|
||||
|
||||
/**
|
||||
* A plugin that can be used to capture metrics in Client.
|
||||
*/
|
||||
interface MetricPlugin {
|
||||
/**
|
||||
* Start the measurement of the metrics as soon as task is started.
|
||||
*/
|
||||
fun startTransaction()
|
||||
|
||||
/**
|
||||
* Mark the measuring transaction finished once the task is completed.
|
||||
*/
|
||||
fun finishTransaction()
|
||||
|
||||
/**
|
||||
* Invoked when there is any error in the ongoing task. The metrics tool can use this information to attach to the ongoing transaction.
|
||||
*
|
||||
* @param throwable Exception thrown in the running task.
|
||||
*/
|
||||
fun onError(throwable: Throwable)
|
||||
|
||||
/**
|
||||
* Can be used to log this transaction.
|
||||
*/
|
||||
fun logTransaction(message: String? = "") {
|
||||
// no-op
|
||||
}
|
||||
}
|
|
@ -18,13 +18,17 @@ package org.matrix.android.sdk.internal.crypto
|
|||
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.MatrixPatterns
|
||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
||||
import org.matrix.android.sdk.api.extensions.measureMetric
|
||||
import org.matrix.android.sdk.api.metrics.DownloadDeviceKeysMetricsPlugin
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.CryptoInfoMapper
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.DownloadKeysForUsersTask
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
|
@ -47,8 +51,11 @@ internal class DeviceListManager @Inject constructor(
|
|||
coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val clock: Clock,
|
||||
matrixConfiguration: MatrixConfiguration
|
||||
) {
|
||||
|
||||
private val metricPlugins = matrixConfiguration.metricPlugins
|
||||
|
||||
interface UserDevicesUpdateListener {
|
||||
fun onUsersDeviceUpdate(userIds: List<String>)
|
||||
}
|
||||
|
@ -345,19 +352,25 @@ internal class DeviceListManager @Inject constructor(
|
|||
return MXUsersDevicesMap()
|
||||
}
|
||||
val params = DownloadKeysForUsersTask.Params(filteredUsers, syncTokenStore.getLastToken())
|
||||
val response = try {
|
||||
downloadKeysForUsersTask.execute(params)
|
||||
} catch (throwable: Throwable) {
|
||||
Timber.e(throwable, "## CRYPTO | doKeyDownloadForUsers(): error")
|
||||
if (throwable is CancellationException) {
|
||||
// the crypto module is getting closed, so we cannot access the DB anymore
|
||||
Timber.w("The crypto module is closed, ignoring this error")
|
||||
} else {
|
||||
onKeysDownloadFailed(filteredUsers)
|
||||
val relevantPlugins = metricPlugins.filterIsInstance<DownloadDeviceKeysMetricsPlugin>()
|
||||
|
||||
val response: KeysQueryResponse
|
||||
measureMetric(relevantPlugins) {
|
||||
response = try {
|
||||
downloadKeysForUsersTask.execute(params)
|
||||
} catch (throwable: Throwable) {
|
||||
Timber.e(throwable, "## CRYPTO | doKeyDownloadForUsers(): error")
|
||||
if (throwable is CancellationException) {
|
||||
// the crypto module is getting closed, so we cannot access the DB anymore
|
||||
Timber.w("The crypto module is closed, ignoring this error")
|
||||
} else {
|
||||
onKeysDownloadFailed(filteredUsers)
|
||||
}
|
||||
throw throwable
|
||||
}
|
||||
throw throwable
|
||||
Timber.v("## CRYPTO | doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
|
||||
}
|
||||
Timber.v("## CRYPTO | doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
|
||||
|
||||
for (userId in filteredUsers) {
|
||||
// al devices =
|
||||
val models = response.deviceKeys?.get(userId)?.mapValues { entry -> CryptoInfoMapper.map(entry.value) }
|
||||
|
|
|
@ -47,6 +47,7 @@ import im.vector.app.core.utils.SystemSettingsProvider
|
|||
import im.vector.app.features.analytics.AnalyticsTracker
|
||||
import im.vector.app.features.analytics.VectorAnalytics
|
||||
import im.vector.app.features.analytics.impl.DefaultVectorAnalytics
|
||||
import im.vector.app.features.analytics.metrics.VectorPlugins
|
||||
import im.vector.app.features.invite.AutoAcceptInvites
|
||||
import im.vector.app.features.invite.CompileTimeAutoAcceptInvites
|
||||
import im.vector.app.features.navigation.DefaultNavigator
|
||||
|
@ -75,9 +76,7 @@ import org.matrix.android.sdk.api.session.Session
|
|||
import org.matrix.android.sdk.api.settings.LightweightSettingsStorage
|
||||
import javax.inject.Singleton
|
||||
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@Module
|
||||
abstract class VectorBindModule {
|
||||
@InstallIn(SingletonComponent::class) @Module abstract class VectorBindModule {
|
||||
|
||||
@Binds
|
||||
abstract fun bindNavigator(navigator: DefaultNavigator): Navigator
|
||||
|
@ -119,9 +118,7 @@ abstract class VectorBindModule {
|
|||
abstract fun bindGetDeviceInfoUseCase(getDeviceInfoUseCase: DefaultGetDeviceInfoUseCase): GetDeviceInfoUseCase
|
||||
}
|
||||
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@Module
|
||||
object VectorStaticModule {
|
||||
@InstallIn(SingletonComponent::class) @Module object VectorStaticModule {
|
||||
|
||||
@Provides
|
||||
fun providesContext(application: Application): Context {
|
||||
|
@ -143,6 +140,7 @@ object VectorStaticModule {
|
|||
vectorPreferences: VectorPreferences,
|
||||
vectorRoomDisplayNameFallbackProvider: VectorRoomDisplayNameFallbackProvider,
|
||||
flipperProxy: FlipperProxy,
|
||||
vectorPlugins: VectorPlugins,
|
||||
): MatrixConfiguration {
|
||||
return MatrixConfiguration(
|
||||
applicationFlavor = BuildConfig.FLAVOR_DESCRIPTION,
|
||||
|
@ -150,7 +148,8 @@ object VectorStaticModule {
|
|||
threadMessagesEnabledDefault = vectorPreferences.areThreadMessagesEnabled(),
|
||||
networkInterceptors = listOfNotNull(
|
||||
flipperProxy.networkInterceptor(),
|
||||
)
|
||||
),
|
||||
metricPlugins = vectorPlugins.plugins(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.analytics.metrics
|
||||
|
||||
import im.vector.app.features.analytics.metrics.sentry.SentryDownloadDeviceKeysMetrics
|
||||
import org.matrix.android.sdk.api.metrics.MetricPlugin
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Class that contains the all plugins which can be used for tracking.
|
||||
*/
|
||||
@Singleton
|
||||
data class VectorPlugins @Inject constructor(
|
||||
val sentryDownloadDeviceKeysMetrics: SentryDownloadDeviceKeysMetrics,
|
||||
) {
|
||||
/**
|
||||
* Returns [List] of all [MetricPlugin] hold by this class.
|
||||
*/
|
||||
fun plugins(): List<MetricPlugin> = listOf(sentryDownloadDeviceKeysMetrics)
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.analytics.metrics.sentry
|
||||
|
||||
import io.sentry.ITransaction
|
||||
import io.sentry.Sentry
|
||||
import io.sentry.SpanStatus
|
||||
import org.matrix.android.sdk.api.metrics.DownloadDeviceKeysMetricsPlugin
|
||||
import javax.inject.Inject
|
||||
|
||||
class SentryDownloadDeviceKeysMetrics @Inject constructor() : DownloadDeviceKeysMetricsPlugin {
|
||||
private var transaction: ITransaction? = null
|
||||
|
||||
override fun startTransaction() {
|
||||
transaction = Sentry.startTransaction("download_device_keys", "task")
|
||||
logTransaction("Sentry transaction started")
|
||||
}
|
||||
|
||||
override fun finishTransaction() {
|
||||
transaction?.finish()
|
||||
logTransaction("Sentry transaction finished")
|
||||
}
|
||||
|
||||
override fun onError(throwable: Throwable) {
|
||||
transaction?.apply {
|
||||
this.throwable = throwable
|
||||
this.status = SpanStatus.INTERNAL_ERROR
|
||||
}
|
||||
logTransaction("Sentry transaction encountered error ${throwable.message}")
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue