refactor(logging): Replace receiver-derived log tags with explicit tags

The Any.log overload built its tag from this::class.java.name, which turns
into CAP:a once the class is obfuscated. Every call site now passes a
logTag(...) constant and the overload is gone so it cannot be reintroduced.
This commit is contained in:
darken
2026-09-06 10:41:37 +02:00
committed by Matthias Urhahn
parent ed66731207
commit 83849efe80
14 changed files with 59 additions and 53 deletions
@@ -10,6 +10,7 @@ import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.error.HasLocalizedError
import eu.darken.capod.common.error.LocalizedError
import eu.darken.capod.common.debug.logging.logTag
class GplayServiceUnavailableException(cause: Throwable) :
BillingException("Google Play services are unavailable.", cause), HasLocalizedError {
@@ -48,10 +49,11 @@ class GplayServiceUnavailableException(cause: Throwable) :
)
private fun onLaunchFailed(e: Exception) {
log(ERROR) { "Can't launch settings intent for Google Play: $e" }
log(TAG, ERROR) { "Can't launch settings intent for Google Play: $e" }
}
companion object {
private const val GPLAY_PKG = "com.android.vending"
private val TAG = logTag("Upgrade", "Gplay", "ServiceUnavailable")
}
}
@@ -1,10 +1,13 @@
import android.content.BroadcastReceiver
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
fun BroadcastReceiver.PendingResult.finish2(): Boolean = try {
finish()
true
} catch (e: IllegalStateException) {
log { "BroadcastReceiver.PendingResult.finish() failed: $e" }
log(TAG) { "BroadcastReceiver.PendingResult.finish() failed: $e" }
false
}
}
private val TAG = logTag("Common", "BroadcastReceiver")
@@ -9,6 +9,7 @@ import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR
import eu.darken.capod.common.debug.logging.asLog
import eu.darken.capod.common.debug.logging.log
import javax.inject.Inject
import eu.darken.capod.common.debug.logging.logTag
@Reusable
class WebpageTool @Inject constructor(
@@ -25,9 +26,12 @@ class WebpageTool @Inject constructor(
context.startActivity(intent)
true
} catch (e: Exception) {
log(ERROR) { "Failed to launch: ${e.asLog()}" }
log(TAG, ERROR) { "Failed to launch: ${e.asLog()}" }
false
}
}
companion object {
private val TAG = logTag("WebpageTool")
}
}
@@ -2,6 +2,7 @@ package eu.darken.capod.common.bluetooth
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
suspend fun Collection<BleScanResult>.onlyNewAndUnique(): List<BleScanResult> = this
.groupBy { it.address }
@@ -10,7 +11,9 @@ suspend fun Collection<BleScanResult>.onlyNewAndUnique(): List<BleScanResult> =
// For each address we only want the newest result, upstream may batch data
val newest = sameAdrDevs.maxByOrNull { it.generatedAtNanos }!!
sameAdrDevs.minus(newest).let {
if (it.isNotEmpty()) log(VERBOSE) { "Discarding stale results: ${it.logSummary()}" }
if (it.isNotEmpty()) log(TAG, VERBOSE) { "Discarding stale results: ${it.logSummary()}" }
}
newest
}
private val TAG = logTag("Bluetooth", "Scanner")
@@ -6,6 +6,7 @@ import android.bluetooth.le.ScanResult
import android.os.ParcelUuid
import eu.darken.capod.common.debug.logging.asLog
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
fun BluetoothDevice.hasFeature(uuid: ParcelUuid): Boolean {
return uuids?.contains(uuid) ?: false
@@ -21,6 +22,8 @@ fun BluetoothDevice.hasFeature(uuid: ParcelUuid): Boolean {
fun ScanFilter.matchesSafe(scanResult: ScanResult): Boolean = try {
matches(scanResult)
} catch (e: NullPointerException) {
log { "AOSP error: ${e.asLog()}" }
log(TAG) { "AOSP error: ${e.asLog()}" }
false
}
}
private val TAG = logTag("Bluetooth", "Extensions")
@@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import eu.darken.capod.common.debug.logging.logTag
class DataStoreValue<T>(
private val dataStore: DataStore<Preferences>,
@@ -45,10 +46,14 @@ class DataStoreValue<T>(
prefs[key as Preferences.Key<Any>] = raw
}
}
log(VERBOSE) { "DataStoreValue($keyName) updated from $old to $new" }
log(TAG, VERBOSE) { "DataStoreValue($keyName) updated from $old to $new" }
@Suppress("UNCHECKED_CAST")
return Updated(old as T, new as T)
}
companion object {
private val TAG = logTag("DataStore", "Value")
}
}
suspend fun <T> DataStoreValue<T>.value(): T = flow.first()
@@ -34,6 +34,8 @@ object Logging {
)
}
private val TAG = logTag("Logging")
private val internalLoggers = mutableListOf<Logger>()
val loggers: List<Logger>
@@ -46,11 +48,11 @@ object Logging {
fun install(logger: Logger) {
synchronized(internalLoggers) { internalLoggers.add(logger) }
log { "Was installed $logger" }
log(TAG) { "Was installed $logger" }
}
fun remove(logger: Logger) {
log { "Removing: $logger" }
log(TAG) { "Removing: $logger" }
synchronized(internalLoggers) { internalLoggers.remove(logger) }
}
@@ -77,26 +79,11 @@ object Logging {
}
fun clearAll() {
log { "Clearing all loggers" }
log(TAG) { "Clearing all loggers" }
synchronized(internalLoggers) { internalLoggers.clear() }
}
}
inline fun Any.log(
priority: Logging.Priority = Logging.Priority.DEBUG,
metaData: Map<String, Any>? = null,
message: () -> String,
) {
if (Logging.hasReceivers) {
Logging.logInternal(
tag = "CAP:${logTagViaCallSite()}",
priority = priority,
metaData = metaData,
message = message(),
)
}
}
inline fun log(
tag: String,
priority: Logging.Priority = Logging.Priority.DEBUG,
@@ -135,15 +122,3 @@ fun Throwable.asLogSummary(): String {
private fun Throwable.safeMessage(): String? = runCatching { message }.getOrNull()
@PublishedApi
internal fun Any.logTagViaCallSite(): String {
val javaClass = this::class.java
val fullClassName = javaClass.name
val outerClassName = fullClassName.substringBefore('$')
val simplerOuterClassName = outerClassName.substringAfterLast('.')
return if (simplerOuterClassName.isEmpty()) {
fullClassName
} else {
simplerOuterClassName.removeSuffix("Kt")
}
}
@@ -25,6 +25,7 @@ import androidx.lifecycle.Observer
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
import eu.darken.capod.common.debug.logging.log
import java.util.concurrent.atomic.AtomicBoolean
import eu.darken.capod.common.debug.logging.logTag
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
@@ -46,7 +47,7 @@ class SingleLiveEvent<T> : MutableLiveData<T>() {
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
log(WARN) { "Multiple observers registered but only one will be notified of changes." }
log(TAG, WARN) { "Multiple observers registered but only one will be notified of changes." }
}
// Observe the internal MutableLiveData
@@ -73,4 +74,8 @@ class SingleLiveEvent<T> : MutableLiveData<T>() {
fun call() {
value = null
}
companion object {
private val TAG = logTag("SingleLiveEvent")
}
}
@@ -42,7 +42,7 @@ abstract class ViewModel2(
if (this is ErrorEventSource) {
return CoroutineExceptionHandler { _, ex ->
log(WARN) { "Error during launch: ${ex.asLog()}" }
log(TAG, WARN) { "Error during launch: ${ex.asLog()}" }
errorEvents.postValue(ex)
}
}
@@ -34,7 +34,7 @@ class BluetoothEventReceiver : BroadcastReceiver() {
log(TAG, WARN) { "Event without Bluetooth device association." }
return
} else {
log { "Event related to $bluetoothDevice" }
log(TAG) { "Event related to $bluetoothDevice" }
}
val supportedFeatures = try {
ContinuityProtocol.BLE_FEATURE_UUIDS.filter { bluetoothDevice.hasFeature(it) }
@@ -47,7 +47,7 @@ class BluetoothEventReceiver : BroadcastReceiver() {
log(TAG) { "Device has no features we support." }
return
} else {
log { "Device has the following we features we support $supportedFeatures" }
log(TAG) { "Device has the following we features we support $supportedFeatures" }
}
when (intent.action) {
@@ -8,6 +8,7 @@ import eu.darken.capod.pods.core.apple.ble.BATTERY_RESOLUTION_DECILE
import eu.darken.capod.pods.core.apple.ble.BATTERY_RESOLUTION_PERCENT
import eu.darken.capod.pods.core.apple.ble.DualBlePodSnapshot
import eu.darken.capod.pods.core.apple.ble.DualBlePodSnapshot.Pod
import eu.darken.capod.common.debug.logging.logTag
interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot, HasEarDetectionDual, HasCase,
HasDualMicrophone, HasAppleColor {
@@ -38,7 +39,7 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot,
return when (value) {
15 -> null
else -> if (value > 10) {
log { "Left pod: Above 100% battery: $value" }
log(TAG) { "Left pod: Above 100% battery: $value" }
1.0f
} else {
(value / 10f)
@@ -59,7 +60,7 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot,
return when (value) {
15 -> null
else -> if (value > 10) {
log { "Right pod: Above 100% battery: $value" }
log(TAG) { "Right pod: Above 100% battery: $value" }
1.0f
} else {
value / 10f
@@ -131,7 +132,7 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot,
return when (val value = pubCaseBattery.toInt()) {
15 -> null
else -> if (value > 10) {
log { "Case: Above 100% battery: $value" }
log(TAG) { "Case: Above 100% battery: $value" }
1.0f
} else {
value / 10f
@@ -206,3 +207,5 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualBlePodSnapshot,
}
}
private val TAG = logTag("Pod", "Apple", "DualApplePods")
@@ -3,6 +3,7 @@ package eu.darken.capod.pods.core.apple.ble.devices
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.lowerNibble
import eu.darken.capod.pods.core.apple.ble.SingleBlePodSnapshot
import eu.darken.capod.common.debug.logging.logTag
/**
* Devices that only present a single charge level, e.g. most Beats devices
@@ -17,11 +18,13 @@ interface SingleApplePods : ApplePods, SingleBlePodSnapshot, HasAppleColor {
return when (val value = pubPodsBattery.lowerNibble.toInt()) {
15 -> null
else -> if (value > 10) {
log { "Headset above 100% battery: $value" }
log(TAG) { "Headset above 100% battery: $value" }
1.0f
} else {
(value / 10f)
}
}
}
}
}
private val TAG = logTag("Pod", "Apple", "SingleApplePods")
@@ -14,7 +14,7 @@ object ProximityPairing {
class Decoder @Inject constructor() {
fun decode(message: ContinuityProtocol.Message): ProximityMessage? {
if (message.type != CONTINUITY_PROTOCOL_MESSAGE_TYPE_PROXIMITY_PAIRING) {
log { "Not a proximity pairing message: $this" }
log(TAG) { "Not a proximity pairing message: $message" }
return null
}
@@ -141,7 +141,7 @@ class DeviceProfilesRepo @Inject constructor(
if (addFirst) add(0, profile) else add(profile)
}.toList()
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
log(VERBOSE) { "Added device profile: ${profile.label}" }
log(TAG, VERBOSE) { "Added device profile: ${profile.label}" }
}
suspend fun updateProfile(profile: DeviceProfile) = mutex.withLock {
@@ -152,7 +152,7 @@ class DeviceProfilesRepo @Inject constructor(
if (it.id == profile.id) profile else it
}
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
log(VERBOSE) { "Updated device profile: ${profile.label}" }
log(TAG, VERBOSE) { "Updated device profile: ${profile.label}" }
}
/**
@@ -176,14 +176,14 @@ class DeviceProfilesRepo @Inject constructor(
checkAddressUniqueness(updated, otherProfiles)
val updatedProfiles = currentContainer.profiles.map { if (it.id == id) updated else it }
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
log(VERBOSE) { "Updated apple device profile: ${updated.label}" }
log(TAG, VERBOSE) { "Updated apple device profile: ${updated.label}" }
}
suspend fun removeProfile(profileId: ProfileId) = mutex.withLock {
val currentContainer = settings.profiles.valueBlocking
val updatedProfiles = currentContainer.profiles.filter { it.id != profileId }
settings.profiles.valueBlocking = DeviceProfilesContainer(updatedProfiles)
log(VERBOSE) { "Removed device profile with ID: $profileId" }
log(TAG, VERBOSE) { "Removed device profile with ID: $profileId" }
deviceStateCache.delete(profileId)
batteryDrainStore.delete(profileId)
}
@@ -196,7 +196,7 @@ class DeviceProfilesRepo @Inject constructor(
val byId = current.associateBy { it.id }
val reordered = orderedIds.map { byId.getValue(it) }
settings.profiles.valueBlocking = DeviceProfilesContainer(reordered)
log(VERBOSE) { "Reordered ${reordered.size} device profiles by ID" }
log(TAG, VERBOSE) { "Reordered ${reordered.size} device profiles by ID" }
}
suspend fun clear() = mutex.withLock {