From 95c2d0e30faa6b7265343bd9b5e2df1aeade4f0e Mon Sep 17 00:00:00 2001 From: Manmohan Gupta Date: Wed, 24 Jun 2026 18:24:27 +0530 Subject: [PATCH 1/3] UIC-947 : Added logger implementation --- .../Auth0UniversalComponents.kt | 6 +- .../universalcomponents/data/TokenManager.kt | 8 +- .../ui/mfa/EmailEnrollmentScreen.kt | 4 +- .../ui/mfa/OTPVerificationScreen.kt | 4 +- .../presentation/ui/mfa/QREnrollmentScreen.kt | 6 +- .../ui/mfa/RecoveryCodeEnrollmentScreen.kt | 4 +- .../ui/mfa/phone/PhoneEnrollmentScreen.kt | 4 +- .../ui/passkeys/PasskeyViewModel.kt | 4 +- .../viewmodel/EnrollmentViewModel.kt | 22 +++--- .../utils/logging/Logger.kt | 76 ++++++++++++++++++ .../utils/logging/LoggerTest.kt | 77 +++++++++++++++++++ 11 files changed, 186 insertions(+), 29 deletions(-) create mode 100644 universal_components/src/main/java/com/auth0/universalcomponents/utils/logging/Logger.kt create mode 100644 universal_components/src/test/java/com/auth0/universalcomponents/utils/logging/LoggerTest.kt diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/Auth0UniversalComponents.kt b/universal_components/src/main/java/com/auth0/universalcomponents/Auth0UniversalComponents.kt index dfaf869..abc6c0b 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/Auth0UniversalComponents.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/Auth0UniversalComponents.kt @@ -1,10 +1,10 @@ package com.auth0.universalcomponents import android.content.Context -import android.util.Log import androidx.credentials.CredentialManager import com.auth0.android.Auth0 import com.auth0.universalcomponents.token.TokenProvider +import com.auth0.universalcomponents.utils.logging.Logger import java.util.concurrent.atomic.AtomicBoolean /** @@ -24,6 +24,8 @@ data class PasskeyConfiguration( public object Auth0UniversalComponents { + private const val TAG = "Auth0UniversalComponents" + private var initialized = AtomicBoolean(false) private lateinit var _context: Context private lateinit var _account: Auth0 @@ -69,7 +71,7 @@ public object Auth0UniversalComponents { passkeyConfiguration: PasskeyConfiguration = PasskeyConfiguration() ) { if (initialized.get()) { - Log.d("Auth0UniversalComponents", "Auth0UniversalComponents is already initialized.") + Logger.d(TAG, "Auth0UniversalComponents is already initialized.") return } _context = context.applicationContext diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/data/TokenManager.kt b/universal_components/src/main/java/com/auth0/universalcomponents/data/TokenManager.kt index e33d4cf..f699dc1 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/data/TokenManager.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/data/TokenManager.kt @@ -1,9 +1,9 @@ package com.auth0.universalcomponents.data -import android.util.Log import androidx.annotation.VisibleForTesting import com.auth0.android.result.APICredentials import com.auth0.universalcomponents.Auth0UniversalComponents +import com.auth0.universalcomponents.utils.logging.Logger import java.util.concurrent.ConcurrentHashMap /** @@ -51,12 +51,12 @@ class TokenManager private constructor() { val cachedCredentials = tokenMap[cacheKey(audience, scope)] if (cachedCredentials != null) { if (!willTokenExpire(cachedCredentials.expiresAt.time)) { - Log.d(TAG, "Returning cached token for audience: $audience, scope: $scope") + Logger.d(TAG, "Returning cached token for audience: $audience, scope: $scope") return cachedCredentials.accessToken } } - Log.d(TAG, "Fetching new token from provider for audience: $audience, scope: $scope") + Logger.d(TAG, "Fetching new token from provider for audience: $audience, scope: $scope") val credentials = tokenProvider.fetchApiCredentials(audience, scope) // Saving the same token for scenario where we request multiple scopes together @@ -79,7 +79,7 @@ class TokenManager private constructor() { val splitScope = scope.split(" ") if (splitScope.size > 1) { splitScope.forEach { - Log.d(TAG, "token:$it ") + Logger.d(TAG, "Caching token for scope: $it") tokenMap[cacheKey(audience, it)] = credentials } } diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/EmailEnrollmentScreen.kt b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/EmailEnrollmentScreen.kt index c0a246f..78a0aef 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/EmailEnrollmentScreen.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/EmailEnrollmentScreen.kt @@ -1,6 +1,5 @@ package com.auth0.universalcomponents.presentation.ui.mfa -import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box @@ -44,6 +43,7 @@ import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentUiState import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentViewModel import com.auth0.universalcomponents.theme.Auth0Theme import com.auth0.universalcomponents.utils.ValidationUtil +import com.auth0.universalcomponents.utils.logging.Logger @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -80,7 +80,7 @@ internal fun EmailEnrollmentScreen( } is EnrollmentEvent.VerificationSuccess -> { - Log.d("EmailEnrollmentScreen", "$event not handled ") + Logger.d("EmailEnrollmentScreen", "$event not handled ") } } } diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/OTPVerificationScreen.kt b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/OTPVerificationScreen.kt index cf4fdc0..68bb9ea 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/OTPVerificationScreen.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/OTPVerificationScreen.kt @@ -1,6 +1,5 @@ package com.auth0.universalcomponents.presentation.ui.mfa -import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement @@ -57,6 +56,7 @@ import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentEvent import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentUiState import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentViewModel import com.auth0.universalcomponents.theme.Auth0Theme +import com.auth0.universalcomponents.utils.logging.Logger @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -94,7 +94,7 @@ internal fun OTPVerificationScreen( ObserveAsEvents(viewModel.events) { event -> when (event) { is EnrollmentEvent.EnrollmentChallengeSuccess -> { - Log.d("OTPVerificationScreen", "EnrollmentChallengeSuccess not handled ") + Logger.d("OTPVerificationScreen", "EnrollmentChallengeSuccess not handled ") } is EnrollmentEvent.VerificationSuccess -> { diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/QREnrollmentScreen.kt b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/QREnrollmentScreen.kt index daef74a..6756928 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/QREnrollmentScreen.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/QREnrollmentScreen.kt @@ -1,7 +1,6 @@ package com.auth0.universalcomponents.presentation.ui.mfa import android.graphics.Bitmap -import android.util.Log import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -70,10 +69,13 @@ import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentEvent import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentUiState import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentViewModel import com.auth0.universalcomponents.theme.Auth0Theme +import com.auth0.universalcomponents.utils.logging.Logger import com.google.zxing.BarcodeFormat import com.google.zxing.WriterException import com.google.zxing.qrcode.QRCodeWriter +private const val TAG = "QREnrollmentScreen" + @Composable internal fun QREnrollmentScreen( authenticatorType: AuthenticatorType, @@ -533,7 +535,7 @@ private fun generateQRCode( val bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, size, size) createQRBitmap(bitMatrix, qrCodeColor, qrBackgroundColor) } catch (e: WriterException) { - Log.e("TAG", "Failed to generate QR code", e) + Logger.e(TAG, "Failed to generate QR code", e) null } } diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/RecoveryCodeEnrollmentScreen.kt b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/RecoveryCodeEnrollmentScreen.kt index 070cdb0..0101197 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/RecoveryCodeEnrollmentScreen.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/RecoveryCodeEnrollmentScreen.kt @@ -1,6 +1,5 @@ package com.auth0.universalcomponents.presentation.ui.mfa -import android.util.Log import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -51,6 +50,7 @@ import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentEvent import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentUiState import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentViewModel import com.auth0.universalcomponents.theme.Auth0Theme +import com.auth0.universalcomponents.utils.logging.Logger import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch @@ -88,7 +88,7 @@ internal fun RecoveryCodeEnrollmentScreen( event.authenticationMethod.id, event.authenticationMethod.type ) - Log.d("EmailEnrollmentScreen", "$event not handled ") + Logger.d("RecoveryCodeEnrollmentScreen", "$event not handled ") } } } diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/phone/PhoneEnrollmentScreen.kt b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/phone/PhoneEnrollmentScreen.kt index 8a1f2fe..9529263 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/phone/PhoneEnrollmentScreen.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/phone/PhoneEnrollmentScreen.kt @@ -1,6 +1,5 @@ package com.auth0.universalcomponents.presentation.ui.mfa.phone -import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable @@ -58,6 +57,7 @@ import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentUiState import com.auth0.universalcomponents.presentation.viewmodel.EnrollmentViewModel import com.auth0.universalcomponents.theme.Auth0Theme import com.auth0.universalcomponents.utils.ValidationUtil +import com.auth0.universalcomponents.utils.logging.Logger @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -90,7 +90,7 @@ internal fun PhoneEnrollmentScreen( } is EnrollmentEvent.VerificationSuccess -> { - Log.d("PhoneEnrollmentScreen", "$event not handled ") + Logger.d("PhoneEnrollmentScreen", "$event not handled ") } } } diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/passkeys/PasskeyViewModel.kt b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/passkeys/PasskeyViewModel.kt index cde85fd..ca712dc 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/passkeys/PasskeyViewModel.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/passkeys/PasskeyViewModel.kt @@ -1,6 +1,5 @@ package com.auth0.universalcomponents.presentation.ui.passkeys -import android.util.Log import androidx.credentials.exceptions.CreateCredentialCancellationException import androidx.credentials.exceptions.CreateCredentialException import androidx.credentials.exceptions.CreateCredentialInterruptedException @@ -12,6 +11,7 @@ import com.auth0.universalcomponents.domain.error.Auth0Error import com.auth0.universalcomponents.domain.model.PublicKeyCredentials import com.auth0.universalcomponents.domain.repository.MyAccountRepository import com.auth0.universalcomponents.presentation.ui.UiError +import com.auth0.universalcomponents.utils.logging.Logger import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -161,7 +161,7 @@ class PasskeyViewModel( } else -> { - Log.w(TAG, "Unexpected exception type ${exception::class.java.name}") + Logger.w(TAG, "Unexpected exception type ${exception::class.java.name}") Auth0Error.PasskeyError( "An error occurred when creating a passkey: ${exception.message ?: "Unknown error"}", exception, diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/viewmodel/EnrollmentViewModel.kt b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/viewmodel/EnrollmentViewModel.kt index a99edc1..f0a7941 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/viewmodel/EnrollmentViewModel.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/viewmodel/EnrollmentViewModel.kt @@ -1,6 +1,5 @@ package com.auth0.universalcomponents.presentation.viewmodel -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.auth0.android.result.AuthenticationMethod @@ -15,6 +14,7 @@ import com.auth0.universalcomponents.domain.usecase.EnrollAuthenticatorUseCase import com.auth0.universalcomponents.domain.usecase.GetUserInfoUseCase import com.auth0.universalcomponents.domain.usecase.VerifyAuthenticatorUseCase import com.auth0.universalcomponents.presentation.ui.UiError +import com.auth0.universalcomponents.utils.logging.Logger import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -84,7 +84,7 @@ class EnrollmentViewModel( } else -> { - Log.d(TAG, "No need to fetch the data during initialization") + Logger.d(TAG, "No need to fetch the data during initialization") } } } @@ -99,7 +99,7 @@ class EnrollmentViewModel( } enrollAuthenticatorUseCase(authenticatorType, input) .onSuccess { enrollmentResult -> - Log.d(TAG, "Enrollment initiated successfully") + Logger.d(TAG, "Enrollment initiated successfully") val (authMethodId, authSession) = when (enrollmentResult) { is EnrollmentResult.RecoveryCodeEnrollment -> @@ -146,7 +146,7 @@ class EnrollmentViewModel( _uiState.update { it.copy(verifyingAuthenticator = true) } - Log.d(TAG, "Verifying with OTP") + Logger.d(TAG, "Verifying with OTP") val input = VerificationInput.WithOtp( authenticationMethodId = authenticationMethodId, @@ -156,7 +156,7 @@ class EnrollmentViewModel( verifyAuthenticatorUseCase(input) .onSuccess { authenticationMethod -> - Log.d(TAG, "Verification successful") + Logger.d(TAG, "Verification successful") _uiState.update { EnrollmentUiState() @@ -164,7 +164,7 @@ class EnrollmentViewModel( eventChannel.send(EnrollmentEvent.VerificationSuccess(authenticationMethod)) } .onError { error -> - Log.e(TAG, "Verification failed", error.cause) + Logger.e(TAG, "Verification failed", error.cause) when (error) { is Auth0Error.InvalidOTP -> { _uiState.update { @@ -206,7 +206,7 @@ class EnrollmentViewModel( _uiState.update { EnrollmentUiState(verifyingAuthenticator = true) } - Log.d(TAG, "Verifying without OTP") + Logger.d(TAG, "Verifying without OTP") val input = VerificationInput.WithoutOtp( authenticationMethodId = authenticationMethodId, @@ -215,14 +215,14 @@ class EnrollmentViewModel( verifyAuthenticatorUseCase(input) .onSuccess { authenticationMethod -> - Log.d(TAG, "Verification successful") + Logger.d(TAG, "Verification successful") _uiState.update { EnrollmentUiState() } eventChannel.send(EnrollmentEvent.VerificationSuccess(authenticationMethod)) } .onError { error -> - Log.e(TAG, "Verification failed", error.cause) + Logger.e(TAG, "Verification failed", error.cause) _uiState.update { EnrollmentUiState( uiError = UiError( @@ -239,11 +239,11 @@ class EnrollmentViewModel( viewModelScope.launch { getUserInfoUseCase() .onSuccess { userInfo -> - Log.d(TAG, "Email prefill succeeded") + Logger.d(TAG, "Email prefill succeeded") _uiState.update { it.copy(prefillEmail = userInfo.email.orEmpty()) } } .onError { error -> - Log.w(TAG, "Could not prefill email: ${error.message}") + Logger.w(TAG, "Could not prefill email: ${error.message}") } } } diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/utils/logging/Logger.kt b/universal_components/src/main/java/com/auth0/universalcomponents/utils/logging/Logger.kt new file mode 100644 index 0000000..8f44dd6 --- /dev/null +++ b/universal_components/src/main/java/com/auth0/universalcomponents/utils/logging/Logger.kt @@ -0,0 +1,76 @@ +package com.auth0.universalcomponents.utils.logging + +import android.util.Log + +/** + * Lightweight, SDK-internal logging facade. + * + * All diagnostic logging inside the SDK should go through this object instead of calling + * [android.util.Log] directly. Routing every log statement through a single choke point lets us + * decide *in one place* whether logs are emitted, which is important because this is a published + * library: consumers ship our compiled artifact inside their own apps and we must not leak internal + * diagnostics (tokens, sessions, enrollment details) into their production Logcat by default. + * + * ### Usage + * ``` + * Logger.d(TAG, "Fetching new token for audience: $audience") + * Logger.e(TAG, "Verification failed", throwable) + * ``` + * ### Enabling output + * Logging is **off by default** so a released SDK is silent. The switch is flipped only through the + * SDK-internal [setEnabled]; it is intentionally **not yet wired to any public initialization + * option** — the seam exists so a future change can decide how diagnostics get turned on without + * touching call sites. + * + */ +public object Logger { + + /** + * Master switch for all SDK logging. + * + * When `false` (the default) every logging method below is a no-op and nothing reaches Logcat. + * Mutable only from within the SDK via [setEnabled]. + */ + private var enabled: Boolean = false + + /** + * Turns SDK logging on or off. Intended to be called once during SDK initialization. + */ + internal fun setEnabled(enabled: Boolean) { + this.enabled = enabled + } + + /** Logs a debug message. No-op unless [enabled] is `true`. */ + @JvmStatic + public fun d(tag: String, message: String) { + if (enabled) Log.d(tag, message) + } + + /** Logs an info message. No-op unless [enabled] is `true`. */ + @JvmStatic + public fun i(tag: String, message: String) { + if (enabled) Log.i(tag, message) + } + + /** Logs a verbose message. No-op unless [enabled] is `true`. */ + @JvmStatic + public fun v(tag: String, message: String) { + if (enabled) Log.v(tag, message) + } + + /** + * Logs a warning, optionally with a [throwable]. No-op unless [enabled] is `true`. + */ + @JvmStatic + public fun w(tag: String, message: String, throwable: Throwable? = null) { + if (enabled) Log.w(tag, message, throwable) + } + + /** + * Logs an error, optionally with a [throwable]. No-op unless [enabled] is `true`. + */ + @JvmStatic + public fun e(tag: String, message: String, throwable: Throwable? = null) { + if (enabled) Log.e(tag, message, throwable) + } +} diff --git a/universal_components/src/test/java/com/auth0/universalcomponents/utils/logging/LoggerTest.kt b/universal_components/src/test/java/com/auth0/universalcomponents/utils/logging/LoggerTest.kt new file mode 100644 index 0000000..0f9fab7 --- /dev/null +++ b/universal_components/src/test/java/com/auth0/universalcomponents/utils/logging/LoggerTest.kt @@ -0,0 +1,77 @@ +package com.auth0.universalcomponents.utils.logging + +import android.util.Log +import io.mockk.every +import io.mockk.mockkStatic +import io.mockk.unmockkStatic +import io.mockk.verify +import org.junit.After +import org.junit.Before +import org.junit.Test + +class LoggerTest { + + private val tag = "TestTag" + private val message = "test message" + + @Before + fun setup() { + mockkStatic(Log::class) + every { Log.d(any(), any()) } returns 0 + every { Log.i(any(), any()) } returns 0 + every { Log.v(any(), any()) } returns 0 + every { Log.w(any(), any(), any()) } returns 0 + every { Log.e(any(), any(), any()) } returns 0 + } + + @After + fun tearDown() { + Logger.setEnabled(false) + unmockkStatic(Log::class) + } + + @Test + fun `d - when disabled - does not call Log`() { + Logger.setEnabled(false) + Logger.d(tag, message) + verify(exactly = 0) { Log.d(any(), any()) } + } + + @Test + fun `d - when enabled - delegates to Log`() { + Logger.setEnabled(true) + Logger.d(tag, message) + verify(exactly = 1) { Log.d(tag, message) } + } + + @Test + fun `e - when enabled - delegates to Log with throwable`() { + Logger.setEnabled(true) + val throwable = RuntimeException("boom") + Logger.e(tag, message, throwable) + verify(exactly = 1) { Log.e(tag, message, throwable) } + } + + @Test + fun `e - when disabled - does not call Log`() { + Logger.setEnabled(false) + Logger.e(tag, message, RuntimeException("boom")) + verify(exactly = 0) { Log.e(any(), any(), any()) } + } + + @Test + fun `w - when enabled - delegates to Log`() { + Logger.setEnabled(true) + Logger.w(tag, message) + verify(exactly = 1) { Log.w(tag, message, null) } + } + + @Test + fun `i and v - when enabled - delegate to Log`() { + Logger.setEnabled(true) + Logger.i(tag, message) + Logger.v(tag, message) + verify(exactly = 1) { Log.i(tag, message) } + verify(exactly = 1) { Log.v(tag, message) } + } +} From 4f5984b97773efbcbb4c19349012dbef4d9d7ab0 Mon Sep 17 00:00:00 2001 From: Manmohan Gupta Date: Thu, 25 Jun 2026 15:37:50 +0530 Subject: [PATCH 2/3] Resolved PR feedback & added rules for Logger in consumer file --- universal_components/consumer-rules.pro | 9 +++ .../ui/mfa/EmailEnrollmentScreen.kt | 2 +- .../ui/mfa/RecoveryCodeEnrollmentScreen.kt | 2 +- .../ui/mfa/phone/PhoneEnrollmentScreen.kt | 2 +- .../utils/logging/Logger.kt | 74 ++++++------------- .../utils/logging/LoggerTest.kt | 44 ++++------- 6 files changed, 50 insertions(+), 83 deletions(-) diff --git a/universal_components/consumer-rules.pro b/universal_components/consumer-rules.pro index c1a4012..40a5e2e 100644 --- a/universal_components/consumer-rules.pro +++ b/universal_components/consumer-rules.pro @@ -21,4 +21,13 @@ -keep public enum com.auth0.universalcomponents.domain.model.AuthenticatorType { **[] $VALUES; public *; +} + +# --- Strip SDK internal logging from minified consumer builds ---------------- +-assumenosideeffects class com.auth0.universalcomponents.utils.logging.Logger { + void d$*(...); + void i$*(...); + void v$*(...); + void w$*(...); + void e$*(...); } \ No newline at end of file diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/EmailEnrollmentScreen.kt b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/EmailEnrollmentScreen.kt index 78a0aef..03cfe83 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/EmailEnrollmentScreen.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/EmailEnrollmentScreen.kt @@ -80,7 +80,7 @@ internal fun EmailEnrollmentScreen( } is EnrollmentEvent.VerificationSuccess -> { - Logger.d("EmailEnrollmentScreen", "$event not handled ") + Logger.d("EmailEnrollmentScreen", "${event::class.simpleName} not handled") } } } diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/RecoveryCodeEnrollmentScreen.kt b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/RecoveryCodeEnrollmentScreen.kt index 0101197..5341df0 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/RecoveryCodeEnrollmentScreen.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/RecoveryCodeEnrollmentScreen.kt @@ -88,7 +88,7 @@ internal fun RecoveryCodeEnrollmentScreen( event.authenticationMethod.id, event.authenticationMethod.type ) - Logger.d("RecoveryCodeEnrollmentScreen", "$event not handled ") + Logger.d("RecoveryCodeEnrollmentScreen", "${event::class.simpleName} not handled") } } } diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/phone/PhoneEnrollmentScreen.kt b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/phone/PhoneEnrollmentScreen.kt index 9529263..ffa8aa8 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/phone/PhoneEnrollmentScreen.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/ui/mfa/phone/PhoneEnrollmentScreen.kt @@ -90,7 +90,7 @@ internal fun PhoneEnrollmentScreen( } is EnrollmentEvent.VerificationSuccess -> { - Logger.d("PhoneEnrollmentScreen", "$event not handled ") + Logger.d("PhoneEnrollmentScreen", "${event::class.simpleName} not handled") } } } diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/utils/logging/Logger.kt b/universal_components/src/main/java/com/auth0/universalcomponents/utils/logging/Logger.kt index 8f44dd6..3eb02a1 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/utils/logging/Logger.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/utils/logging/Logger.kt @@ -5,72 +5,44 @@ import android.util.Log /** * Lightweight, SDK-internal logging facade. * - * All diagnostic logging inside the SDK should go through this object instead of calling - * [android.util.Log] directly. Routing every log statement through a single choke point lets us - * decide *in one place* whether logs are emitted, which is important because this is a published - * library: consumers ship our compiled artifact inside their own apps and we must not leak internal - * diagnostics (tokens, sessions, enrollment details) into their production Logcat by default. + * All diagnostic logging inside the SDK goes through this object instead of calling + * [android.util.Log] directly. Routing every log statement through a single choke point gives us + * one consistent entry point — useful for keeping tags uniform and as the natural place to add + * redaction or gating later should a privacy requirement call for it. + * + * Logged messages must never contain sensitive values (tokens, sessions, PII); callers are + * responsible for logging only non-sensitive, debug-friendly context. * * ### Usage * ``` * Logger.d(TAG, "Fetching new token for audience: $audience") * Logger.e(TAG, "Verification failed", throwable) * ``` - * ### Enabling output - * Logging is **off by default** so a released SDK is silent. The switch is flipped only through the - * SDK-internal [setEnabled]; it is intentionally **not yet wired to any public initialization - * option** — the seam exists so a future change can decide how diagnostics get turned on without - * touching call sites. - * */ -public object Logger { - - /** - * Master switch for all SDK logging. - * - * When `false` (the default) every logging method below is a no-op and nothing reaches Logcat. - * Mutable only from within the SDK via [setEnabled]. - */ - private var enabled: Boolean = false - - /** - * Turns SDK logging on or off. Intended to be called once during SDK initialization. - */ - internal fun setEnabled(enabled: Boolean) { - this.enabled = enabled - } +internal object Logger { - /** Logs a debug message. No-op unless [enabled] is `true`. */ - @JvmStatic - public fun d(tag: String, message: String) { - if (enabled) Log.d(tag, message) + /** Logs a debug message. */ + internal fun d(tag: String, message: String) { + Log.d(tag, message) } - /** Logs an info message. No-op unless [enabled] is `true`. */ - @JvmStatic - public fun i(tag: String, message: String) { - if (enabled) Log.i(tag, message) + /** Logs an info message. */ + internal fun i(tag: String, message: String) { + Log.i(tag, message) } - /** Logs a verbose message. No-op unless [enabled] is `true`. */ - @JvmStatic - public fun v(tag: String, message: String) { - if (enabled) Log.v(tag, message) + /** Logs a verbose message. */ + internal fun v(tag: String, message: String) { + Log.v(tag, message) } - /** - * Logs a warning, optionally with a [throwable]. No-op unless [enabled] is `true`. - */ - @JvmStatic - public fun w(tag: String, message: String, throwable: Throwable? = null) { - if (enabled) Log.w(tag, message, throwable) + /** Logs a warning, optionally with a [throwable]. */ + internal fun w(tag: String, message: String, throwable: Throwable? = null) { + Log.w(tag, message, throwable) } - /** - * Logs an error, optionally with a [throwable]. No-op unless [enabled] is `true`. - */ - @JvmStatic - public fun e(tag: String, message: String, throwable: Throwable? = null) { - if (enabled) Log.e(tag, message, throwable) + /** Logs an error, optionally with a [throwable]. */ + internal fun e(tag: String, message: String, throwable: Throwable? = null) { + Log.e(tag, message, throwable) } } diff --git a/universal_components/src/test/java/com/auth0/universalcomponents/utils/logging/LoggerTest.kt b/universal_components/src/test/java/com/auth0/universalcomponents/utils/logging/LoggerTest.kt index 0f9fab7..2375863 100644 --- a/universal_components/src/test/java/com/auth0/universalcomponents/utils/logging/LoggerTest.kt +++ b/universal_components/src/test/java/com/auth0/universalcomponents/utils/logging/LoggerTest.kt @@ -26,52 +26,38 @@ class LoggerTest { @After fun tearDown() { - Logger.setEnabled(false) unmockkStatic(Log::class) } @Test - fun `d - when disabled - does not call Log`() { - Logger.setEnabled(false) - Logger.d(tag, message) - verify(exactly = 0) { Log.d(any(), any()) } - } - - @Test - fun `d - when enabled - delegates to Log`() { - Logger.setEnabled(true) + fun `d - delegates to Log`() { Logger.d(tag, message) verify(exactly = 1) { Log.d(tag, message) } } @Test - fun `e - when enabled - delegates to Log with throwable`() { - Logger.setEnabled(true) - val throwable = RuntimeException("boom") - Logger.e(tag, message, throwable) - verify(exactly = 1) { Log.e(tag, message, throwable) } + fun `i - delegates to Log`() { + Logger.i(tag, message) + verify(exactly = 1) { Log.i(tag, message) } } @Test - fun `e - when disabled - does not call Log`() { - Logger.setEnabled(false) - Logger.e(tag, message, RuntimeException("boom")) - verify(exactly = 0) { Log.e(any(), any(), any()) } + fun `v - delegates to Log`() { + Logger.v(tag, message) + verify(exactly = 1) { Log.v(tag, message) } } @Test - fun `w - when enabled - delegates to Log`() { - Logger.setEnabled(true) - Logger.w(tag, message) - verify(exactly = 1) { Log.w(tag, message, null) } + fun `w - delegates to Log with throwable`() { + val throwable = RuntimeException("boom") + Logger.w(tag, message, throwable) + verify(exactly = 1) { Log.w(tag, message, throwable) } } @Test - fun `i and v - when enabled - delegate to Log`() { - Logger.setEnabled(true) - Logger.i(tag, message) - Logger.v(tag, message) - verify(exactly = 1) { Log.i(tag, message) } - verify(exactly = 1) { Log.v(tag, message) } + fun `e - delegates to Log with throwable`() { + val throwable = RuntimeException("boom") + Logger.e(tag, message, throwable) + verify(exactly = 1) { Log.e(tag, message, throwable) } } } From 8b3e4c81452d30377d901df6b563961a6491c4c5 Mon Sep 17 00:00:00 2001 From: Manmohan Gupta Date: Mon, 29 Jun 2026 12:26:50 +0530 Subject: [PATCH 3/3] Fixed Issue related to consumer file while logging as per discussion --- universal_components/consumer-rules.pro | 13 +++++++------ .../presentation/viewmodel/EnrollmentViewModel.kt | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/universal_components/consumer-rules.pro b/universal_components/consumer-rules.pro index 40a5e2e..053c5e7 100644 --- a/universal_components/consumer-rules.pro +++ b/universal_components/consumer-rules.pro @@ -23,11 +23,12 @@ public *; } -# --- Strip SDK internal logging from minified consumer builds ---------------- +# --- Strip verbose SDK logging from minified consumer builds ----------------- +# Errors (e) and warnings (w) are intentionally kept so field failures stay +# diagnosable in release builds. These three have no default args, so a single +# void d(...) matches every overload — no $default synthetic to worry about. -assumenosideeffects class com.auth0.universalcomponents.utils.logging.Logger { - void d$*(...); - void i$*(...); - void v$*(...); - void w$*(...); - void e$*(...); + void d(...); + void i(...); + void v(...); } \ No newline at end of file diff --git a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/viewmodel/EnrollmentViewModel.kt b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/viewmodel/EnrollmentViewModel.kt index f0a7941..6c42cda 100644 --- a/universal_components/src/main/java/com/auth0/universalcomponents/presentation/viewmodel/EnrollmentViewModel.kt +++ b/universal_components/src/main/java/com/auth0/universalcomponents/presentation/viewmodel/EnrollmentViewModel.kt @@ -164,7 +164,7 @@ class EnrollmentViewModel( eventChannel.send(EnrollmentEvent.VerificationSuccess(authenticationMethod)) } .onError { error -> - Logger.e(TAG, "Verification failed", error.cause) + Logger.e(TAG, "Verification failed : ${error.message}") when (error) { is Auth0Error.InvalidOTP -> { _uiState.update { @@ -222,7 +222,7 @@ class EnrollmentViewModel( eventChannel.send(EnrollmentEvent.VerificationSuccess(authenticationMethod)) } .onError { error -> - Logger.e(TAG, "Verification failed", error.cause) + Logger.e(TAG, "Verification failed : ${error.message}") _uiState.update { EnrollmentUiState( uiError = UiError(