diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6eae0a2..63d4afe 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,6 +4,13 @@ plugins { alias(libs.plugins.architecture.koin) } +android { + // Needed for BuildConfig.DEBUG (gating the Timber DebugTree). + buildFeatures { + buildConfig = true + } +} + dependencies { // :app is the only place modules are assembled and the dependency graph is wired. implementation(project(":core:data")) @@ -15,6 +22,8 @@ dependencies { implementation(libs.bundles.lifecycle.compose) // Material Components — required for the Material3 XML Activity theme. implementation(libs.material) + // Logging — the DebugTree is planted here; other modules log via Timber's static API. + implementation(libs.timber) androidTestImplementation(libs.androidx.compose.ui.test.junit4) debugImplementation(libs.androidx.compose.ui.test.manifest) diff --git a/app/src/main/kotlin/com/example/architecture/ArchitectureApp.kt b/app/src/main/kotlin/com/example/architecture/ArchitectureApp.kt index a7170fb..3005658 100644 --- a/app/src/main/kotlin/com/example/architecture/ArchitectureApp.kt +++ b/app/src/main/kotlin/com/example/architecture/ArchitectureApp.kt @@ -5,6 +5,7 @@ import com.example.architecture.core.data.di.coreDataModule import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.core.context.startKoin +import timber.log.Timber /** * Single Koin entry point. Feature modules append their own `*DataModule` / `*PresentationModule` @@ -13,6 +14,12 @@ import org.koin.core.context.startKoin class ArchitectureApp : Application() { override fun onCreate() { super.onCreate() + + // Plant Timber only in debug; release builds get no logs (swap in a crash-reporting tree). + if (BuildConfig.DEBUG) { + Timber.plant(Timber.DebugTree()) + } + startKoin { androidLogger() androidContext(this@ArchitectureApp) diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index c9d7554..c12abdf 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -18,5 +18,5 @@ android { dependencies { implementation(project(":core:domain")) - implementation(libs.kermit) + implementation(libs.timber) } diff --git a/core/data/src/main/kotlin/com/example/architecture/core/data/network/HttpClientExt.kt b/core/data/src/main/kotlin/com/example/architecture/core/data/network/HttpClientExt.kt index edbff16..f2fb972 100644 --- a/core/data/src/main/kotlin/com/example/architecture/core/data/network/HttpClientExt.kt +++ b/core/data/src/main/kotlin/com/example/architecture/core/data/network/HttpClientExt.kt @@ -13,10 +13,10 @@ import io.ktor.client.request.setBody import io.ktor.client.request.url import io.ktor.client.statement.HttpResponse import kotlinx.serialization.SerializationException +import timber.log.Timber import java.net.UnknownHostException import java.nio.channels.UnresolvedAddressException import kotlin.coroutines.cancellation.CancellationException -import co.touchlab.kermit.Logger as KermitLogger suspend inline fun HttpClient.get( route: String, @@ -65,17 +65,17 @@ suspend inline fun safeCall( return try { responseToResult(execute()) } catch (e: UnresolvedAddressException) { - KermitLogger.withTag("HttpClient").e(e) { "No internet (unresolved address)" } + Timber.tag("HttpClient").e(e, "No internet (unresolved address)") Result.Error(DataError.Network.NO_INTERNET) } catch (e: UnknownHostException) { - KermitLogger.withTag("HttpClient").e(e) { "No internet (unknown host)" } + Timber.tag("HttpClient").e(e, "No internet (unknown host)") Result.Error(DataError.Network.NO_INTERNET) } catch (e: SerializationException) { - KermitLogger.withTag("HttpClient").e(e) { "Serialization failure" } + Timber.tag("HttpClient").e(e, "Serialization failure") Result.Error(DataError.Network.SERIALIZATION) } catch (e: Exception) { if (e is CancellationException) throw e - KermitLogger.withTag("HttpClient").e(e) { "Unknown network failure" } + Timber.tag("HttpClient").e(e, "Unknown network failure") Result.Error(DataError.Network.UNKNOWN) } } diff --git a/core/data/src/main/kotlin/com/example/architecture/core/data/network/HttpClientFactory.kt b/core/data/src/main/kotlin/com/example/architecture/core/data/network/HttpClientFactory.kt index 49268ec..30181e0 100644 --- a/core/data/src/main/kotlin/com/example/architecture/core/data/network/HttpClientFactory.kt +++ b/core/data/src/main/kotlin/com/example/architecture/core/data/network/HttpClientFactory.kt @@ -10,12 +10,13 @@ import io.ktor.http.ContentType import io.ktor.http.contentType import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json -import co.touchlab.kermit.Logger as KermitLogger +import timber.log.Timber import io.ktor.client.plugins.logging.Logger as KtorLogger /** * Builds the app's single [HttpClient]. The [engine] is injected so tests can pass a Ktor - * `MockEngine` while production passes OkHttp (see `coreDataModule`). + * `MockEngine` while production passes OkHttp (see `coreDataModule`). Ktor logging is bridged to + * Timber so all logs flow through one tree (planted in the Application). */ object HttpClientFactory { fun create(engine: HttpClientEngine): HttpClient { @@ -30,7 +31,7 @@ object HttpClientFactory { install(Logging) { logger = object : KtorLogger { override fun log(message: String) { - KermitLogger.withTag("HttpClient").d(message) + Timber.tag("HttpClient").d(message) } } level = LogLevel.ALL diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5ee5428..4b33f3e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ ktor = "3.1.3" coil = "3.1.0" # Logging -kermit = "2.0.5" +timber = "5.0.1" # Material Components (Views renderer) material = "1.12.0" @@ -110,7 +110,7 @@ coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" } # --- Logging --- -kermit = { module = "co.touchlab:kermit", version.ref = "kermit" } +timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } # --- Testing --- junit4 = { module = "junit:junit", version.ref = "junit4" }