From 596a347f03a6279552626d80b5fb4923da21233b Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Tue, 24 Mar 2026 11:37:55 -0400 Subject: [PATCH 1/2] [AI] Enable Ktor logging for debug builds in firebase-ai This change enables network logging for debug variants of the `firebase-ai` SDK to facilitate development and debugging. - Enable `BuildConfig` generation in firebase-ai. - Install Ktor Logging plugin in `APIController` for debug builds, with header sanitization for security (`X-Android-Cert`, `x-goog-api-key`). - Configure `slf4j-simple` for debug builds and `slf4j-nop` for release builds in firebase-ai. --- ai-logic/firebase-ai/firebase-ai.gradle.kts | 6 +++++- .../com/google/firebase/ai/common/APIController.kt | 10 ++++++++++ gradle/libs.versions.toml | 5 +++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/ai-logic/firebase-ai/firebase-ai.gradle.kts b/ai-logic/firebase-ai/firebase-ai.gradle.kts index 654a81d7fef..cd0d36b2155 100644 --- a/ai-logic/firebase-ai/firebase-ai.gradle.kts +++ b/ai-logic/firebase-ai/firebase-ai.gradle.kts @@ -51,6 +51,7 @@ android { proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } + buildFeatures { buildConfig = true } compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 @@ -89,7 +90,6 @@ dependencies { implementation(libs.androidx.annotation) implementation(libs.kotlinx.serialization.json) implementation(libs.androidx.core.ktx) - implementation(libs.slf4j.nop) implementation(libs.kotlinx.coroutines.android) implementation(libs.kotlinx.coroutines.reactive) implementation(libs.reactive.streams) @@ -99,6 +99,10 @@ dependencies { implementation("com.google.firebase:firebase-auth-interop:18.0.0") implementation("com.google.firebase:firebase-ai-ondevice-interop:16.0.0-beta01") + // Use different logging libraries depending on the variant + releaseImplementation(libs.slf4j.nop) + debugImplementation(libs.slf4j.simple) + testImplementation(libs.kotest.assertions.core) testImplementation(libs.kotest.assertions) testImplementation(libs.kotest.assertions.json) diff --git a/ai-logic/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt b/ai-logic/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt index 3fa4e46acc4..aa5ee12d94c 100644 --- a/ai-logic/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt +++ b/ai-logic/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt @@ -22,6 +22,7 @@ import android.os.Build import android.util.Log import com.google.firebase.Firebase import com.google.firebase.FirebaseApp +import com.google.firebase.ai.BuildConfig import com.google.firebase.ai.common.util.decodeToFlow import com.google.firebase.ai.common.util.fullModelName import com.google.firebase.ai.type.APINotConfiguredException @@ -51,6 +52,8 @@ import io.ktor.client.engine.HttpClientEngine import io.ktor.client.engine.okhttp.OkHttp import io.ktor.client.plugins.HttpTimeout import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.plugins.logging.Logging import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession import io.ktor.client.plugins.websocket.WebSockets import io.ktor.client.plugins.websocket.webSocketSession @@ -155,6 +158,13 @@ internal constructor( } install(WebSockets) install(ContentNegotiation) { json(JSON) } + if (BuildConfig.DEBUG) { + install(Logging) { + sanitizeHeader { header -> header == "X-Android-Cert" } + sanitizeHeader { header -> header == "x-goog-api-key" } + level = LogLevel.ALL + } + } } suspend fun generateContent(request: GenerateContentRequest): GenerateContentResponse.Internal = diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a3ebcfabb78..bf8ffbcf871 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -69,7 +69,7 @@ runner = "1.0.2" rxandroid = "2.0.2" rxjava = "2.2.21" serialization = "1.7.3" -slf4jNop = "2.0.17" +slf4j = "2.0.17" spotless = "7.0.4" symbolProcessingApi = "2.2.10-2.0.2" testServices = "1.6.0" @@ -207,7 +207,8 @@ robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectr runner = { module = "com.android.support.test:runner", version.ref = "runner" } rxandroid = { module = "io.reactivex.rxjava2:rxandroid", version.ref = "rxandroid" } rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava" } -slf4j-nop = { module = "org.slf4j:slf4j-nop", version.ref = "slf4jNop" } +slf4j-nop = { module = "org.slf4j:slf4j-nop", version.ref = "slf4j" } +slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } spotless-plugin-gradle = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } symbol-processing-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "symbolProcessingApi" } truth = { module = "com.google.truth:truth", version.ref = "truth" } From db64979af49bb63017bc69e12c15aebd00cfcd6d Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Tue, 24 Mar 2026 11:56:12 -0400 Subject: [PATCH 2/2] Use case-insensitive comparison for the headers --- .../kotlin/com/google/firebase/ai/common/APIController.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ai-logic/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt b/ai-logic/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt index aa5ee12d94c..1f6bb9a5f73 100644 --- a/ai-logic/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt +++ b/ai-logic/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt @@ -160,8 +160,10 @@ internal constructor( install(ContentNegotiation) { json(JSON) } if (BuildConfig.DEBUG) { install(Logging) { - sanitizeHeader { header -> header == "X-Android-Cert" } - sanitizeHeader { header -> header == "x-goog-api-key" } + sanitizeHeader { header -> + header.equals("X-Android-Cert", ignoreCase = true) || + header.equals("x-goog-api-key", ignoreCase = true) + } level = LogLevel.ALL } }