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 a6761c770e8927af5815f2e2bf32a83d4f4bfa33..a170cbccc02d67989685f6b6bb53eca7694a3d01 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 46a4f28ceb32c1a7815f34ee0a1ab5b6be5cbe6f..9f31b1da0b2f88f87fd89957994db7f5ea733bb8 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 06cc6e4b23aa6dc70917c4ec56ebb18e402dd299..bc1a1a21787d987144f1310e819a755474217a14 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 2ab5e032dffe46a71617bdcbca9837cef06365d2..152210598a56430a5302f907c0855f12d0377f85 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 8f5e861a8f2d1d384501bc41a0114cceb5e2f798..c786d1b9603cd2d2bc7319a97ae19d91bf80e3c1 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 0a4adb9932ba4e081d5cf790ac292026d60b01bb..69a543058c0d9f1bf1fa94f86b8a85a19a5f8ec1 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 5ca18e89b518ace58f878bcced9d0af19f6ad3c7..dc0470a6fab9641a16a5f9a277c4c0800d0afe5d 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 596cf1f7bae5c747f11b2fadb6d4c7e296d3b184..2b204e9acb2079c917de5e91c08bd2b76085f62b 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 555510a7fb87c07a2c8bd0698f1639336d1a54cc..a769af06d30024af0d00851cd0dcdd584fbce3a8 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 e85b036987d0490ec14235b62737780f7b4cef83..f2e4de1c6fff7cbd3fbc6e92f52dead50c34b458 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 d2f6dff16c4c210c7d8e5784a782dddc741cbc25..baa67a29ac6397c6c7e26b17a18feb455924c111 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 2febcb1dc506208ea6f89ad09cb3babc5313975c..4553082342961731a916f04724f510971907c619 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 0c24f1c9f69d99105ba0255feefb7fc0a1ddadfe..e71137f7b61cd25feb510578cc6eeeaf3552ff8c 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 b5fc7ef7c0fcd091b0e44f3b818481c93c2d4400..5473114ee4c52341fcbfc231c6a40cea8aaf93c4 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 c545fbd69e8c6662e2edc16c73edefa5e12fc16f..3dddec421d5173590b9362d1f949a88ba5d23d27 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 cd09f2220f9053bf7aacf8491e863fb39ce80a2b..68c694189b663a9fda79f5614f428ef876e74640 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 99574a872665475932f4c3e6d7bbbacea0568a1b..0df08372be73bbdb38dde21313140ce6f9b8e2ab 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 10ffed8508440cf62e2611766ffb3320b445be8c..34fecf711e163f1ddc4057c5b42bc466c12f0390 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 a089fe20328c1cbfd12d8ecde610b4a38be3f958..faea2ecc7f88e6596c9cde65d3e5e902d8569120 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 99e8c3c8e7213b0331dacd0fc9a4cd77eca2171d..296afdda851d42700d6096308711ee28d2fd28aa 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 14ee2f2bf95b1e54105e0ff9c5099de412e696c3..3a94d653c71c1ea9da6e885a3356c7e3334013a3 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 5aaa9f012214af648b942a71181c410630f631b8..57e3538664e9db093225fd3fc1e6f30af03424c6 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 9b88d841ecfdf3c7a55d3fe118e7b3107b5dd23b..bd1fb14e8bd6baa7525aabfc40ef0e8878c84bfc 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 92eb26541ded5beda0cbfeb7e46fce4eab3b0978..91b2cdf01d21fe17d2a84c9d0130bb04e3adaaee 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 192a43c3d9ad66500a3ebd54d0c33710eaa977ed..28bcab1487b8bfcfbd2f652e2f53f8258fa6b099 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 e40d531bdbe7107ec6a02cae767da510ee4af990..58451070552bf2e9a2478c19a4a150978d6d585d 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 326a8377484f06f708978b46fef7fc27abf78fe6..0dc0235a486f350b4646715868ef412301fda3ef 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 7a26db774683ffec836afd53d3403660e80cc740..0de0e491f7b22369b50b4f9976265b9b6fa6008b 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 aac2391e4c3ac6c35843cc1586436fef6a7bda08..6aa2110506fc94bce54128113ac069bb83d775e0 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 1461b497404ede447974c1fbec747dc3dc23525e..f4109a48899f06af331f454931d1d21b1ef162ec 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 9de8d8b8bb44171e8782a5b5c1b2a52dea7a2652..93ad805e1fdf11f68ea9ac9318dc26399062111c 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 ae9bbb2619d32af94c5fe03736d218dee2bf694c..8fbd63cc8e499c991cbc6e1625826d7c761c0efe 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 b8438530579997fc9cad4a33bec8633785dfc502..f8bb8a616b7dc3fc96471da70e2a64dc04f2c76d 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 c63c02b053a7e990ceb609936ec3d8f03bb26e8e..dd55fa5e9e3a92e603bc9b8502ae7c3df14bca97 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 6b4790ada1bf520a1214d2f4c535d49bc0bef298..511afd27a779dc424a205ebddb24a6002e225d77 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 05dffce52713890c31050938ba248f90258f9dd2..5784cb37830c70d418bc9a649979a3bcf1c824d7 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 da27f3ba06a4c40d4f414e91012874918238c4f1..2e9a7a2b3d42ebe01b686d35e31b4e77655ec3ba 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 f69fab3945c6dbc55fc6fa875cb21e5f62ccf7d1..c3652bfb9c93f91c891591dbaaaac0e9a758def2 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 2ba97b1e2c5a22b8aac0562467d8eea5ba078751..c5c9cd710c2a92e1e698c6676b562471be6aa9b5 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 8b7806359ba7cf9f950582a39a7c176f64d2daf1..4d70fa14b04ba3cd4e21be4bf8d1aefff2a7a594 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 a9a9a6522568f5106305f743efa90aec25f4ea64..c18d2a7c641b846534d351f09669b4345854c27b 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 5e700baccc9c4bdf7e296be0e221a19eacb0412b..7bba1cf50654bb674ad98a1998c60d063792ebe7 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 ebc14bde54334bcb34103f92fd5bc3315c64e663..56d542286ef386392ac273f2d34f3e357fb60d09 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 624104bb34a77f806b9dce12958a370971debf2b..0ad579f5555c48ba93d534c12e62919723f7f753 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 017ab200aa4f11537128fe3a6a642772e9d3c48b..6b29f6f44c47d72c4123c32dd9904daa55941edb 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 7fbdd61e55a52a525d9e50f6b8c027a4656059ae..43b5425321e7e422ef8e8f9168a9b668fe2f39d0 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 dafea3054e097daf2e303509056b6adae8ddcca2..90b91eb149e53eb5296e00817d0d1d6705543542 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 1220f39867976f916cc82ee2c64cd2c8fb98ea2e..feaf88354fdbc4d577b08a210410b5dcd47b6051 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 7f11558e051a4d59f9ffe302c5376c6adb2b4872..20c4500febf8cfdc5482e13c70b2736352c1a102 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 91bde1e8e082a7ede7b2560ab75cd85c401fa6ad..3b32650715dd7bcdea74db8ba3bfd0358059d0e0 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 dcc91bd274a047a7b48f1b634b805881b91307d1..ce2e6f9b2b1ac637775433c23bc8c19228236179 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 983918a1ebefc318533ef4119537814ce37a09c7..9f6380d0faeb96b9c4313fd42a1645ca2d757705 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 aa9861a7f64da4e8bbaaecf9960fa08201a9519e..a668a450d0b389652ce8ecb88a177ccce2d2014e 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 c94acb8731488b29f13c0568b684f3352b1462d9..1e0bce3a26aeb5a027bf60c4c3af3256754402b8 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 65bf9578b3211a37e7eed810e551b23ec7ae56b7..13f817642d1873eb956de5ba76797bc4311d4989 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 5b68f84cb95e6fcc21234625a913bed43d48ad81..bd28b48647077dd18cf6bf6e5f8efad08c08ea96 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 8ac34e717fe59c3b5e964b4347e6b530987cfe24..66bd2aad6a89e276a0f7963a0d0e787750d63a0c 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 252ed6734b804decfa165b11b3e612d19d3badc8..a2961a61ba009fe673ea434e1e88daca5ed76002 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 7a9652a610f22ff4a994655458cdfbf33ff6cf3d..dda025995f06ffd78483c848bf564160ebee1e87 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 814bfe8b4a5594132cea1007db3bff04383dc4db..aa6ba31ac18a06883559b3ad94e8be906b62bb29 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 ea539e0662adc0fb45607a86ef668228409e0719..c64841dd30cd6b52b310ad9975dda3d6a9cfe595 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 00f1a51fa0875336605d09420bd4ec07ccf4e1e9..617f84b996ef44fd7edbd68be76594970b261ce0 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 aaeafd0e41da95de588e6bb8ef21e57465c5ddac..aec6c26bb7ef34203af96f21d73882fa9f4393ed 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 86833aed28e5af626e42061c9174e4e002f329a8..905797acc6eae94d09f1686da63db1dccacfc084 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 4a8e6d11ed5af8f760392ef994bd46b5a1f7a134..2f851569cb4555fa994f405b11cdaf78c6f479b7 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 f1a844cafbbb29f6fc5d53868114f0722c8ba54f..97a64c1a7bdd32fe9825efc6bfd70d11f846f638 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 2c9f50531de25b7a28c3b66cf121efe0a250a0d2..9791e16d5befc1e607344a4d39b3fbfdd1f474b1 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 3e8c5a9415385023dc07188a3e93a940ae7d0e59..1a2cae3ed9a6927183e6c0a6cddfb00279dcee0e 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 89e74e8388f8181f3ed19b7686ffbee750a2eb6b..b568fdcef5a431655e814ec82d9ccb1ac13376f4 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 5dcf6c5995d8338a5ee5fa36a88e0c3e3f835223..92f6343ef076fdd22cc24d6c1886ed83b4e4100b 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 11b65fa437c1cd7f736d2729d9d40d2dd3c673f9..3b257c2f0c39fc746bd2e6f37d2e29567c8b4707 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 b2f661289bbe5880bac920e59a32c810abee54e7..d9faceff50564355f77a017177d51b7698864d33 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 6cacc3c343ce489368a1a50cb447e97a7cde7d81..62e63301696d67ca4da2177de2308a658c6943bb 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 4ae8a08a113bd70d6f5058d469d6917eabc73771..0979afe6a8a2b71e100d5d2568d6eca95eafcdf0 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 895dbb382ed386f78a9df4edaad7bc77d2d38de5..9391be7b9d3a1923d148b475d37293e6f9630709 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 40bd81d9315bc84bbe2b8ae6e9f9fb9b6512ef0f..86149af67ccb3685430d91878e746067de9b1f80 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 7dd7bdf76c20f1e9d86eed59e7ca9f6c989c2328..90b08cd7060371bbddc77d87055a246af1c2a725 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 6087e38e680267e6e8259a3f810777d5dad0e01a..c303d4371e047650ab70ff9915bda41dbd7b584f 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 e2502a19d84ab296565616760464875f2b4dc63e..3eb857aa520a0bccc692417d4a0be4d34d9dcae5 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 9b6068565bb4ef3da6da72740b849e8db0e74182..90afbdeca487716197edc9ee74c44944b0819915 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 56d72e3a813e9961b2bea7efe70b100196aa7128..f47ce308fc80e3d675a86aaa85fb855a15e4b582 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 204bedaa4b6988538aade23621986b053440bd87..32d924d7b21d3af19cf3c664fa3c746ab5adcd9d 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 a4d58d3614a07c8486318e876a176303836b7b37..38a374035cdfa6151badcde3c3a1b96b64b824ef 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 66c4c985e16a162ced6f5caf19c69cfc2633d78e..651bd8a97336a8d5d352ab24b831eae2c5bfff2f 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 ad25f442e61eee3584ad2b4ad8a3a7968e3a142a..811f3a351008aaca919437fc2519d3d47087ba57 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);
 			}
 		});
 	}