feat(logging): Replace Timber with Kermit for multiplatform logging (#4083)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2025-12-28 08:30:15 -06:00 committed by GitHub
parent a927481e4d
commit 0776e029f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
92 changed files with 727 additions and 957 deletions

View file

@ -21,6 +21,7 @@ import android.app.Application
import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import co.touchlab.kermit.Logger
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
@ -57,7 +58,6 @@ import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
import org.meshtastic.proto.LocalOnlyProtos.LocalConfig
import org.meshtastic.proto.MeshProtos
import org.meshtastic.proto.Portnums
import timber.log.Timber
import java.io.BufferedWriter
import java.io.FileNotFoundException
import java.io.FileWriter
@ -287,7 +287,7 @@ constructor(
}
}
} catch (ex: FileNotFoundException) {
Timber.e("Can't write file error: ${ex.message}")
Logger.e { "Can't write file error: ${ex.message}" }
}
}
}

View file

@ -75,6 +75,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.touchlab.kermit.Logger
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -96,7 +97,6 @@ import org.meshtastic.core.ui.theme.AnnotationColor
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.ui.util.showToast
import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog
import timber.log.Timber
import java.io.IOException
import java.io.OutputStreamWriter
import java.nio.charset.StandardCharsets
@ -408,7 +408,7 @@ private suspend fun exportAllLogsToUri(context: Context, targetUri: Uri, logs: L
withContext(Dispatchers.Main) { context.showToast(Res.string.debug_export_success, logs.size) }
} catch (e: IOException) {
withContext(Dispatchers.Main) { context.showToast(Res.string.debug_export_failed, e.message ?: "") }
Timber.w(e, "Error:IOException ")
Logger.w(e) { "Error:IOException" }
}
}

View file

@ -20,6 +20,7 @@ package org.meshtastic.feature.settings.debugging
import androidx.compose.runtime.Immutable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import co.touchlab.kermit.Logger
import com.google.protobuf.InvalidProtocolBufferException
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableList
@ -44,7 +45,6 @@ import org.meshtastic.proto.PaxcountProtos
import org.meshtastic.proto.Portnums.PortNum
import org.meshtastic.proto.StoreAndForwardProtos
import org.meshtastic.proto.TelemetryProtos
import timber.log.Timber
import java.text.DateFormat
import java.util.Date
import java.util.Locale
@ -237,7 +237,7 @@ constructor(
}
init {
Timber.d("DebugViewModel created")
Logger.d { "DebugViewModel created" }
viewModelScope.launch {
combine(searchManager.searchText, filterManager.filteredLogs) { searchText, logs ->
searchManager.findSearchMatches(searchText, logs)
@ -250,7 +250,7 @@ constructor(
override fun onCleared() {
super.onCleared()
Timber.d("DebugViewModel cleared")
Logger.d { "DebugViewModel cleared" }
}
private fun toUiState(databaseLogs: List<MeshLog>) = databaseLogs

View file

@ -31,6 +31,7 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.toRoute
import co.touchlab.kermit.Logger
import com.google.protobuf.MessageLite
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
@ -80,7 +81,6 @@ import org.meshtastic.proto.Portnums
import org.meshtastic.proto.config
import org.meshtastic.proto.deviceProfile
import org.meshtastic.proto.moduleConfig
import timber.log.Timber
import java.io.FileOutputStream
import javax.inject.Inject
@ -179,7 +179,7 @@ constructor(
}
.launchIn(viewModelScope)
Timber.d("RadioConfigViewModel created")
Logger.d { "RadioConfigViewModel created" }
}
private val myNodeInfo: StateFlow<MyNodeEntity?>
@ -205,7 +205,7 @@ constructor(
override fun onCleared() {
super.onCleared()
Timber.d("RadioConfigViewModel cleared")
Logger.d { "RadioConfigViewModel cleared" }
}
private fun request(destNum: Int, requestAction: suspend (IMeshService, Int, Int) -> Unit, errorMessage: String) =
@ -227,7 +227,7 @@ constructor(
}
}
} catch (ex: RemoteException) {
Timber.e("$errorMessage: ${ex.message}")
Logger.e { "$errorMessage: ${ex.message}" }
}
}
}
@ -422,7 +422,7 @@ constructor(
try {
meshService?.setFixedPosition(destNum, position)
} catch (ex: RemoteException) {
Timber.e("Set fixed position error: ${ex.message}")
Logger.e { "Set fixed position error: ${ex.message}" }
}
}
@ -436,7 +436,7 @@ constructor(
onResult(protobuf)
}
} catch (ex: Exception) {
Timber.e("Import DeviceProfile error: ${ex.message}")
Logger.e { "Import DeviceProfile error: ${ex.message}" }
sendError(ex.customMessage)
}
}
@ -452,7 +452,7 @@ constructor(
}
setResponseStateSuccess()
} catch (ex: Exception) {
Timber.e("Can't write file error: ${ex.message}")
Logger.e { "Can't write file error: ${ex.message}" }
sendError(ex.customMessage)
}
}
@ -491,7 +491,7 @@ constructor(
setResponseStateSuccess()
} catch (ex: Exception) {
val errorMessage = "Can't write security keys JSON error: ${ex.message}"
Timber.e(errorMessage)
Logger.e { errorMessage }
sendError(ex.customMessage)
}
}
@ -514,7 +514,7 @@ constructor(
try {
setChannels(channelUrl)
} catch (ex: Exception) {
Timber.e(ex, "DeviceProfile channel import error")
Logger.e(ex) { "DeviceProfile channel import error" }
sendError(ex.customMessage)
}
}
@ -656,7 +656,7 @@ constructor(
if (data?.portnumValue == Portnums.PortNum.ROUTING_APP_VALUE) {
val parsed = MeshProtos.Routing.parseFrom(data.payload)
Timber.d(debugMsg.format(parsed.errorReason.name))
Logger.d { debugMsg.format(parsed.errorReason.name) }
if (parsed.errorReason != MeshProtos.Routing.Error.NONE) {
sendError(getStringResFrom(parsed.errorReasonValue))
} else if (packet.from == destNum && route.isEmpty()) {
@ -670,7 +670,7 @@ constructor(
}
if (data?.portnumValue == Portnums.PortNum.ADMIN_APP_VALUE) {
val parsed = AdminProtos.AdminMessage.parseFrom(data.payload)
Timber.d(debugMsg.format(parsed.payloadVariantCase.name))
Logger.d { debugMsg.format(parsed.payloadVariantCase.name) }
if (destNum != packet.from) {
sendError("Unexpected sender: ${packet.from.toUInt()} instead of ${destNum.toUInt()}.")
return
@ -744,7 +744,7 @@ constructor(
incrementCompleted()
}
else -> Timber.d("No custom processing needed for ${parsed.payloadVariantCase}")
else -> Logger.d { "No custom processing needed for ${parsed.payloadVariantCase}" }
}
if (AdminRoute.entries.any { it.name == route }) {

View file

@ -44,6 +44,7 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.touchlab.kermit.Logger
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.advanced
@ -79,7 +80,6 @@ import org.meshtastic.feature.settings.util.gpioPins
import org.meshtastic.feature.settings.util.toDisplayString
import org.meshtastic.proto.copy
import org.meshtastic.proto.moduleConfig
import timber.log.Timber
import java.io.File
private const val MAX_RINGTONE_SIZE = 230
@ -116,7 +116,7 @@ fun ExternalNotificationConfigScreen(
}
}
} catch (e: Exception) {
Timber.e(e, "Error importing ringtone")
Logger.e(e) { "Error importing ringtone" }
Toast.makeText(context, "Error importing: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
@ -308,7 +308,7 @@ fun ExternalNotificationConfigScreen(
tempFile.delete()
}
} catch (e: Exception) {
Timber.e(e, "Failed to play ringtone")
Logger.e(e) { "Failed to play ringtone" }
}
},
enabled = state.connected,

View file

@ -22,6 +22,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalResources
import androidx.core.os.LocaleListCompat
import co.touchlab.kermit.Logger
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.fr_HT
@ -30,7 +31,6 @@ import org.meshtastic.core.strings.pt_BR
import org.meshtastic.core.strings.zh_CN
import org.meshtastic.core.strings.zh_TW
import org.xmlpull.v1.XmlPullParser
import timber.log.Timber
import java.util.Locale
object LanguageUtils {
@ -69,7 +69,7 @@ object LanguageUtils {
}
}
} catch (e: Exception) {
Timber.e("Error parsing locale_config.xml: ${e.message}")
Logger.e { "Error parsing locale_config.xml: ${e.message}" }
}
}
}