diff --git a/build.gradle.kts b/build.gradle.kts
index 26b937554d5f37b21b72ce3877a4e19a2e5938b2..44bb786e835340b81c9c29b6c400b0293ee3e810 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -57,6 +57,7 @@ dependencies {
     implementation(project(path = ":briar-core", configuration = "default"))
     implementation(project(path = ":bramble-java", configuration = "default"))
 
+    implementation("io.github.microutils:kotlin-logging-jvm:2.0.10")
     implementation("org.slf4j:jul-to-slf4j:1.7.32")
     implementation("ch.qos.logback:logback-classic:1.2.6")
 
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt
index d4dc6985533b468657f8ae243cad406f22766a8a..30e3cc04df2c931dad9dfab31341e701d982c435 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt
@@ -2,6 +2,7 @@ package org.briarproject.briar.desktop.contact
 
 import androidx.compose.runtime.State
 import androidx.compose.runtime.mutableStateOf
+import mu.KotlinLogging
 import org.briarproject.bramble.api.connection.ConnectionRegistry
 import org.briarproject.bramble.api.contact.Contact
 import org.briarproject.bramble.api.contact.ContactId
@@ -12,7 +13,6 @@ import org.briarproject.bramble.api.event.EventBus
 import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent
 import org.briarproject.briar.desktop.conversation.ConversationMessageToBeSentEvent
-import java.util.logging.Logger
 import javax.inject.Inject
 
 class ContactListViewModel
@@ -25,7 +25,7 @@ constructor(
 ) : ContactsViewModel(contactManager, conversationManager, connectionRegistry) {
 
     companion object {
-        private val LOG = Logger.getLogger(ContactListViewModel::class.java.name)
+        private val LOG = KotlinLogging.logger {}
     }
 
     init {
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt
index dd21aece994fb70a41694d37fd00571265437888..4bb2e4a7a4584626339d769f162c241aa2fb0b03 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt
@@ -1,6 +1,7 @@
 package org.briarproject.briar.desktop.contact
 
 import androidx.compose.runtime.mutableStateListOf
+import mu.KotlinLogging
 import org.briarproject.bramble.api.connection.ConnectionRegistry
 import org.briarproject.bramble.api.contact.Contact
 import org.briarproject.bramble.api.contact.ContactId
@@ -14,7 +15,6 @@ import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent
 import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.desktop.utils.removeFirst
 import org.briarproject.briar.desktop.utils.replaceFirst
-import java.util.logging.Logger
 
 abstract class ContactsViewModel(
     protected val contactManager: ContactManager,
@@ -23,7 +23,7 @@ abstract class ContactsViewModel(
 ) : EventListener {
 
     companion object {
-        private val LOG = Logger.getLogger(ContactsViewModel::class.java.name)
+        private val LOG = KotlinLogging.logger {}
     }
 
     private val _fullContactList = mutableListOf<ContactItem>()
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactViewModel.kt
index a0b5d3ca04fac1cbf302f00fe38ad77f1deeecb7..a429601f5c37614000419c9cd1e39fb8e1312f5a 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactViewModel.kt
@@ -10,7 +10,6 @@ import org.briarproject.bramble.api.db.PendingContactExistsException
 import org.briarproject.bramble.api.identity.AuthorConstants
 import org.briarproject.bramble.util.StringUtils
 import java.security.GeneralSecurityException
-import java.util.logging.Logger
 import javax.inject.Inject
 
 class AddContactViewModel
@@ -19,10 +18,6 @@ constructor(
     private val contactManager: ContactManager,
 ) {
 
-    companion object {
-        private val LOG = Logger.getLogger(AddContactViewModel::class.java.name)
-    }
-
     private val _alias = mutableStateOf("")
     private val _remoteHandshakeLink = mutableStateOf("")
     private val _handshakeLink = mutableStateOf("")
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
index 3f4d92305fb1b8ffd1a41f3108dd9b23a47a64f3..235306c1506902c441a64229528f6e7d034788e0 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
@@ -3,6 +3,7 @@ package org.briarproject.briar.desktop.conversation
 import androidx.compose.runtime.State
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
+import mu.KotlinLogging
 import org.briarproject.bramble.api.FormatException
 import org.briarproject.bramble.api.connection.ConnectionRegistry
 import org.briarproject.bramble.api.contact.ContactId
@@ -29,10 +30,9 @@ import org.briarproject.briar.api.messaging.PrivateMessage
 import org.briarproject.briar.api.messaging.PrivateMessageFactory
 import org.briarproject.briar.api.messaging.PrivateMessageHeader
 import org.briarproject.briar.desktop.contact.ContactItem
+import org.briarproject.briar.desktop.utils.KLoggerUtils.logDuration
 import org.briarproject.briar.desktop.utils.replaceIf
 import java.util.Date
-import java.util.logging.Level
-import java.util.logging.Logger
 import javax.inject.Inject
 
 class ConversationViewModel
@@ -47,7 +47,7 @@ constructor(
 ) : EventListener {
 
     companion object {
-        private val LOG = Logger.getLogger(ConversationViewModel::class.java.name)
+        private val LOG = KotlinLogging.logger {}
     }
 
     init {
@@ -91,7 +91,7 @@ constructor(
             val start = LogUtils.now()
             val m = createMessage(text)
             messagingManager.addLocalMessage(m)
-            LogUtils.logDuration(LOG, "Storing message", start)
+            LOG.logDuration("Storing message", start)
 
             val message = m.message
             val h = PrivateMessageHeader(
@@ -103,9 +103,9 @@ constructor(
             _messages.add(0, messageHeaderToItem(h))
             eventBus.broadcast(ConversationMessageToBeSentEvent(h, _contactId.value!!))
         } catch (e: UnexpectedTimerException) {
-            LogUtils.logException(LOG, Level.WARNING, e)
+            LOG.warn(e) {}
         } catch (e: DbException) {
-            LogUtils.logException(LOG, Level.WARNING, e)
+            LOG.warn(e) {}
         }
     }
 
@@ -128,7 +128,7 @@ constructor(
         try {
             val start = LogUtils.now()
             val headers = conversationManager.getMessageHeaders(_contactId.value!!)
-            LogUtils.logDuration(LOG, "Loading message headers", start)
+            LOG.logDuration("Loading message headers", start)
             // Sort headers by timestamp in *descending* order
             val sorted = headers.sortedByDescending { it.timestamp }
             _messages.apply {
@@ -138,12 +138,12 @@ constructor(
                     // todo: use ConversationVisitor to also display Request and Notice Messages
                     sorted.filterIsInstance<PrivateMessageHeader>().map(::messageHeaderToItem)
                 )
-                LogUtils.logDuration(LOG, "Loading messages", start)
+                LOG.logDuration("Loading messages", start)
             }
         } catch (e: NoSuchContactException) {
-            LogUtils.logException(LOG, Level.WARNING, e)
+            LOG.warn(e) {}
         } catch (e: DbException) {
-            LogUtils.logException(LOG, Level.WARNING, e)
+            LOG.warn(e) {}
         }
     }
 
@@ -153,7 +153,7 @@ constructor(
         if (h.hasText()) {
             item.text = loadMessageText(h.id)
         } else {
-            LOG.warning { "private message without text" }
+            LOG.warn { "private message without text" }
         }
         return item
     }
@@ -162,7 +162,7 @@ constructor(
         try {
             return messagingManager.getMessageText(m)
         } catch (e: DbException) {
-            LogUtils.logException(LOG, Level.WARNING, e)
+            LOG.warn(e) {}
         }
         return null
     }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt
index fda0aa64dc456a881146eeb9992d716b7f2f5a6b..6c9481af60ddd72cd810a5fe147ebe1e9ddcddbb 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt
@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.contact.ContactManager
 import org.briarproject.bramble.api.event.EventBus
 import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.desktop.contact.ContactsViewModel
-import java.util.logging.Logger
 import javax.inject.Inject
 
 class IntroductionViewModel
@@ -20,10 +19,6 @@ constructor(
     eventBus: EventBus,
 ) : ContactsViewModel(contactManager, conversationManager, connectionRegistry) {
 
-    companion object {
-        private val LOG = Logger.getLogger(IntroductionViewModel::class.java.name)
-    }
-
     init {
         // todo: where/when to remove listener again?
         eventBus.addListener(this)
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
index 70ffee14b4374f0339cf097ab8b8e5a85d16f6d7..3bcf10712d758790b262557f172016fb2e42ef18 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
@@ -21,7 +21,6 @@ import org.briarproject.briar.desktop.navigation.SidebarViewModel
 import org.briarproject.briar.desktop.theme.BriarTheme
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 import java.awt.Dimension
-import java.util.logging.Logger
 import javax.annotation.concurrent.Immutable
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -56,10 +55,6 @@ constructor(
     private val lifecycleManager: LifecycleManager,
 ) : BriarUi {
 
-    companion object {
-        private val LOG = Logger.getLogger(BriarUiImpl::class.java.name)
-    }
-
     override fun stop() {
         // TODO: check how briar is doing this
         if (lifecycleManager.lifecycleState == RUNNING) {
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/utils/KLoggerUtils.kt b/src/main/kotlin/org/briarproject/briar/desktop/utils/KLoggerUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..10eb1c536e78eb9d6f8aa01a86308a87ebe0932e
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/utils/KLoggerUtils.kt
@@ -0,0 +1,12 @@
+package org.briarproject.briar.desktop.utils
+
+import mu.KLogger
+import org.briarproject.bramble.util.LogUtils
+
+object KLoggerUtils {
+
+    fun KLogger.logDuration(task: String, start: Long) {
+        val duration = LogUtils.now() - start
+        debug { "$task took $duration ms" }
+    }
+}
diff --git a/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt b/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt
index c8daac93e51b6ad1aaf4ba9ef5624ba220f34b39..f13ba8e95bfc44c8e2cac80b366495433101c21d 100644
--- a/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt
+++ b/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt
@@ -2,17 +2,17 @@ package org.briarproject.briar.desktop
 
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.window.application
+import mu.KotlinLogging
 import org.briarproject.bramble.BrambleCoreEagerSingletons
 import org.briarproject.briar.BriarCoreEagerSingletons
 import org.briarproject.briar.desktop.TestUtils.getDataDir
 import java.util.logging.Level.INFO
 import java.util.logging.LogManager
-import java.util.logging.Logger
 
 internal class RunWithTemporaryAccount(val customization: BriarDesktopTestApp.() -> Unit) {
 
     companion object {
-        private val LOG = Logger.getLogger(RunWithTemporaryAccount::class.java.name)
+        private val LOG = KotlinLogging.logger {}
     }
 
     @OptIn(ExperimentalComposeUiApi::class)
@@ -20,7 +20,7 @@ internal class RunWithTemporaryAccount(val customization: BriarDesktopTestApp.()
         LogManager.getLogManager().getLogger("").level = INFO
 
         val dataDir = getDataDir()
-        LOG.info("Using data directory '$dataDir'")
+        LOG.info { "Using data directory '$dataDir'" }
 
         val app =
             DaggerBriarDesktopTestApp.builder().desktopTestModule(
@@ -28,7 +28,7 @@ internal class RunWithTemporaryAccount(val customization: BriarDesktopTestApp.()
             ).build()
 
         app.getShutdownManager().addShutdownHook {
-            LOG.info("deleting temporary account at $dataDir")
+            LOG.info { "deleting temporary account at $dataDir" }
             org.apache.commons.io.FileUtils.deleteDirectory(dataDir.toFile())
         }
 
diff --git a/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt b/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt
index 52f09dbc7afc7cd7f963ea146bc590ff2cd0f2e4..aa4b0689c01dd386466b8522470802fcaade1b45 100644
--- a/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt
+++ b/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt
@@ -1,5 +1,6 @@
 package org.briarproject.briar.desktop.testdata
 
+import mu.KotlinLogging
 import org.briarproject.bramble.api.FormatException
 import org.briarproject.bramble.api.contact.Contact
 import org.briarproject.bramble.api.contact.ContactId
@@ -24,7 +25,6 @@ import org.briarproject.bramble.api.sync.GroupFactory
 import org.briarproject.bramble.api.sync.GroupId
 import org.briarproject.bramble.api.sync.Message
 import org.briarproject.bramble.api.system.Clock
-import org.briarproject.bramble.util.LogUtils
 import org.briarproject.briar.api.autodelete.AutoDeleteConstants
 import org.briarproject.briar.api.avatar.AvatarManager
 import org.briarproject.briar.api.avatar.AvatarMessageEncoder
@@ -37,8 +37,6 @@ import java.time.ZoneOffset
 import java.util.Random
 import java.util.UUID
 import java.util.concurrent.Executor
-import java.util.logging.Level
-import java.util.logging.Logger
 import javax.inject.Inject
 import kotlin.math.min
 
@@ -58,7 +56,7 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
 ) : DeterministicTestDataCreator {
 
     companion object {
-        private val LOG = Logger.getLogger(DeterministicTestDataCreatorImpl::class.java.name)
+        private val LOG = KotlinLogging.logger {}
     }
 
     private val random = Random()
@@ -74,7 +72,7 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
             try {
                 createTestDataOnIoExecutor(numContacts, numPrivateMsgs, avatarPercent)
             } catch (e: DbException) {
-                LogUtils.logException(LOG, Level.WARNING, e)
+                LOG.warn(e) { }
             }
         }
     }
@@ -129,9 +127,7 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
             db.getContact(txn, contactId)
         }
         if (random.nextInt(100) + 1 <= avatarPercent) addAvatar(contact)
-        if (LOG.isLoggable(Level.INFO)) {
-            LOG.info("Added contact ${remote.name} with transport properties: $props")
-        }
+        LOG.info { "Added contact ${remote.name} with transport properties: $props" }
         localAuthors[contact] = remote
         return contact
     }
@@ -242,7 +238,7 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
         val `is`: InputStream = try {
             testAvatarCreator.avatarInputStream
         } catch (e: IOException) {
-            LogUtils.logException(LOG, Level.WARNING, e)
+            LOG.warn(e) {}
             return
         } ?: return
         val m: Message = try {
@@ -282,9 +278,7 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
                 createPrivateMessage(contact.id, group.id, person.messages[k])
             }
         }
-        if (LOG.isLoggable(Level.INFO)) {
-            LOG.info("Created $numPrivateMsgs private messages per contact.")
-        }
+        LOG.info { "Created $numPrivateMsgs private messages per contact." }
     }
 
     @Throws(DbException::class)