Enable logging OkHttp traffic based on the current log level (#5750)
* Use `LogLevel` to decide whether to log the HTTP requests and responses Added `DynamicHttpLoggingInterceptor` for this. * Code cleanup. * Use Timber.d * OutOfMemoryError should not be caught. They are considered unrecoverable. * Improve code in DefaultBugReporter. --------- Co-authored-by: Benoit Marty <benoit@matrix.org>
This commit is contained in:
parent
740e486cd0
commit
bf0274074d
8 changed files with 72 additions and 40 deletions
|
|
@ -39,8 +39,5 @@ fun compressFile(file: File): File? {
|
|||
} catch (e: Exception) {
|
||||
Timber.e(e, "## compressFile() failed")
|
||||
null
|
||||
} catch (oom: OutOfMemoryError) {
|
||||
Timber.e(oom, "## compressFile() failed")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ dependencies {
|
|||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.di)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.preferences.api)
|
||||
implementation(platform(libs.network.okhttp.bom))
|
||||
implementation(libs.network.okhttp)
|
||||
implementation(libs.network.okhttp.logging)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import dev.zacsweers.metro.BindingContainer
|
|||
import dev.zacsweers.metro.ContributesTo
|
||||
import dev.zacsweers.metro.Provides
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.network.interceptors.DynamicHttpLoggingInterceptor
|
||||
import io.element.android.libraries.network.interceptors.FormattedJsonHttpLogger
|
||||
import io.element.android.libraries.network.interceptors.UserAgentInterceptor
|
||||
import okhttp3.OkHttpClient
|
||||
|
|
@ -26,21 +26,20 @@ object NetworkModule {
|
|||
@Provides
|
||||
@SingleIn(AppScope::class)
|
||||
fun providesOkHttpClient(
|
||||
buildMeta: BuildMeta,
|
||||
userAgentInterceptor: UserAgentInterceptor,
|
||||
dynamicHttpLoggingInterceptor: DynamicHttpLoggingInterceptor,
|
||||
): OkHttpClient = OkHttpClient.Builder().apply {
|
||||
connectTimeout(30, TimeUnit.SECONDS)
|
||||
readTimeout(60, TimeUnit.SECONDS)
|
||||
writeTimeout(60, TimeUnit.SECONDS)
|
||||
addInterceptor(userAgentInterceptor)
|
||||
if (buildMeta.isDebuggable) addInterceptor(providesHttpLoggingInterceptor())
|
||||
addInterceptor(dynamicHttpLoggingInterceptor)
|
||||
}.build()
|
||||
}
|
||||
|
||||
private fun providesHttpLoggingInterceptor(): HttpLoggingInterceptor {
|
||||
val loggingLevel = HttpLoggingInterceptor.Level.BODY
|
||||
val logger = FormattedJsonHttpLogger(loggingLevel)
|
||||
val interceptor = HttpLoggingInterceptor(logger)
|
||||
interceptor.level = loggingLevel
|
||||
return interceptor
|
||||
@Provides
|
||||
@SingleIn(AppScope::class)
|
||||
fun providesHttpLoggingInterceptor(): HttpLoggingInterceptor {
|
||||
val logger = FormattedJsonHttpLogger(HttpLoggingInterceptor.Level.BODY)
|
||||
return HttpLoggingInterceptor(logger)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations 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.libraries.network.interceptors
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import io.element.android.libraries.matrix.api.tracing.LogLevel
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import okhttp3.logging.HttpLoggingInterceptor.Level
|
||||
|
||||
/**
|
||||
* HTTP logging interceptor that decides whether to display the HTTP logs or not based on the current log level.
|
||||
*/
|
||||
@Inject
|
||||
@SingleIn(AppScope::class)
|
||||
class DynamicHttpLoggingInterceptor(
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val loggingInterceptor: HttpLoggingInterceptor,
|
||||
) : Interceptor by loggingInterceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
// This is called in a separate thread, so calling `runBlocking` here should be fine, it should be also instant after the value is cached
|
||||
val logLevel = runBlocking { appPreferencesStore.getTracingLogLevelFlow().first() }
|
||||
loggingInterceptor.level = if (logLevel >= LogLevel.DEBUG) Level.BODY else Level.NONE
|
||||
return loggingInterceptor.intercept(chain)
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ internal class FormattedJsonHttpLogger(
|
|||
*/
|
||||
@Synchronized
|
||||
override fun log(message: String) {
|
||||
Timber.v(message.ellipsize(200_000))
|
||||
Timber.d(message.ellipsize(200_000))
|
||||
|
||||
// Try to log formatted Json only if there is a chance that [message] contains Json.
|
||||
// It can be only the case if we log the bodies of Http requests.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue