diff --git a/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java
index 5be846f61c66d139770efa17e45dcaae70a74dda..7859bd6521091bb87ddb981f08e603e15b9503b4 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java
@@ -9,6 +9,7 @@ import org.briarproject.bramble.socks.SocksModule;
 import org.briarproject.bramble.system.AndroidSystemModule;
 import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
 import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
+import org.briarproject.bramble.system.DefaultThreadFactoryModule;
 
 import dagger.Module;
 
@@ -18,6 +19,7 @@ import dagger.Module;
 		AndroidSystemModule.class,
 		AndroidTaskSchedulerModule.class,
 		AndroidWakefulIoExecutorModule.class,
+		DefaultThreadFactoryModule.class,
 		CircumventionModule.class,
 		DnsModule.class,
 		ReportingModule.class,
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 dc0b96d3d487b28ee9faea453b510dfc3d219b72..3513b5f540a948575a38298332df2f5fc7358023 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/TimeLoggingExecutor.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/TimeLoggingExecutor.java
@@ -4,6 +4,7 @@ import org.briarproject.nullsafety.NotNullByDefault;
 
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Logger;
@@ -19,9 +20,10 @@ public class TimeLoggingExecutor extends ThreadPoolExecutor {
 	public TimeLoggingExecutor(String tag, int corePoolSize, int maxPoolSize,
 			long keepAliveTime, TimeUnit unit,
 			BlockingQueue<Runnable> workQueue,
+			ThreadFactory threadFactory,
 			RejectedExecutionHandler handler) {
 		super(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue,
-				handler);
+				threadFactory, handler);
 		log = Logger.getLogger(tag);
 	}
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoExecutorModule.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoExecutorModule.java
index ab0e4114f5672469fd0c26b8061389c1e1b7172c..7a069b2eb817cbfd0d1943e4b5cf01b27ec59192 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoExecutorModule.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/CryptoExecutorModule.java
@@ -9,6 +9,7 @@ import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
 
 import javax.inject.Inject;
@@ -37,31 +38,31 @@ public class CryptoExecutorModule {
 	private static final int MAX_EXECUTOR_THREADS =
 			Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
 
-	private final ExecutorService cryptoExecutor;
-
 	public CryptoExecutorModule() {
-		// Use an unbounded queue
-		BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
-		// Discard tasks that are submitted during shutdown
-		RejectedExecutionHandler policy =
-				new ThreadPoolExecutor.DiscardPolicy();
-		// Create a limited # of threads and keep them in the pool for 60 secs
-		cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
-				MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
 	}
 
 	@Provides
 	@Singleton
 	@CryptoExecutor
 	ExecutorService provideCryptoExecutorService(
-			LifecycleManager lifecycleManager) {
+			LifecycleManager lifecycleManager, ThreadFactory threadFactory) {
+		// Use an unbounded queue
+		BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
+		// Discard tasks that are submitted during shutdown
+		RejectedExecutionHandler policy =
+				new ThreadPoolExecutor.DiscardPolicy();
+		// Create a limited # of threads and keep them in the pool for 60 secs
+		ExecutorService cryptoExecutor = new TimeLoggingExecutor(
+				"CryptoExecutor", 0, MAX_EXECUTOR_THREADS, 60, SECONDS, queue,
+				threadFactory, policy);
 		lifecycleManager.registerForShutdown(cryptoExecutor);
 		return cryptoExecutor;
 	}
 
 	@Provides
 	@CryptoExecutor
-	Executor provideCryptoExecutor() {
+	Executor provideCryptoExecutor(
+			@CryptoExecutor ExecutorService cryptoExecutor) {
 		return cryptoExecutor;
 	}
 }
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseExecutorModule.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseExecutorModule.java
index a65feef89da60c3f967fb891906ee1a13c14a3d8..73b26710a4b323dc36380728ae38eb13b85ec59e 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseExecutorModule.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseExecutorModule.java
@@ -9,6 +9,7 @@ import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
 
 import javax.inject.Inject;
@@ -28,24 +29,20 @@ public class DatabaseExecutorModule {
 		ExecutorService executorService;
 	}
 
-	private final ExecutorService databaseExecutor;
-
-	public DatabaseExecutorModule() {
+	@Provides
+	@Singleton
+	@DatabaseExecutor
+	ExecutorService provideDatabaseExecutorService(
+			LifecycleManager lifecycleManager, ThreadFactory threadFactory) {
 		// Use an unbounded queue
 		BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
 		// Discard tasks that are submitted during shutdown
 		RejectedExecutionHandler policy =
 				new ThreadPoolExecutor.DiscardPolicy();
 		// Use a single thread and keep it in the pool for 60 secs
-		databaseExecutor = new TimeLoggingExecutor("DatabaseExecutor", 0, 1,
-				60, SECONDS, queue, policy);
-	}
-
-	@Provides
-	@Singleton
-	@DatabaseExecutor
-	ExecutorService provideDatabaseExecutorService(
-			LifecycleManager lifecycleManager) {
+		ExecutorService databaseExecutor = new TimeLoggingExecutor(
+				"DatabaseExecutor", 0, 1, 60, SECONDS, queue, threadFactory,
+				policy);
 		lifecycleManager.registerForShutdown(databaseExecutor);
 		return databaseExecutor;
 	}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/event/DefaultEventExecutorModule.java b/bramble-core/src/main/java/org/briarproject/bramble/event/DefaultEventExecutorModule.java
index 5fc21cc86a3142a3f2c1ff5f07188dc2c3e1c7c7..2d43800c174ff48ccd7dfc941e2802c115c60f18 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/event/DefaultEventExecutorModule.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/event/DefaultEventExecutorModule.java
@@ -3,6 +3,7 @@ package org.briarproject.bramble.event;
 import org.briarproject.bramble.api.event.EventExecutor;
 
 import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadFactory;
 
 import javax.inject.Singleton;
 
@@ -22,10 +23,11 @@ public class DefaultEventExecutorModule {
 	@Provides
 	@Singleton
 	@EventExecutor
-	Executor provideEventExecutor() {
+	Executor provideEventExecutor(ThreadFactory threadFactory) {
 		return newSingleThreadExecutor(r -> {
-			Thread t = new Thread(r);
+			Thread t = threadFactory.newThread(r);
 			t.setDaemon(true);
+			t.setName(t.getName() + "-Event");
 			return t;
 		});
 	}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleModule.java b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleModule.java
index 43db266efb188d5b3a3c2bbe90d48e194f7b1990..b38f79b1c5933f731b1a13c62255be94f3336e0d 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleModule.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleModule.java
@@ -9,6 +9,7 @@ import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.RejectedExecutionHandler;
 import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
 
 import javax.inject.Inject;
@@ -28,19 +29,6 @@ public class LifecycleModule {
 		Executor executor;
 	}
 
-	private final ExecutorService ioExecutor;
-
-	public LifecycleModule() {
-		// The thread pool is unbounded, so use direct handoff
-		BlockingQueue<Runnable> queue = new SynchronousQueue<>();
-		// Discard tasks that are submitted during shutdown
-		RejectedExecutionHandler policy =
-				new ThreadPoolExecutor.DiscardPolicy();
-		// Create threads as required and keep them in the pool for 60 seconds
-		ioExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
-				60, SECONDS, queue, policy);
-	}
-
 	@Provides
 	@Singleton
 	ShutdownManager provideShutdownManager() {
@@ -57,7 +45,16 @@ public class LifecycleModule {
 	@Provides
 	@Singleton
 	@IoExecutor
-	Executor provideIoExecutor(LifecycleManager lifecycleManager) {
+	Executor provideIoExecutor(LifecycleManager lifecycleManager,
+			ThreadFactory threadFactory) {
+		// The thread pool is unbounded, so use direct handoff
+		BlockingQueue<Runnable> queue = new SynchronousQueue<>();
+		// Discard tasks that are submitted during shutdown
+		RejectedExecutionHandler policy =
+				new ThreadPoolExecutor.DiscardPolicy();
+		// Create threads as required and keep them in the pool for 60 seconds
+		ExecutorService ioExecutor = new ThreadPoolExecutor(0,
+				Integer.MAX_VALUE, 60, SECONDS, queue, threadFactory, policy);
 		lifecycleManager.registerForShutdown(ioExecutor);
 		return ioExecutor;
 	}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/system/DefaultTaskSchedulerModule.java b/bramble-core/src/main/java/org/briarproject/bramble/system/DefaultTaskSchedulerModule.java
index 3e36faff275884f76df2e001d8b3c4ac2f9709f7..30e4732370db1c4abe8e7717021d6755248c5628 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/system/DefaultTaskSchedulerModule.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/system/DefaultTaskSchedulerModule.java
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.system.TaskScheduler;
 import java.util.concurrent.RejectedExecutionHandler;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -21,18 +22,15 @@ public class DefaultTaskSchedulerModule {
 		TaskScheduler scheduler;
 	}
 
-	private final ScheduledExecutorService scheduledExecutorService;
-
-	public DefaultTaskSchedulerModule() {
+	@Provides
+	@Singleton
+	TaskScheduler provideTaskScheduler(LifecycleManager lifecycleManager,
+			ThreadFactory threadFactory) {
 		// Discard tasks that are submitted during shutdown
 		RejectedExecutionHandler policy =
 				new ScheduledThreadPoolExecutor.DiscardPolicy();
-		scheduledExecutorService = new ScheduledThreadPoolExecutor(1, policy);
-	}
-
-	@Provides
-	@Singleton
-	TaskScheduler provideTaskScheduler(LifecycleManager lifecycleManager) {
+		ScheduledExecutorService scheduledExecutorService =
+				new ScheduledThreadPoolExecutor(1, threadFactory, policy);
 		lifecycleManager.registerForShutdown(scheduledExecutorService);
 		return new TaskSchedulerImpl(scheduledExecutorService);
 	}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/system/DefaultThreadFactoryModule.java b/bramble-core/src/main/java/org/briarproject/bramble/system/DefaultThreadFactoryModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2ea5f976dd022bed1dafa285e0a46eb2e91aed7
--- /dev/null
+++ b/bramble-core/src/main/java/org/briarproject/bramble/system/DefaultThreadFactoryModule.java
@@ -0,0 +1,18 @@
+package org.briarproject.bramble.system;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class DefaultThreadFactoryModule {
+	@Provides
+	@Singleton
+	ThreadFactory provideThreadFactory() {
+		return Executors.defaultThreadFactory();
+	}
+}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/plugin/file/RemovableDriveIntegrationTestComponent.java b/bramble-core/src/test/java/org/briarproject/bramble/plugin/file/RemovableDriveIntegrationTestComponent.java
index a353f6fcbe73d57627ec7edc5e13e36b19569bfb..6a071e2deb17225fb3ee4d6cbf4f6be80844a430 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/plugin/file/RemovableDriveIntegrationTestComponent.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/plugin/file/RemovableDriveIntegrationTestComponent.java
@@ -10,6 +10,7 @@ import org.briarproject.bramble.api.plugin.file.RemovableDriveManager;
 import org.briarproject.bramble.battery.DefaultBatteryManagerModule;
 import org.briarproject.bramble.event.DefaultEventExecutorModule;
 import org.briarproject.bramble.mailbox.ModularMailboxModule;
+import org.briarproject.bramble.system.DefaultThreadFactoryModule;
 import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
 import org.briarproject.bramble.system.TimeTravelModule;
 import org.briarproject.bramble.test.TestDatabaseConfigModule;
@@ -29,6 +30,7 @@ import dagger.Component;
 		DefaultBatteryManagerModule.class,
 		DefaultEventExecutorModule.class,
 		DefaultWakefulIoExecutorModule.class,
+		DefaultThreadFactoryModule.class,
 		TestDatabaseConfigModule.class,
 		TestDnsModule.class,
 		TestFeatureFlagModule.class,
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/BrambleCoreIntegrationTestModule.java b/bramble-core/src/test/java/org/briarproject/bramble/test/BrambleCoreIntegrationTestModule.java
index 8d4224df6ccc561d82dcc4b356e3ee09f770725b..e2ddb2fa94e99e9f4ce1cb4d4e6e43b5d1fede7f 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/test/BrambleCoreIntegrationTestModule.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/test/BrambleCoreIntegrationTestModule.java
@@ -11,6 +11,7 @@ import dagger.Module;
 		DefaultBatteryManagerModule.class,
 		DefaultEventExecutorModule.class,
 		DefaultWakefulIoExecutorModule.class,
+		TestThreadFactoryModule.class,
 		TestDatabaseConfigModule.class,
 		TestFeatureFlagModule.class,
 		TestMailboxDirectoryModule.class,
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/TestThreadFactoryModule.java b/bramble-core/src/test/java/org/briarproject/bramble/test/TestThreadFactoryModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd8432d98495a3d90c661c9b0c602a061d7dd2a7
--- /dev/null
+++ b/bramble-core/src/test/java/org/briarproject/bramble/test/TestThreadFactoryModule.java
@@ -0,0 +1,66 @@
+package org.briarproject.bramble.test;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class TestThreadFactoryModule {
+
+	@Nullable
+	private final String prefix;
+
+	public TestThreadFactoryModule() {
+		this(null);
+	}
+
+	public TestThreadFactoryModule(@Nullable String prefix) {
+		this.prefix = prefix;
+	}
+
+	@Provides
+	ThreadFactory provideThreadFactory() {
+		if (prefix == null) return Executors.defaultThreadFactory();
+		return new TestThreadFactory(prefix);
+	}
+
+	/**
+	 * This class is mostly copied from
+	 * {@link Executors#defaultThreadFactory()} only adds a given prefix.
+	 */
+	static class TestThreadFactory implements ThreadFactory {
+		private static final AtomicInteger poolNumber = new AtomicInteger(1);
+		private final ThreadGroup group;
+		private final AtomicInteger threadNumber = new AtomicInteger(1);
+		private final String namePrefix;
+
+		private TestThreadFactory(String prefix) {
+			SecurityManager s = System.getSecurityManager();
+			this.group = s != null ? s.getThreadGroup() :
+					Thread.currentThread().getThreadGroup();
+			this.namePrefix =
+					prefix + "-p-" + poolNumber.getAndIncrement() + "-t-";
+		}
+
+		@Override
+		public Thread newThread(@Nonnull Runnable r) {
+			Thread t = new Thread(this.group, r,
+					this.namePrefix + this.threadNumber.getAndIncrement(), 0L);
+			if (t.isDaemon()) {
+				t.setDaemon(false);
+			}
+
+			if (t.getPriority() != 5) {
+				t.setPriority(5);
+			}
+
+			return t;
+		}
+	}
+}
diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt
index a841f9913289d4aeed8afdecd6229ed9e5b1649f..d15db40dd92716270eacb8dab8de80e33a859ca7 100644
--- a/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt
+++ b/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt
@@ -22,6 +22,7 @@ import org.briarproject.bramble.plugin.tor.UnixTorPluginFactory
 import org.briarproject.bramble.plugin.tor.WindowsTorPluginFactory
 import org.briarproject.bramble.system.ClockModule
 import org.briarproject.bramble.system.DefaultTaskSchedulerModule
+import org.briarproject.bramble.system.DefaultThreadFactoryModule
 import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule
 import org.briarproject.bramble.system.DesktopSecureRandomModule
 import org.briarproject.bramble.util.OsUtils.isLinux
@@ -44,6 +45,7 @@ import javax.inject.Singleton
         DefaultEventExecutorModule::class,
         DefaultTaskSchedulerModule::class,
         DefaultWakefulIoExecutorModule::class,
+        DefaultThreadFactoryModule::class,
         DesktopSecureRandomModule::class,
         HeadlessBlogModule::class,
         HeadlessContactModule::class,
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt
index 2725b70a5195a3f5bf72529b53baaa41aa6558fd..219051d16cb67aa984f47963cb9e9f3ec4faa4b8 100644
--- a/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt
@@ -17,6 +17,7 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory
 import org.briarproject.bramble.event.DefaultEventExecutorModule
 import org.briarproject.bramble.system.ClockModule
 import org.briarproject.bramble.system.DefaultTaskSchedulerModule
+import org.briarproject.bramble.system.DefaultThreadFactoryModule
 import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule
 import org.briarproject.bramble.test.TestFeatureFlagModule
 import org.briarproject.bramble.test.TestSecureRandomModule
@@ -37,6 +38,7 @@ import javax.inject.Singleton
         DefaultEventExecutorModule::class,
         DefaultTaskSchedulerModule::class,
         DefaultWakefulIoExecutorModule::class,
+        DefaultThreadFactoryModule::class,
         TestFeatureFlagModule::class,
         TestSecureRandomModule::class,
         HeadlessBlogModule::class,
diff --git a/mailbox-integration-tests/src/test/java/org/briarproject/bramble/mailbox/AbstractMailboxIntegrationTest.java b/mailbox-integration-tests/src/test/java/org/briarproject/bramble/mailbox/AbstractMailboxIntegrationTest.java
index 97fb507fb51085999b400ebb0b4da87ade9d1a5a..39a4ba1b4f558db4789ca770864a863d652b9a18 100644
--- a/mailbox-integration-tests/src/test/java/org/briarproject/bramble/mailbox/AbstractMailboxIntegrationTest.java
+++ b/mailbox-integration-tests/src/test/java/org/briarproject/bramble/mailbox/AbstractMailboxIntegrationTest.java
@@ -18,6 +18,8 @@ import org.briarproject.bramble.api.mailbox.MailboxUpdateWithMailbox;
 import org.briarproject.bramble.api.sync.GroupId;
 import org.briarproject.bramble.test.BrambleIntegrationTest;
 import org.briarproject.bramble.test.TestDatabaseConfigModule;
+import org.briarproject.bramble.test.TestLogFormatter;
+import org.briarproject.bramble.test.TestThreadFactoryModule;
 import org.briarproject.briar.api.messaging.PrivateMessage;
 import org.briarproject.mailbox.lib.AbstractMailbox;
 import org.briarproject.mailbox.lib.TestMailbox;
@@ -43,6 +45,10 @@ abstract class AbstractMailboxIntegrationTest
 
 	static final String URL_BASE = "http://127.0.0.1:8000";
 
+	AbstractMailboxIntegrationTest() {
+		TestLogFormatter.use();
+	}
+
 	private final File dir1 = new File(testDir, "alice");
 	private final File dir2 = new File(testDir, "bob");
 	private final SecretKey rootKey = getSecretKey();
@@ -73,13 +79,16 @@ abstract class AbstractMailboxIntegrationTest
 		mailbox.stopLifecycle(true);
 	}
 
-	MailboxIntegrationTestComponent startTestComponent(
+	private MailboxIntegrationTestComponent startTestComponent(
 			File databaseDir, String name) throws Exception {
+		TestThreadFactoryModule threadFactoryModule =
+				new TestThreadFactoryModule(name);
 		TestDatabaseConfigModule dbModule =
 				new TestDatabaseConfigModule(databaseDir);
 		MailboxIntegrationTestComponent component =
 				DaggerMailboxIntegrationTestComponent
 						.builder()
+						.testThreadFactoryModule(threadFactoryModule)
 						.testDatabaseConfigModule(dbModule)
 						.build();
 		injectEagerSingletons(component);
diff --git a/mailbox-integration-tests/src/test/java/org/briarproject/bramble/test/TestLogFormatter.java b/mailbox-integration-tests/src/test/java/org/briarproject/bramble/test/TestLogFormatter.java
new file mode 100644
index 0000000000000000000000000000000000000000..b5b819d15ff828cf9bf9d984cf37019d5aebd560
--- /dev/null
+++ b/mailbox-integration-tests/src/test/java/org/briarproject/bramble/test/TestLogFormatter.java
@@ -0,0 +1,57 @@
+package org.briarproject.bramble.test;
+
+import org.briarproject.nullsafety.NotNullByDefault;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.logging.SimpleFormatter;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+@ThreadSafe
+@NotNullByDefault
+public class TestLogFormatter extends SimpleFormatter {
+
+	private final Object lock = new Object();
+	private final DateFormat dateFormat; // Locking: lock
+	private final Date date; // Locking: lock
+
+	public static void use() {
+		LogManager.getLogManager().reset();
+		Logger rootLogger = LogManager.getLogManager().getLogger("");
+		ConsoleHandler handler = new ConsoleHandler();
+		handler.setFormatter(new TestLogFormatter());
+		rootLogger.addHandler(handler);
+	}
+
+	private TestLogFormatter() {
+		synchronized (lock) {
+			dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
+			date = new Date();
+		}
+	}
+
+	@Override
+	public String format(LogRecord rec) {
+		if (rec.getThrown() == null) {
+			String dateString;
+			synchronized (lock) {
+				date.setTime(rec.getMillis());
+				dateString = dateFormat.format(date);
+			}
+			return String.format("%s [%s] %s %s - %s\n",
+					dateString,
+					Thread.currentThread().getName(),
+					rec.getLevel().getName(),
+					rec.getLoggerName(),
+					rec.getMessage());
+		} else {
+			return super.format(rec);
+		}
+	}
+}