From d5d6db67237641d51c4964ef6865039ae608a894 Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Fri, 15 Jun 2018 17:09:33 +0100
Subject: [PATCH] Add utility method for logging exceptions.

---
 .../bluetooth/AndroidBluetoothPlugin.java     |  5 ++-
 .../bramble/plugin/tor/TorPlugin.java         | 15 ++++----
 .../util/{TimeUtils.java => LogUtils.java}    |  7 +++-
 .../briarproject/bramble/PoliteExecutor.java  |  2 +-
 .../bramble/TimeLoggingExecutor.java          |  2 +-
 .../contact/ContactExchangeTaskImpl.java      | 13 ++++---
 .../bramble/crypto/CryptoComponentImpl.java   |  4 +-
 .../bramble/crypto/ScryptKdf.java             |  4 +-
 .../bramble/crypto/Sec1KeyParser.java         |  4 +-
 .../bramble/db/DatabaseComponentImpl.java     |  7 ++--
 .../briarproject/bramble/db/JdbcDatabase.java | 10 ++---
 .../bramble/db/Migration38_39.java            |  3 +-
 .../keyagreement/KeyAgreementConnector.java   |  3 +-
 .../keyagreement/KeyAgreementTaskImpl.java    |  7 ++--
 .../keyagreement/KeyAgreementTransport.java   |  5 ++-
 .../lifecycle/LifecycleManagerImpl.java       | 15 ++++----
 .../bramble/plugin/ConnectionManagerImpl.java | 37 ++++++++++---------
 .../bramble/plugin/PluginManagerImpl.java     | 17 +++++----
 .../briarproject/bramble/plugin/Poller.java   |  7 ++--
 .../BluetoothConnectionLimiterImpl.java       |  3 +-
 .../plugin/bluetooth/BluetoothPlugin.java     |  5 ++-
 .../bramble/plugin/file/FilePlugin.java       |  5 ++-
 .../plugin/file/FileTransportReader.java      |  3 +-
 .../plugin/file/FileTransportWriter.java      |  3 +-
 .../bramble/plugin/tcp/LanTcpPlugin.java      |  3 +-
 .../bramble/plugin/tcp/PortMapperImpl.java    |  7 ++--
 .../bramble/plugin/tcp/TcpPlugin.java         |  5 ++-
 .../reliability/ReliabilityLayerImpl.java     |  3 +-
 .../bramble/reporting/DevReporterImpl.java    |  5 ++-
 .../bramble/sync/DuplexOutgoingSession.java   |  9 +++--
 .../bramble/sync/IncomingSession.java         |  9 +++--
 .../bramble/sync/SimplexOutgoingSession.java  |  5 ++-
 .../bramble/sync/ValidationManagerImpl.java   | 22 +++++------
 .../system/LinuxSecureRandomProvider.java     |  3 +-
 .../bramble/system/LinuxSecureRandomSpi.java  |  3 +-
 .../transport/TransportKeyManagerImpl.java    |  3 +-
 .../lifecycle/WindowsShutdownManagerImpl.java |  3 +-
 .../plugin/bluetooth/JavaBluetoothPlugin.java |  3 +-
 .../bramble/plugin/modem/ModemImpl.java       |  8 ++--
 .../bramble/plugin/modem/ModemPlugin.java     | 11 +++---
 .../AndroidNotificationManagerImpl.java       |  4 +-
 .../briar/android/NetworkUsageLogger.java     |  2 +-
 .../android/ScreenFilterMonitorImpl.java      |  3 +-
 .../android/blog/BaseControllerImpl.java      | 17 ++++-----
 .../android/blog/BlogControllerImpl.java      | 14 +++----
 .../android/blog/FeedControllerImpl.java      | 12 +++---
 .../android/blog/RssFeedImportActivity.java   |  3 +-
 .../android/blog/RssFeedManageActivity.java   |  5 ++-
 .../android/blog/WriteBlogPostActivity.java   |  3 +-
 .../android/contact/ContactListFragment.java  |  7 ++--
 .../android/contact/ConversationActivity.java | 27 +++++++-------
 .../ContactSelectorControllerImpl.java        |  3 +-
 .../controller/BriarControllerImpl.java       |  7 ++--
 .../controller/ConfigControllerImpl.java      |  5 ++-
 .../android/forum/CreateForumActivity.java    |  7 ++--
 .../android/forum/ForumControllerImpl.java    |  5 ++-
 .../android/forum/ForumListFragment.java      |  9 +++--
 .../introduction/ContactChooserFragment.java  |  3 +-
 .../IntroductionMessageFragment.java          |  5 ++-
 .../android/keyagreement/CameraView.java      |  7 ++--
 .../keyagreement/KeyAgreementActivity.java    |  3 +-
 .../android/keyagreement/QrCodeUtils.java     |  3 +-
 .../keyagreement/ShowQrCodeFragment.java      |  5 ++-
 .../android/login/PasswordControllerImpl.java |  4 +-
 .../navdrawer/NavDrawerControllerImpl.java    |  8 ++--
 .../conversation/GroupControllerImpl.java     |  9 +++--
 .../creation/CreateGroupControllerImpl.java   |  9 +++--
 .../GroupInvitationControllerImpl.java        |  3 +-
 .../list/GroupListControllerImpl.java         | 11 +++---
 .../GroupMemberListControllerImpl.java        |  3 +-
 .../reveal/RevealContactsControllerImpl.java  | 14 +++----
 .../android/settings/SettingsFragment.java    | 13 ++++---
 .../sharing/BlogInvitationControllerImpl.java |  3 +-
 .../ForumInvitationControllerImpl.java        |  3 +-
 .../sharing/InvitationControllerImpl.java     |  7 ++--
 .../sharing/ShareBlogControllerImpl.java      |  6 +--
 .../sharing/ShareForumControllerImpl.java     |  6 +--
 .../sharing/SharingStatusActivity.java        |  3 +-
 .../threaded/ThreadListControllerImpl.java    | 19 +++++-----
 .../components/emoji/EmojiProvider.java       |  6 +--
 .../emoji/RecentEmojiPageModel.java           |  5 ++-
 .../securesms/util/BitmapUtil.java            |  3 +-
 .../briar/feed/FeedManagerImpl.java           | 16 +++-----
 .../IntroduceeProtocolEngine.java             |  4 +-
 .../briar/test/TestDataCreatorImpl.java       |  3 +-
 85 files changed, 327 insertions(+), 278 deletions(-)
 rename bramble-api/src/main/java/org/briarproject/bramble/util/{TimeUtils.java => LogUtils.java} (79%)

diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPlugin.java
index a6761c770e..a170cbccc0 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPlugin.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPlugin.java
@@ -38,6 +38,7 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
 import static android.bluetooth.BluetoothAdapter.STATE_OFF;
 import static android.bluetooth.BluetoothAdapter.STATE_ON;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -145,7 +146,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
 		try {
 			if (ss != null) ss.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -185,7 +186,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
 		try {
 			if (c != null) c.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
index 46a4f28ceb..9f31b1da0b 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
@@ -91,6 +91,7 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_
 import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WIFI;
 import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
 import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
 
 @MethodsNotNullByDefault
@@ -345,7 +346,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 		try {
 			if (c != null) c.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -353,7 +354,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 		try {
 			if (s != null) s.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -395,7 +396,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 				ss = new ServerSocket();
 				ss.bind(new InetSocketAddress("127.0.0.1", port));
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				tryToClose(ss);
 				return;
 			}
@@ -421,7 +422,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 		try {
 			if (ss != null) ss.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		} finally {
 			callback.transportDisabled();
 		}
@@ -440,7 +441,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 				response = controlConnection.addOnion(portLines);
 			else response = controlConnection.addOnion(privKey, portLines);
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return;
 		}
 		if (!response.containsKey(HS_ADDRESS)) {
@@ -508,7 +509,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 				controlConnection.shutdownTor("TERM");
 				controlSocket.close();
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 		wakeLock.release();
@@ -686,7 +687,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 					enableNetwork(true);
 				}
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/util/TimeUtils.java b/bramble-api/src/main/java/org/briarproject/bramble/util/LogUtils.java
similarity index 79%
rename from bramble-api/src/main/java/org/briarproject/bramble/util/TimeUtils.java
rename to bramble-api/src/main/java/org/briarproject/bramble/util/LogUtils.java
index 06cc6e4b23..bc1a1a2178 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/util/TimeUtils.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/util/LogUtils.java
@@ -1,10 +1,11 @@
 package org.briarproject.bramble.util;
 
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import static java.util.logging.Level.FINE;
 
-public class TimeUtils {
+public class LogUtils {
 
 	private static final int NANOS_PER_MILLI = 1000 * 1000;
 
@@ -28,4 +29,8 @@ public class TimeUtils {
 			logger.fine(task + " took " + duration + " ms");
 		}
 	}
+
+	public static void logException(Logger logger, Level level, Throwable t) {
+		if (logger.isLoggable(level)) logger.log(level, t.toString(), t);
+	}
 }
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/PoliteExecutor.java b/bramble-core/src/main/java/org/briarproject/bramble/PoliteExecutor.java
index 2ab5e032df..152210598a 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/PoliteExecutor.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/PoliteExecutor.java
@@ -10,7 +10,7 @@ import java.util.logging.Logger;
 import javax.annotation.concurrent.GuardedBy;
 
 import static java.util.logging.Level.FINE;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 /**
  * An {@link Executor} that delegates its tasks to another {@link Executor}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/TimeLoggingExecutor.java b/bramble-core/src/main/java/org/briarproject/bramble/TimeLoggingExecutor.java
index 8f5e861a8f..c786d1b960 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/TimeLoggingExecutor.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/TimeLoggingExecutor.java
@@ -9,7 +9,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.logging.Logger;
 
 import static java.util.logging.Level.FINE;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 @NotNullByDefault
 public class TimeLoggingExecutor extends ThreadPoolExecutor {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeTaskImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeTaskImpl.java
index 0a4adb9932..69a543058c 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeTaskImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeTaskImpl.java
@@ -46,6 +46,7 @@ import javax.inject.Inject;
 import static java.util.logging.Level.WARNING;
 import static org.briarproject.bramble.api.contact.RecordTypes.CONTACT_INFO;
 import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.bramble.util.ValidationUtils.checkLength;
 import static org.briarproject.bramble.util.ValidationUtils.checkSize;
 
@@ -122,7 +123,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
 			in = conn.getReader().getInputStream();
 			out = conn.getWriter().getOutputStream();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			listener.contactExchangeFailed();
 			tryToClose(conn);
 			return;
@@ -133,7 +134,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
 		try {
 			localProperties = transportPropertyManager.getLocalProperties();
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			listener.contactExchangeFailed();
 			tryToClose(conn);
 			return;
@@ -194,7 +195,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
 				LOG.info("End of stream");
 			}
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			listener.contactExchangeFailed();
 			tryToClose(conn);
 			return;
@@ -222,11 +223,11 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
 			LOG.info("Pseudonym exchange succeeded");
 			listener.contactExchangeSucceeded(remoteInfo.author);
 		} catch (ContactExistsException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			tryToClose(conn);
 			listener.duplicateContact(remoteInfo.author);
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			tryToClose(conn);
 			listener.contactExchangeFailed();
 		}
@@ -307,7 +308,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
 			conn.getReader().dispose(true, true);
 			conn.getWriter().dispose(true);
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java
index 5ca18e89b5..dc0470a6fa 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoComponentImpl.java
@@ -32,8 +32,8 @@ import javax.inject.Inject;
 
 import static java.util.logging.Level.INFO;
 import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 @NotNullByDefault
 class CryptoComponentImpl implements CryptoComponent {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/ScryptKdf.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/ScryptKdf.java
index 596cf1f7ba..2b204e9acb 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/ScryptKdf.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/ScryptKdf.java
@@ -10,8 +10,8 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.INFO;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 class ScryptKdf implements PasswordBasedKdf {
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/Sec1KeyParser.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/Sec1KeyParser.java
index 555510a7fb..a769af06d3 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/Sec1KeyParser.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/Sec1KeyParser.java
@@ -16,8 +16,8 @@ import java.util.logging.Logger;
 
 import javax.annotation.concurrent.Immutable;
 
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 /**
  * A key parser that uses the encoding defined in "SEC 1: Elliptic Curve
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
index e85b036987..f2e4de1c6f 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
@@ -74,8 +74,9 @@ import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
 import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERED;
 import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
 import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 @ThreadSafe
 @NotNullByDefault
@@ -109,7 +110,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			try {
 				close();
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 		return reopened;
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
index d2f6dff16c..baa67a29ac 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
@@ -66,6 +66,7 @@ import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
 import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
 import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
 import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 /**
  * A generic database implementation that can be used with any JDBC-compatible
@@ -404,7 +405,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 		try {
 			if (rs != null) rs.close();
 		} catch (SQLException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -412,7 +413,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 		try {
 			if (s != null) s.close();
 		} catch (SQLException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -509,12 +510,11 @@ abstract class JdbcDatabase implements Database<Connection> {
 			}
 		} catch (SQLException e) {
 			// Try to close the connection
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			try {
 				txn.close();
 			} catch (SQLException e1) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e1.toString(), e1);
+				logException(LOG, WARNING, e1);
 			}
 			// Whatever happens, allow the database to close
 			connectionsLock.lock();
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Migration38_39.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Migration38_39.java
index 2febcb1dc5..4553082342 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/Migration38_39.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Migration38_39.java
@@ -10,6 +10,7 @@ import java.util.logging.Logger;
 import javax.annotation.Nullable;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 class Migration38_39 implements Migration<Connection> {
 
@@ -48,7 +49,7 @@ class Migration38_39 implements Migration<Connection> {
 		try {
 			if (s != null) s.close();
 		} catch (SQLException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 }
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementConnector.java b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementConnector.java
index 0c24f1c9f6..e71137f7b6 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementConnector.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementConnector.java
@@ -31,6 +31,7 @@ import javax.annotation.Nullable;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.CONNECTION_TIMEOUT;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @NotNullByDefault
 class KeyAgreementConnector {
@@ -134,7 +135,7 @@ class KeyAgreementConnector {
 			Thread.currentThread().interrupt();
 			return null;
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return null;
 		} finally {
 			stopListening();
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java
index b5fc7ef7c0..5473114ee4 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java
@@ -28,6 +28,7 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -120,13 +121,11 @@ class KeyAgreementTaskImpl extends Thread implements KeyAgreementTask,
 			// Broadcast result to caller
 			eventBus.broadcast(new KeyAgreementFinishedEvent(result));
 		} catch (AbortException e) {
-			if (LOG.isLoggable(WARNING))
-				LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			// Notify caller that the protocol was aborted
 			eventBus.broadcast(new KeyAgreementAbortedEvent(e.receivedAbort));
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING))
-				LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			// Notify caller that the connection failed
 			eventBus.broadcast(new KeyAgreementFailedEvent());
 		}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTransport.java b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTransport.java
index c545fbd69e..3dddec421d 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTransport.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTransport.java
@@ -20,6 +20,7 @@ import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PR
 import static org.briarproject.bramble.api.keyagreement.RecordTypes.ABORT;
 import static org.briarproject.bramble.api.keyagreement.RecordTypes.CONFIRM;
 import static org.briarproject.bramble.api.keyagreement.RecordTypes.KEY;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 /**
  * Handles the sending and receiving of BQP records.
@@ -72,7 +73,7 @@ class KeyAgreementTransport {
 		try {
 			writeRecord(ABORT, new byte[0]);
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			exception = true;
 		}
 		tryToClose(exception);
@@ -83,7 +84,7 @@ class KeyAgreementTransport {
 			kac.getConnection().getReader().dispose(exception, true);
 			kac.getConnection().getWriter().dispose(exception);
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java
index cd09f2220f..68c694189b 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java
@@ -44,8 +44,9 @@ import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResul
 import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DB_ERROR;
 import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SERVICE_ERROR;
 import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 @ThreadSafe
 @NotNullByDefault
@@ -170,16 +171,16 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
 			eventBus.broadcast(new LifecycleEvent(RUNNING));
 			return SUCCESS;
 		} catch (DataTooOldException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return DATA_TOO_OLD_ERROR;
 		} catch (DataTooNewException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return DATA_TOO_NEW_ERROR;
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return DB_ERROR;
 		} catch (ServiceException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return SERVICE_ERROR;
 		} finally {
 			startStopSemaphore.release();
@@ -224,7 +225,7 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
 			logDuration(LOG, "Closing database", start);
 			shutdownLatch.countDown();
 		} catch (DbException | ServiceException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		} finally {
 			startStopSemaphore.release();
 		}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionManagerImpl.java
index 99574a8726..0df08372be 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionManagerImpl.java
@@ -27,6 +27,7 @@ import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
 import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 class ConnectionManagerImpl implements ConnectionManager {
 
@@ -135,7 +136,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 				byte[] tag = readTag(reader);
 				ctx = keyManager.getStreamContext(transportId, tag);
 			} catch (IOException | DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeReader(true, false);
 				return;
 			}
@@ -151,7 +152,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 				createIncomingSession(ctx, reader).run();
 				disposeReader(false, true);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeReader(true, true);
 			} finally {
 				connectionRegistry.unregisterConnection(contactId, transportId,
@@ -163,7 +164,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			try {
 				reader.dispose(exception, recognised);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 	}
@@ -188,7 +189,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			try {
 				ctx = keyManager.getStreamContext(contactId, transportId);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeWriter(true);
 				return;
 			}
@@ -204,7 +205,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 				createSimplexOutgoingSession(ctx, writer).run();
 				disposeWriter(false);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeWriter(true);
 			} finally {
 				connectionRegistry.unregisterConnection(contactId, transportId,
@@ -216,7 +217,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			try {
 				writer.dispose(exception);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 	}
@@ -246,7 +247,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 				byte[] tag = readTag(reader);
 				ctx = keyManager.getStreamContext(transportId, tag);
 			} catch (IOException | DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeReader(true, false);
 				return;
 			}
@@ -265,7 +266,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 				incomingSession.run();
 				disposeReader(false, true);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeReader(true, true);
 			} finally {
 				connectionRegistry.unregisterConnection(contactId, transportId,
@@ -279,7 +280,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			try {
 				ctx = keyManager.getStreamContext(contactId, transportId);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeWriter(true);
 				return;
 			}
@@ -294,7 +295,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 				outgoingSession.run();
 				disposeWriter(false);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeWriter(true);
 			}
 		}
@@ -305,7 +306,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			try {
 				reader.dispose(exception, recognised);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 
@@ -317,7 +318,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			try {
 				writer.dispose(exception);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 	}
@@ -347,7 +348,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			try {
 				ctx = keyManager.getStreamContext(contactId, transportId);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeWriter(true);
 				return;
 			}
@@ -364,7 +365,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 				outgoingSession.run();
 				disposeWriter(false);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeWriter(true);
 			}
 		}
@@ -376,7 +377,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 				byte[] tag = readTag(reader);
 				ctx = keyManager.getStreamContext(transportId, tag);
 			} catch (IOException | DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeReader(true, false);
 				return;
 			}
@@ -400,7 +401,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 				incomingSession.run();
 				disposeReader(false, true);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				disposeReader(true, true);
 			} finally {
 				connectionRegistry.unregisterConnection(contactId, transportId,
@@ -414,7 +415,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			try {
 				reader.dispose(exception, recognised);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 
@@ -426,7 +427,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			try {
 				writer.dispose(exception);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 	}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java
index 10ffed8508..34fecf711e 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java
@@ -52,8 +52,9 @@ import javax.inject.Inject;
 import static java.util.logging.Level.FINE;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 @ThreadSafe
 @NotNullByDefault
@@ -217,7 +218,7 @@ class PluginManagerImpl implements PluginManager, Service {
 			} catch (PluginException e) {
 				if (LOG.isLoggable(WARNING)) {
 					LOG.warning("Plugin " + plugin.getId() + " did not start");
-					LOG.log(WARNING, e.toString(), e);
+					logException(LOG, WARNING, e);
 				}
 			} finally {
 				startLatch.countDown();
@@ -257,7 +258,7 @@ class PluginManagerImpl implements PluginManager, Service {
 			} catch (PluginException e) {
 				if (LOG.isLoggable(WARNING)) {
 					LOG.warning("Plugin " + plugin.getId() + " did not stop");
-					LOG.log(WARNING, e.toString(), e);
+					logException(LOG, WARNING, e);
 				}
 			} finally {
 				stopLatch.countDown();
@@ -279,7 +280,7 @@ class PluginManagerImpl implements PluginManager, Service {
 			try {
 				return settingsManager.getSettings(id.getString());
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				return new Settings();
 			}
 		}
@@ -289,7 +290,7 @@ class PluginManagerImpl implements PluginManager, Service {
 			try {
 				return transportPropertyManager.getLocalProperties(id);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				return new TransportProperties();
 			}
 		}
@@ -299,7 +300,7 @@ class PluginManagerImpl implements PluginManager, Service {
 			try {
 				settingsManager.mergeSettings(s, id.getString());
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 
@@ -308,7 +309,7 @@ class PluginManagerImpl implements PluginManager, Service {
 			try {
 				transportPropertyManager.mergeLocalProperties(id, p);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/Poller.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/Poller.java
index a089fe2032..faea2ecc7f 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/Poller.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/Poller.java
@@ -41,6 +41,7 @@ import javax.annotation.concurrent.ThreadSafe;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @ThreadSafe
 @NotNullByDefault
@@ -134,7 +135,7 @@ class Poller implements EventListener {
 				if (w != null)
 					connectionManager.manageOutgoingConnection(c, t, w);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -150,7 +151,7 @@ class Poller implements EventListener {
 				if (d != null)
 					connectionManager.manageOutgoingConnection(c, t, d);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -211,7 +212,7 @@ class Poller implements EventListener {
 			remote.keySet().removeAll(connected);
 			if (!remote.isEmpty()) p.poll(remote);
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiterImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiterImpl.java
index 99e8c3c8e7..296afdda85 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiterImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiterImpl.java
@@ -13,6 +13,7 @@ import javax.annotation.concurrent.ThreadSafe;
 
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @NotNullByDefault
 @ThreadSafe
@@ -92,7 +93,7 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
 			conn.getWriter().dispose(false);
 			conn.getReader().dispose(false, false);
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java
index 14ee2f2bf9..3a94d653c7 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java
@@ -43,6 +43,7 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENA
 import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
 import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
 import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
 
 @MethodsNotNullByDefault
@@ -169,7 +170,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
 			try {
 				ss = openServerSocket(contactConnectionsUuid);
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				return;
 			}
 			if (!isRunning() || !shouldAllowContactConnections()) {
@@ -336,7 +337,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
 		try {
 			ss = openServerSocket(uuid);
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return null;
 		}
 		if (!isRunning()) {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java
index 5aaa9f0122..57e3538664 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java
@@ -15,6 +15,7 @@ import java.util.logging.Logger;
 
 import static java.util.logging.Level.WARNING;
 import static org.briarproject.bramble.api.plugin.FileConstants.PROP_PATH;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
 
 @NotNullByDefault
@@ -51,7 +52,7 @@ abstract class FilePlugin implements SimplexPlugin {
 			FileInputStream in = new FileInputStream(file);
 			return new FileTransportReader(file, in, this);
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return null;
 		}
 	}
@@ -70,7 +71,7 @@ abstract class FilePlugin implements SimplexPlugin {
 			FileOutputStream out = new FileOutputStream(file);
 			return new FileTransportWriter(file, out, this);
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return null;
 		}
 	}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FileTransportReader.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FileTransportReader.java
index 9b88d841ec..bd1fb14e8b 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FileTransportReader.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FileTransportReader.java
@@ -9,6 +9,7 @@ import java.io.InputStream;
 import java.util.logging.Logger;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @NotNullByDefault
 class FileTransportReader implements TransportConnectionReader {
@@ -36,7 +37,7 @@ class FileTransportReader implements TransportConnectionReader {
 		try {
 			in.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 		plugin.readerFinished(file, exception, recognised);
 	}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FileTransportWriter.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FileTransportWriter.java
index 92eb26541d..91b2cdf01d 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FileTransportWriter.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FileTransportWriter.java
@@ -9,6 +9,7 @@ import java.io.OutputStream;
 import java.util.logging.Logger;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @NotNullByDefault
 class FileTransportWriter implements TransportConnectionWriter {
@@ -46,7 +47,7 @@ class FileTransportWriter implements TransportConnectionWriter {
 		try {
 			out.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 		plugin.writerFinished(file, exception);
 	}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java
index 192a43c3d9..28bcab1487 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java
@@ -35,6 +35,7 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
 import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS;
 import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS;
 import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
 
 @NotNullByDefault
@@ -295,7 +296,7 @@ class LanTcpPlugin extends TcpPlugin {
 			try {
 				ss.close();
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 	}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java
index e40d531bdb..5845107055 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java
@@ -17,6 +17,7 @@ import javax.xml.parsers.ParserConfigurationException;
 
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.bramble.util.PrivacyUtils.scrubInetAddress;
 
 @ThreadSafe
@@ -59,7 +60,7 @@ class PortMapperImpl implements PortMapper {
 			if (externalString != null)
 				external = InetAddress.getByName(externalString);
 		} catch (IOException | SAXException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 		return new MappingResult(internal, external, port, succeeded);
 	}
@@ -76,7 +77,7 @@ class PortMapperImpl implements PortMapper {
 		try {
 			d.discover();
 		} catch (IOException | SAXException | ParserConfigurationException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 		gateway = d.getValidGateway();
 	}
@@ -87,7 +88,7 @@ class PortMapperImpl implements PortMapper {
 			if (LOG.isLoggable(INFO))
 				LOG.info("Deleted mapping for port " + port);
 		} catch (IOException | SAXException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 }
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java
index 326a837748..0dc0235a48 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java
@@ -37,6 +37,7 @@ import javax.annotation.Nullable;
 
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
 
 @MethodsNotNullByDefault
@@ -152,7 +153,7 @@ abstract class TcpPlugin implements DuplexPlugin {
 		try {
 			if (ss != null) ss.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		} finally {
 			callback.transportDisabled();
 		}
@@ -306,7 +307,7 @@ abstract class TcpPlugin implements DuplexPlugin {
 		try {
 			ifaces = Collections.list(NetworkInterface.getNetworkInterfaces());
 		} catch (SocketException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return Collections.emptyList();
 		}
 		List<InetAddress> addrs = new ArrayList<>();
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/reliability/ReliabilityLayerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/reliability/ReliabilityLayerImpl.java
index 7a26db7746..0de0e491f7 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/reliability/ReliabilityLayerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/reliability/ReliabilityLayerImpl.java
@@ -16,6 +16,7 @@ import java.util.logging.Logger;
 
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -78,7 +79,7 @@ class ReliabilityLayerImpl implements ReliabilityLayer, WriteHandler {
 				Thread.currentThread().interrupt();
 				running = false;
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				running = false;
 			}
 		});
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/reporting/DevReporterImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/reporting/DevReporterImpl.java
index aac2391e4c..6aa2110506 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/reporting/DevReporterImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/reporting/DevReporterImpl.java
@@ -32,6 +32,7 @@ import javax.inject.Inject;
 import javax.net.SocketFactory;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @Immutable
 @NotNullByDefault
@@ -132,7 +133,7 @@ class DevReporterImpl implements DevReporter, EventListener {
 		try {
 			if (c != null) c.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -140,7 +141,7 @@ class DevReporterImpl implements DevReporter, EventListener {
 		try {
 			if (s != null) s.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 }
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java
index 1461b49740..f4109a4889 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java
@@ -42,6 +42,7 @@ import static java.util.logging.Level.WARNING;
 import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING;
 import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
 import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 /**
  * An outgoing {@link SyncSession} suitable for duplex transports. The session
@@ -240,7 +241,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
 					LOG.info("Generated ack: " + (a != null));
 				if (a != null) writerTasks.add(new WriteAck(a));
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				interrupt();
 			}
 		}
@@ -287,7 +288,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
 					LOG.info("Generated batch: " + (b != null));
 				if (b != null) writerTasks.add(new WriteBatch(b));
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				interrupt();
 			}
 		}
@@ -334,7 +335,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
 					LOG.info("Generated offer: " + (o != null));
 				if (o != null) writerTasks.add(new WriteOffer(o));
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				interrupt();
 			}
 		}
@@ -379,7 +380,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
 					LOG.info("Generated request: " + (r != null));
 				if (r != null) writerTasks.add(new WriteRequest(r));
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				interrupt();
 			}
 		}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/IncomingSession.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/IncomingSession.java
index 9de8d8b8bb..93ad805e1f 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/sync/IncomingSession.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/IncomingSession.java
@@ -28,6 +28,7 @@ import javax.annotation.concurrent.ThreadSafe;
 
 import static java.util.logging.Level.WARNING;
 import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 /**
  * An incoming {@link SyncSession}.
@@ -127,7 +128,7 @@ class IncomingSession implements SyncSession, EventListener {
 					db.endTransaction(txn);
 				}
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				interrupt();
 			}
 		}
@@ -153,7 +154,7 @@ class IncomingSession implements SyncSession, EventListener {
 					db.endTransaction(txn);
 				}
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				interrupt();
 			}
 		}
@@ -179,7 +180,7 @@ class IncomingSession implements SyncSession, EventListener {
 					db.endTransaction(txn);
 				}
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				interrupt();
 			}
 		}
@@ -205,7 +206,7 @@ class IncomingSession implements SyncSession, EventListener {
 					db.endTransaction(txn);
 				}
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				interrupt();
 			}
 		}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java
index ae9bbb2619..8fbd63cc8e 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java
@@ -32,6 +32,7 @@ import static java.util.logging.Level.WARNING;
 import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING;
 import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
 import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 /**
  * An outgoing {@link SyncSession} suitable for simplex transports. The session
@@ -139,7 +140,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
 				if (a == null) decrementOutstandingQueries();
 				else writerTasks.add(new WriteAck(a));
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				interrupt();
 			}
 		}
@@ -184,7 +185,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
 				if (b == null) decrementOutstandingQueries();
 				else writerTasks.add(new WriteBatch(b));
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				interrupt();
 			}
 		}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/ValidationManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/ValidationManagerImpl.java
index b843853057..f8bb8a616b 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/sync/ValidationManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/ValidationManagerImpl.java
@@ -40,6 +40,7 @@ import static java.util.logging.Level.WARNING;
 import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERED;
 import static org.briarproject.bramble.api.sync.ValidationManager.State.INVALID;
 import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @ThreadSafe
 @NotNullByDefault
@@ -110,7 +111,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 			}
 			validateNextMessageAsync(unvalidated);
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -144,7 +145,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 			LOG.info("Group removed before validation");
 			validateNextMessageAsync(unvalidated);
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -165,7 +166,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 			}
 			deliverNextPendingMessageAsync(pending);
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -231,7 +232,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 			LOG.info("Group removed before delivery");
 			deliverNextPendingMessageAsync(pending);
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -252,8 +253,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 				storeMessageContextAsync(m, g.getClientId(),
 						g.getMajorVersion(), context);
 			} catch (InvalidMessageException e) {
-				if (LOG.isLoggable(INFO))
-					LOG.log(INFO, e.toString(), e);
+				logException(LOG, INFO, e);
 				Queue<MessageId> invalidate = new LinkedList<>();
 				invalidate.add(m.getId());
 				invalidateNextMessageAsync(invalidate);
@@ -326,7 +326,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 		} catch (NoSuchGroupException e) {
 			LOG.info("Group removed during validation");
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -377,7 +377,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 			}
 			shareNextMessageAsync(toShare);
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -412,7 +412,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 			LOG.info("Group removed before sharing");
 			shareNextMessageAsync(toShare);
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -440,7 +440,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 			LOG.info("Message removed before invalidation");
 			invalidateNextMessageAsync(invalidate);
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -492,7 +492,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 		} catch (NoSuchGroupException e) {
 			LOG.info("Group removed before validation");
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/system/LinuxSecureRandomProvider.java b/bramble-core/src/main/java/org/briarproject/bramble/system/LinuxSecureRandomProvider.java
index c63c02b053..dd55fa5e9e 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/system/LinuxSecureRandomProvider.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/system/LinuxSecureRandomProvider.java
@@ -13,6 +13,7 @@ import java.util.logging.Logger;
 import javax.annotation.concurrent.Immutable;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @Immutable
 @NotNullByDefault
@@ -49,7 +50,7 @@ class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
 			out.close();
 		} catch (IOException e) {
 			// On some devices /dev/urandom isn't writable - this isn't fatal
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/system/LinuxSecureRandomSpi.java b/bramble-core/src/main/java/org/briarproject/bramble/system/LinuxSecureRandomSpi.java
index 6b4790ada1..511afd27a7 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/system/LinuxSecureRandomSpi.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/system/LinuxSecureRandomSpi.java
@@ -10,6 +10,7 @@ import java.security.SecureRandomSpi;
 import java.util.logging.Logger;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 public class LinuxSecureRandomSpi extends SecureRandomSpi {
 
@@ -39,7 +40,7 @@ public class LinuxSecureRandomSpi extends SecureRandomSpi {
 			out.close();
 		} catch (IOException e) {
 			// On some devices /dev/urandom isn't writable - this isn't fatal
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java
index 05dffce527..5784cb3783 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java
@@ -36,6 +36,7 @@ import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOC
 import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
 import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
 import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @ThreadSafe
 @NotNullByDefault
@@ -169,7 +170,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
 					db.endTransaction(txn);
 				}
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/lifecycle/WindowsShutdownManagerImpl.java b/bramble-j2se/src/main/java/org/briarproject/bramble/lifecycle/WindowsShutdownManagerImpl.java
index da27f3ba06..2e9a7a2b3d 100644
--- a/bramble-j2se/src/main/java/org/briarproject/bramble/lifecycle/WindowsShutdownManagerImpl.java
+++ b/bramble-j2se/src/main/java/org/briarproject/bramble/lifecycle/WindowsShutdownManagerImpl.java
@@ -28,6 +28,7 @@ import javax.annotation.concurrent.ThreadSafe;
 import static com.sun.jna.Library.OPTION_FUNCTION_MAPPER;
 import static com.sun.jna.Library.OPTION_TYPE_MAPPER;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @ThreadSafe
 @NotNullByDefault
@@ -141,7 +142,7 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
 					user32.DispatchMessage(msg);
 				}
 			} catch (UnsatisfiedLinkError e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 	}
diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothPlugin.java b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothPlugin.java
index f69fab3945..c3652bfb9c 100644
--- a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothPlugin.java
+++ b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothPlugin.java
@@ -19,6 +19,7 @@ import javax.microedition.io.StreamConnection;
 import javax.microedition.io.StreamConnectionNotifier;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.bramble.util.StringUtils.isValidMac;
 
 @MethodsNotNullByDefault
@@ -85,7 +86,7 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
 		try {
 			if (ss != null) ss.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemImpl.java b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemImpl.java
index 2ba97b1e2c..c5c9cd710c 100644
--- a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemImpl.java
+++ b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemImpl.java
@@ -27,6 +27,7 @@ import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 import static jssc.SerialPort.PURGE_RXCLEAR;
 import static jssc.SerialPort.PURGE_TXCLEAR;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -136,7 +137,7 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 		try {
 			if (port != null) port.closePort();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -327,7 +328,7 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 				}
 			}
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
@@ -394,8 +395,7 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 						try {
 							answer();
 						} catch (IOException e) {
-							if (LOG.isLoggable(WARNING))
-								LOG.log(WARNING, e.toString(), e);
+							logException(LOG, WARNING, e);
 						}
 					});
 				}
diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java
index 8b7806359b..4d70fa14b0 100644
--- a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java
+++ b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java
@@ -23,6 +23,7 @@ import java.util.logging.Logger;
 
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -81,7 +82,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 				running = true;
 				return;
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 		throw new PluginException();
@@ -94,7 +95,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 			try {
 				modem.stop();
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 	}
@@ -131,7 +132,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 					LOG.info("Initialised modem on " + portName);
 				return true;
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		}
 		running = false;
@@ -157,7 +158,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 		try {
 			if (!modem.dial(number)) return null;
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			resetModem();
 			return null;
 		}
@@ -209,7 +210,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 			try {
 				modem.hangUp();
 			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				exception = true;
 			}
 			if (exception) resetModem();
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java
index a9a9a65225..c18d2a7c64 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java
@@ -70,6 +70,7 @@ import static android.os.Build.VERSION.SDK_INT;
 import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
 import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
 import static org.briarproject.briar.android.contact.ConversationActivity.CONTACT_ID;
 import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_BLOGS;
@@ -265,8 +266,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			try {
 				settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/NetworkUsageLogger.java b/briar-android/src/main/java/org/briarproject/briar/android/NetworkUsageLogger.java
index 5e700baccc..7bba1cf506 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/NetworkUsageLogger.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/NetworkUsageLogger.java
@@ -8,7 +8,7 @@ import org.briarproject.bramble.api.lifecycle.Service;
 import java.util.logging.Logger;
 
 import static java.util.logging.Level.INFO;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 class NetworkUsageLogger implements Service {
 
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java
index ebc14bde54..56d542286e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java
@@ -48,6 +48,7 @@ import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.GET_SIGNATURES;
 import static android.os.Build.VERSION.SDK_INT;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @NotNullByDefault
 class ScreenFilterMonitorImpl implements ScreenFilterMonitor, Service {
@@ -189,7 +190,7 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor, Service {
 			String publicKey = StringUtils.toHexString(publicKeyBytes);
 			return PLAY_SERVICES_PUBLIC_KEY.equals(publicKey);
 		} catch (NameNotFoundException | CertificateException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return false;
 		}
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BaseControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BaseControllerImpl.java
index 624104bb34..0ad579f555 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BaseControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BaseControllerImpl.java
@@ -33,8 +33,9 @@ import java.util.logging.Logger;
 import javax.annotation.Nullable;
 
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 import static org.briarproject.briar.util.HtmlUtils.ARTICLE;
 
 @MethodsNotNullByDefault
@@ -102,8 +103,7 @@ abstract class BaseControllerImpl extends DbControllerImpl
 				Collection<BlogPostItem> items = loadItems(groupId);
 				handler.onResult(items);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -142,8 +142,7 @@ abstract class BaseControllerImpl extends DbControllerImpl
 				logDuration(LOG, "Loading body", start);
 				handler.onResult(item);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -167,8 +166,7 @@ abstract class BaseControllerImpl extends DbControllerImpl
 				logDuration(LOG, "Loading post", start);
 				handler.onResult(item);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -184,8 +182,7 @@ abstract class BaseControllerImpl extends DbControllerImpl
 				BlogPostHeader h = item.getHeader();
 				blogManager.addLocalComment(a, b.getId(), comment, h);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogControllerImpl.java
index 017ab200aa..6b29f6f44c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogControllerImpl.java
@@ -36,8 +36,9 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -164,8 +165,7 @@ class BlogControllerImpl extends BaseControllerImpl
 				logDuration(LOG, "Loading blog", start);
 				handler.onResult(blog);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -182,8 +182,7 @@ class BlogControllerImpl extends BaseControllerImpl
 				logDuration(LOG, "Removing blog", start);
 				handler.onResult(null);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -202,8 +201,7 @@ class BlogControllerImpl extends BaseControllerImpl
 				for (Contact c : contacts) contactIds.add(c.getId());
 				handler.onResult(contactIds);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedControllerImpl.java
index 7fbdd61e55..43b5425321 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedControllerImpl.java
@@ -27,8 +27,9 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
 
 @MethodsNotNullByDefault
@@ -106,14 +107,13 @@ class FeedControllerImpl extends BaseControllerImpl
 					try {
 						posts.addAll(loadItems(b.getId()));
 					} catch (NoSuchGroupException | NoSuchMessageException e) {
-						if (LOG.isLoggable(WARNING))
-							LOG.log(WARNING, e.toString(), e);
+						logException(LOG, WARNING, e);
 					}
 				}
 				logDuration(LOG, "Loading all posts", start);
 				handler.onResult(posts);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -130,7 +130,7 @@ class FeedControllerImpl extends BaseControllerImpl
 				logDuration(LOG, "Loading personal blog", start);
 				handler.onResult(b);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java
index dafea3054e..90b91eb149 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java
@@ -30,6 +30,7 @@ import javax.inject.Inject;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 public class RssFeedImportActivity extends BriarActivity {
 
@@ -124,7 +125,7 @@ public class RssFeedImportActivity extends BriarActivity {
 				feedManager.addFeed(url);
 				feedImported();
 			} catch (DbException | IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				importFailed();
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java
index 1220f39867..feaf88354f 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java
@@ -27,6 +27,7 @@ import javax.inject.Inject;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.support.design.widget.Snackbar.LENGTH_LONG;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 public class RssFeedManageActivity extends BriarActivity
 		implements RssFeedListener {
@@ -123,7 +124,7 @@ public class RssFeedManageActivity extends BriarActivity
 			try {
 				displayFeeds(revision, feedManager.getFeeds());
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				onLoadError();
 			}
 		});
@@ -148,7 +149,7 @@ public class RssFeedManageActivity extends BriarActivity
 				feedManager.removeFeed(feed);
 				onFeedDeleted(feed);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				onDeleteError();
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java
index 7f11558e05..20c4500feb 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java
@@ -34,6 +34,7 @@ import javax.inject.Inject;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
 
 public class WriteBlogPostActivity extends BriarActivity
@@ -151,7 +152,7 @@ public class WriteBlogPostActivity extends BriarActivity
 				postPublished();
 			} catch (DbException | GeneralSecurityException
 					| FormatException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				postFailedToPublish();
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java
index 91bde1e8e0..3b32650715 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java
@@ -60,8 +60,9 @@ import javax.inject.Inject;
 import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
 import static android.support.v4.view.ViewCompat.getTransitionName;
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 import static org.briarproject.briar.android.contact.ConversationActivity.CONTACT_ID;
 
 @MethodsNotNullByDefault
@@ -212,7 +213,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
 				logDuration(LOG, "Full load", start);
 				displayContacts(revision, contacts);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ConversationActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ConversationActivity.java
index dcc91bd274..ce2e6f9b2b 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ConversationActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ConversationActivity.java
@@ -106,8 +106,9 @@ import static android.support.v7.util.SortedList.INVALID_POSITION;
 import static android.widget.Toast.LENGTH_SHORT;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRODUCTION;
 import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
 import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
@@ -304,7 +305,7 @@ public class ConversationActivity extends BriarActivity
 			} catch (NoSuchContactException e) {
 				finishOnUiThread();
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -362,7 +363,7 @@ public class ConversationActivity extends BriarActivity
 			} catch (NoSuchContactException e) {
 				finishOnUiThread();
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -441,7 +442,7 @@ public class ConversationActivity extends BriarActivity
 				logDuration(LOG, "Loading body", start);
 				displayMessageBody(m, body);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -665,7 +666,7 @@ public class ConversationActivity extends BriarActivity
 						messagingManager.getConversationId(contactId);
 				createMessage(body, timestamp);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 
 		});
@@ -697,7 +698,7 @@ public class ConversationActivity extends BriarActivity
 				bodyCache.put(message.getId(), body);
 				addConversationItem(item);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -720,7 +721,7 @@ public class ConversationActivity extends BriarActivity
 			try {
 				contactManager.removeContact(contactId);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			} finally {
 				finishAfterContactRemoved();
 			}
@@ -749,7 +750,7 @@ public class ConversationActivity extends BriarActivity
 					}
 				}
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -797,7 +798,7 @@ public class ConversationActivity extends BriarActivity
 				settings.putBoolean(SHOW_ONBOARDING_INTRODUCTION, false);
 				settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -814,7 +815,7 @@ public class ConversationActivity extends BriarActivity
 				messagingManager.setReadFlag(g, m, true);
 				logDuration(LOG, "Marking read", start);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -852,10 +853,10 @@ public class ConversationActivity extends BriarActivity
 				loadMessages();
 			} catch (ProtocolStateException e) {
 				// Action is no longer valid - reloading should solve the issue
-				if (LOG.isLoggable(INFO)) LOG.log(INFO, e.toString(), e);
+				logException(LOG, INFO, e);
 			} catch (DbException e) {
 				// TODO show an error message
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contactselection/ContactSelectorControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/contactselection/ContactSelectorControllerImpl.java
index 983918a1eb..9f6380d0fa 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contactselection/ContactSelectorControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contactselection/ContactSelectorControllerImpl.java
@@ -19,6 +19,7 @@ import java.util.logging.Logger;
 import javax.annotation.concurrent.Immutable;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @Immutable
 @NotNullByDefault
@@ -53,7 +54,7 @@ public abstract class ContactSelectorControllerImpl
 				}
 				handler.onResult(contacts);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarControllerImpl.java
index aa9861a7f6..a668a450d0 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarControllerImpl.java
@@ -21,6 +21,7 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
 import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
 
@@ -101,8 +102,7 @@ public class BriarControllerImpl implements BriarController {
 				boolean ask = settings.getBoolean(DOZE_ASK_AGAIN, true);
 				handler.onResult(ask);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -115,8 +115,7 @@ public class BriarControllerImpl implements BriarController {
 				settings.putBoolean(DOZE_ASK_AGAIN, false);
 				settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java
index c94acb8731..1e0bce3a26 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java
@@ -20,6 +20,7 @@ import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @NotNullByDefault
 public class ConfigControllerImpl implements ConfigController {
@@ -89,7 +90,7 @@ public class ConfigControllerImpl implements ConfigController {
 			reader.close();
 			return key;
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return null;
 		}
 	}
@@ -137,7 +138,7 @@ public class ConfigControllerImpl implements ConfigController {
 			LOG.info("Stored second copy of database key in backup file");
 			return true;
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return false;
 		}
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/forum/CreateForumActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/forum/CreateForumActivity.java
index 65bf9578b3..13f817642d 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/forum/CreateForumActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/forum/CreateForumActivity.java
@@ -29,8 +29,9 @@ import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 import static android.widget.Toast.LENGTH_LONG;
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
 
 @MethodsNotNullByDefault
@@ -128,7 +129,7 @@ public class CreateForumActivity extends BriarActivity {
 				logDuration(LOG, "Storing forum", start);
 				displayForum(f);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				finishOnUiThread();
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumControllerImpl.java
index 5b68f84cb9..bd28b48647 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumControllerImpl.java
@@ -39,6 +39,7 @@ import javax.inject.Inject;
 
 import static java.lang.Math.max;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @NotNullByDefault
 class ForumControllerImpl extends
@@ -130,7 +131,7 @@ class ForumControllerImpl extends
 				for (Contact c : contacts) contactIds.add(c.getId());
 				handler.onResult(contactIds);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -150,7 +151,7 @@ class ForumControllerImpl extends
 						parentItem.getId() : null;
 				createMessage(body, timestamp, parentId, author, handler);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumListFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumListFragment.java
index 8ac34e717f..66bd2aad6a 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumListFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumListFragment.java
@@ -45,8 +45,9 @@ import javax.inject.Inject;
 
 import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
 
 @MethodsNotNullByDefault
@@ -172,7 +173,7 @@ public class ForumListFragment extends BaseEventFragment implements
 				logDuration(LOG, "Full load", start);
 				displayForums(revision, forums);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -198,7 +199,7 @@ public class ForumListFragment extends BaseEventFragment implements
 				logDuration(LOG, "Loading available", start);
 				displayAvailableForums(available);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java
index 252ed6734b..a2961a61ba 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java
@@ -29,6 +29,7 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.android.contact.ConversationActivity.CONTACT_ID;
 
 public class ContactChooserFragment extends BaseFragment {
@@ -129,7 +130,7 @@ public class ContactChooserFragment extends BaseFragment {
 				}
 				displayContacts(contacts);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java
index 7a9652a610..dda025995f 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java
@@ -36,6 +36,7 @@ import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 import static android.widget.Toast.LENGTH_SHORT;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH;
 
 public class IntroductionMessageFragment extends BaseFragment
@@ -129,7 +130,7 @@ public class IntroductionMessageFragment extends BaseFragment
 				boolean possible = introductionManager.canIntroduce(c1, c2);
 				setUpViews(c1, c2, possible);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -204,7 +205,7 @@ public class IntroductionMessageFragment extends BaseFragment
 				long timestamp = System.currentTimeMillis();
 				introductionManager.makeIntroduction(c1, c2, msg, timestamp);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				introductionError();
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/CameraView.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/CameraView.java
index 814bfe8b4a..aa6ba31ac1 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/CameraView.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/CameraView.java
@@ -35,6 +35,7 @@ import static android.hardware.Camera.Parameters.SCENE_MODE_BARCODE;
 import static android.os.Build.VERSION.SDK_INT;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @SuppressWarnings("deprecation")
 @MethodsNotNullByDefault
@@ -408,7 +409,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
 			try {
 				surfaceCreatedUi(holder);
 			} catch (CameraException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -430,7 +431,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
 			try {
 				surfaceChangedUi(holder, w, h);
 			} catch (CameraException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -488,7 +489,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
 		try {
 			startAutoFocus();
 		} catch (CameraException e) {
-			LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
index ea539e0662..c64841dd30 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
@@ -54,6 +54,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Build.VERSION.SDK_INT;
 import static android.widget.Toast.LENGTH_LONG;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ENABLE_BLUETOOTH;
 import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA;
 
@@ -298,7 +299,7 @@ public class KeyAgreementActivity extends BriarActivity implements
 			try {
 				localAuthor = identityManager.getLocalAuthor();
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				contactExchangeFailed();
 				return;
 			}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/QrCodeUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/QrCodeUtils.java
index 00f1a51fa0..617f84b996 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/QrCodeUtils.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/QrCodeUtils.java
@@ -18,6 +18,7 @@ import static android.graphics.Color.BLACK;
 import static android.graphics.Color.WHITE;
 import static com.google.zxing.BarcodeFormat.QR_CODE;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @NotNullByDefault
 class QrCodeUtils {
@@ -34,7 +35,7 @@ class QrCodeUtils {
 					smallestDimen, smallestDimen);
 			return renderQrCode(encoded);
 		} catch (WriterException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return null;
 		}
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java
index aaeafd0e41..aec6c26bb7 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java
@@ -59,6 +59,7 @@ import static android.widget.LinearLayout.HORIZONTAL;
 import static android.widget.Toast.LENGTH_LONG;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -214,7 +215,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
 
 	@UiThread
 	private void logCameraExceptionAndFinish(CameraException e) {
-		if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+		logException(LOG, WARNING, e);
 		Toast.makeText(getActivity(), R.string.camera_error,
 				LENGTH_LONG).show();
 		finish();
@@ -278,7 +279,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
 		} catch (CameraException e) {
 			logCameraExceptionAndFinish(e);
 		} catch (IOException | IllegalArgumentException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, "QR Code Invalid", e);
+			LOG.log(WARNING, "QR Code Invalid", e);
 			reset();
 			Toast.makeText(getActivity(), R.string.qr_code_invalid,
 					LENGTH_LONG).show();
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
index 86833aed28..905797acc6 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
@@ -17,8 +17,8 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 @NotNullByDefault
 public class PasswordControllerImpl extends ConfigControllerImpl
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java
index 4a8e6d11ed..2f851569cb 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java
@@ -28,6 +28,7 @@ import javax.inject.Inject;
 
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
 import static org.briarproject.briar.android.TestingConstants.IS_BETA_BUILD;
 import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
@@ -143,7 +144,7 @@ public class NavDrawerControllerImpl extends DbControllerImpl
 					}
 				}
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -158,7 +159,7 @@ public class NavDrawerControllerImpl extends DbControllerImpl
 				settings.putBoolean(EXPIRY_SHOW_UPDATE, false);
 				settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -178,8 +179,7 @@ public class NavDrawerControllerImpl extends DbControllerImpl
 				boolean ask = settings.getBoolean(DOZE_ASK_AGAIN, true);
 				handler.onResult(ask);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onResult(true);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupControllerImpl.java
index f1a844cafb..97a64c1a7b 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupControllerImpl.java
@@ -42,6 +42,7 @@ import javax.inject.Inject;
 
 import static java.lang.Math.max;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -152,7 +153,7 @@ class GroupControllerImpl extends
 				}
 				handler.onResult(contactIds);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -176,7 +177,7 @@ class GroupControllerImpl extends
 				createMessage(body, timestamp, parentId, author, previousMsgId,
 						handler);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -223,7 +224,7 @@ class GroupControllerImpl extends
 				LocalAuthor author = identityManager.getLocalAuthor();
 				handler.onResult(author);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -238,7 +239,7 @@ class GroupControllerImpl extends
 						privateGroupManager.isDissolved(getGroupId());
 				handler.onResult(isDissolved);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/creation/CreateGroupControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/creation/CreateGroupControllerImpl.java
index 2c9f50531d..9791e16d5b 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/creation/CreateGroupControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/creation/CreateGroupControllerImpl.java
@@ -33,6 +33,7 @@ import javax.annotation.concurrent.Immutable;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @Immutable
 @NotNullByDefault
@@ -81,7 +82,7 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
 				LocalAuthor author = identityManager.getLocalAuthor();
 				createGroupAndMessages(author, name, handler);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -109,7 +110,7 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
 				groupManager.addPrivateGroup(group, joinMsg, true);
 				handler.onResult(group.getId());
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -136,7 +137,7 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
 				}
 				signInvitations(g, localAuthor, contacts, message, handler);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -176,7 +177,7 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
 				//noinspection ConstantConditions
 				handler.onResult(null);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/invitation/GroupInvitationControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/invitation/GroupInvitationControllerImpl.java
index 3e8c5a9415..1a2cae3ed9 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/invitation/GroupInvitationControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/invitation/GroupInvitationControllerImpl.java
@@ -22,6 +22,7 @@ import java.util.concurrent.Executor;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID;
 
 @NotNullByDefault
@@ -72,7 +73,7 @@ class GroupInvitationControllerImpl
 				ContactId c = item.getCreator().getId();
 				groupInvitationManager.respondToInvitation(c, g, accept);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListControllerImpl.java
index 89e74e8388..b568fdcef5 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListControllerImpl.java
@@ -37,8 +37,9 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID;
 
 @MethodsNotNullByDefault
@@ -165,7 +166,7 @@ class GroupListControllerImpl extends DbControllerImpl
 				logDuration(LOG, "Loading groups", start);
 				handler.onResult(items);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -179,7 +180,7 @@ class GroupListControllerImpl extends DbControllerImpl
 				groupManager.removePrivateGroup(g);
 				logDuration(LOG, "Removing group", start);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -193,7 +194,7 @@ class GroupListControllerImpl extends DbControllerImpl
 				handler.onResult(
 						groupInvitationManager.getInvitations().size());
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListControllerImpl.java
index 5dcf6c5995..92f6343ef0 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListControllerImpl.java
@@ -19,6 +19,7 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 class GroupMemberListControllerImpl extends DbControllerImpl
 		implements GroupMemberListController {
@@ -56,7 +57,7 @@ class GroupMemberListControllerImpl extends DbControllerImpl
 				}
 				handler.onResult(items);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealContactsControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealContactsControllerImpl.java
index 11b65fa437..3b257c2f0c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealContactsControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealContactsControllerImpl.java
@@ -28,6 +28,7 @@ import javax.inject.Inject;
 
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
 import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
 
@@ -65,7 +66,7 @@ class RevealContactsControllerImpl extends DbControllerImpl
 			try {
 				handler.onResult(getItems(g, selection));
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -106,7 +107,7 @@ class RevealContactsControllerImpl extends DbControllerImpl
 						settings.getBoolean(SHOW_ONBOARDING_REVEAL_CONTACTS,
 								true));
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 
@@ -121,8 +122,7 @@ class RevealContactsControllerImpl extends DbControllerImpl
 				settings.putBoolean(SHOW_ONBOARDING_REVEAL_CONTACTS, false);
 				settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -136,11 +136,9 @@ class RevealContactsControllerImpl extends DbControllerImpl
 					groupInvitationManager.revealRelationship(c, g);
 				} catch (ProtocolStateException e) {
 					// action is outdated, move to next contact
-					if (LOG.isLoggable(INFO))
-						LOG.log(INFO, e.toString(), e);
+					logException(LOG, INFO, e);
 				} catch (DbException e) {
-					if (LOG.isLoggable(WARNING))
-						LOG.log(WARNING, e.toString(), e);
+					logException(LOG, WARNING, e);
 					handler.onException(e);
 					break;
 				}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java
index b2f661289b..d9faceff50 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java
@@ -64,8 +64,9 @@ import static java.util.logging.Level.WARNING;
 import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE;
 import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
 import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_ALWAYS;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
 import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGTONE;
 import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_SIGN_OUT;
@@ -256,7 +257,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
 						PREF_TOR_NETWORK_ALWAYS);
 				displaySettings(btSetting, torSetting);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -439,7 +440,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
 				settingsManager.mergeSettings(s, TOR_NAMESPACE);
 				logDuration(LOG, "Merging settings", start);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -453,7 +454,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
 				settingsManager.mergeSettings(s, BT_NAMESPACE);
 				logDuration(LOG, "Merging settings", start);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -465,7 +466,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
 				settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
 				logDuration(LOG, "Merging settings", start);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/BlogInvitationControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/BlogInvitationControllerImpl.java
index 6cacc3c343..62e6330169 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/BlogInvitationControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/BlogInvitationControllerImpl.java
@@ -20,6 +20,7 @@ import java.util.concurrent.Executor;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
 
 @NotNullByDefault
@@ -68,7 +69,7 @@ class BlogInvitationControllerImpl
 					blogSharingManager.respondToInvitation(f, c, accept);
 				}
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/ForumInvitationControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/ForumInvitationControllerImpl.java
index 4ae8a08a11..0979afe6a8 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/ForumInvitationControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/ForumInvitationControllerImpl.java
@@ -20,6 +20,7 @@ import java.util.concurrent.Executor;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
 
 @NotNullByDefault
@@ -69,7 +70,7 @@ class ForumInvitationControllerImpl
 					forumSharingManager.respondToInvitation(f, c, accept);
 				}
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/InvitationControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/InvitationControllerImpl.java
index 895dbb382e..9391be7b9d 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/InvitationControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/InvitationControllerImpl.java
@@ -25,8 +25,9 @@ import java.util.concurrent.Executor;
 import java.util.logging.Logger;
 
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -101,7 +102,7 @@ public abstract class InvitationControllerImpl<I extends InvitationItem>
 				logDuration(LOG, "Loading invitations", start);
 				handler.onResult(invitations);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareBlogControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareBlogControllerImpl.java
index 40bd81d931..86149af67c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareBlogControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareBlogControllerImpl.java
@@ -24,6 +24,7 @@ import javax.annotation.concurrent.Immutable;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
 
 @Immutable
@@ -67,12 +68,11 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
 										.getLatestMsgTime() + 1);
 						blogSharingManager.sendInvitation(g, c, msg, time);
 					} catch (NoSuchContactException | NoSuchGroupException e) {
-						if (LOG.isLoggable(WARNING))
-							LOG.log(WARNING, e.toString(), e);
+						logException(LOG, WARNING, e);
 					}
 				}
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareForumControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareForumControllerImpl.java
index 7dd7bdf76c..90b08cd706 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareForumControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareForumControllerImpl.java
@@ -24,6 +24,7 @@ import javax.annotation.concurrent.Immutable;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
 
 @Immutable
@@ -67,12 +68,11 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
 										.getLatestMsgTime() + 1);
 						forumSharingManager.sendInvitation(g, c, msg, time);
 					} catch (NoSuchContactException | NoSuchGroupException e) {
-						if (LOG.isLoggable(WARNING))
-							LOG.log(WARNING, e.toString(), e);
+						logException(LOG, WARNING, e);
 					}
 				}
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java
index 6087e38e68..c303d4371e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java
@@ -28,6 +28,7 @@ import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -110,7 +111,7 @@ abstract class SharingStatusActivity extends BriarActivity {
 				}
 				displaySharedWith(contactItems);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListControllerImpl.java
index e2502a19d8..3eb857aa52 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListControllerImpl.java
@@ -36,8 +36,9 @@ import java.util.logging.Logger;
 
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.TimeUtils.logDuration;
-import static org.briarproject.bramble.util.TimeUtils.now;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.LogUtils.now;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -107,8 +108,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
 				try {
 					messageTracker.storeMessageId(groupId, messageId);
 				} catch (DbException e) {
-					if (LOG.isLoggable(WARNING))
-						LOG.log(WARNING, e.toString(), e);
+					logException(LOG, WARNING, e);
 				}
 			});
 		}
@@ -138,8 +138,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
 				logDuration(LOG, "Loading group", start);
 				handler.onResult(groupItem);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -172,7 +171,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
 				// Build and hand over items
 				handler.onResult(buildItems(headers));
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
@@ -199,7 +198,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
 				}
 				logDuration(LOG, "Marking read", start);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
@@ -217,7 +216,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
 				logDuration(LOG, "Storing message", start);
 				resultHandler.onResult(buildItem(header, body));
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				resultHandler.onException(e);
 			}
 		});
@@ -235,7 +234,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
 				deleteNamedGroup(groupItem);
 				logDuration(LOG, "Removing group", start);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				handler.onException(e);
 			}
 		});
diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java
index 9b6068565b..90afbdeca4 100644
--- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java
+++ b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java
@@ -41,6 +41,7 @@ import static android.graphics.PixelFormat.TRANSLUCENT;
 import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -144,8 +145,7 @@ public class EmojiProvider {
 
 			@Override
 			public void onFailure(Throwable error) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, error.toString(), error);
+				logException(LOG, WARNING, error);
 			}
 		});
 		return drawable;
@@ -291,7 +291,7 @@ public class EmojiProvider {
 					LOG.info("Loaded page " + model.getSprite());
 				return bitmap;
 			} catch (BitmapDecodingException e) {
-				LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				throw new IOException(e);
 			}
 		}
diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java
index 56d72e3a81..f47ce308fc 100644
--- a/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java
+++ b/briar-android/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java
@@ -23,6 +23,7 @@ import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
 
 @MethodsNotNullByDefault
@@ -58,7 +59,7 @@ public class RecentEmojiPageModel implements EmojiPageModel {
 			Settings settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
 			serialized = settings.get(EMOJI_LRU_PREFERENCE);
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			serialized = null;
 		}
 		return deserialize(serialized);
@@ -116,7 +117,7 @@ public class RecentEmojiPageModel implements EmojiPageModel {
 			try {
 				settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 			}
 		});
 	}
diff --git a/briar-android/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java b/briar-android/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java
index 204bedaa4b..32d924d7b2 100644
--- a/briar-android/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java
+++ b/briar-android/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java
@@ -19,6 +19,7 @@ import java.io.InputStream;
 import java.util.logging.Logger;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 public class BitmapUtil {
 
@@ -74,7 +75,7 @@ public class BitmapUtil {
 		try {
 			fis.close();
 		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 
 		if (options.outWidth == -1 || options.outHeight == -1) {
diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java
index a4d58d3614..38a374035c 100644
--- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java
@@ -63,6 +63,7 @@ import okhttp3.ResponseBody;
 
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
 import static org.briarproject.briar.api.feed.FeedConstants.FETCH_DELAY_INITIAL;
 import static org.briarproject.briar.api.feed.FeedConstants.FETCH_INTERVAL;
@@ -284,8 +285,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener,
 		try {
 			feeds = getFeeds();
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING))
-				LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return;
 		}
 
@@ -295,8 +295,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener,
 			try {
 				newFeeds.add(fetchFeed(feed));
 			} catch (IOException | DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				newFeeds.add(feed);
 			}
 		}
@@ -305,8 +304,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener,
 		try {
 			storeFeeds(newFeeds);
 		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING))
-				LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 		LOG.info("Done updating RSS feeds");
 	}
@@ -464,13 +462,11 @@ class FeedManagerImpl implements FeedManager, Client, EventListener,
 					.createBlogPost(groupId, time, null, localAuthor, body);
 			blogManager.addLocalPost(txn, post);
 		} catch (DbException | GeneralSecurityException | FormatException e) {
-			if (LOG.isLoggable(WARNING))
-				LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		} catch (IllegalArgumentException e) {
 			// yes even catch this, so we at least get a stacktrace
 			// and the executor doesn't just die a silent death
-			if (LOG.isLoggable(WARNING))
-				LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 		}
 	}
 
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
index 66c4c985e1..651bd8a973 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
@@ -40,6 +40,7 @@ import javax.annotation.concurrent.Immutable;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.api.introduction.Role.INTRODUCEE;
 import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_AUTH;
 import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_RESPONSES;
@@ -412,8 +413,7 @@ class IntroduceeProtocolEngine
 			mac = crypto.authMac(ourMacKey, s, localAuthor.getId());
 			signature = crypto.sign(ourMacKey, localAuthor.getPrivateKey());
 		} catch (GeneralSecurityException e) {
-			if (LOG.isLoggable(WARNING))
-				LOG.log(WARNING, e.toString(), e);
+			logException(LOG, WARNING, e);
 			return abort(txn, s);
 		}
 		if (s.getState() != AWAIT_AUTH) throw new AssertionError();
diff --git a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
index ad25f442e6..811f3a3510 100644
--- a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
@@ -124,8 +124,7 @@ public class TestDataCreatorImpl implements TestDataCreator {
 				createTestDataOnIoExecutor(numContacts, numPrivateMsgs,
 						numBlogPosts, numForums, numForumPosts);
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING))
-					LOG.log(WARNING, "Creating test data failed", e);
+				LOG.log(WARNING, "Creating test data failed", e);
 			}
 		});
 	}
-- 
GitLab