diff --git a/briar-core/src/net/sf/briar/plugins/modem/Receiver.java b/briar-core/src/net/sf/briar/plugins/modem/Receiver.java
index 44f117ce38f7e755eb00d543bb5f7233e1f9b1d9..e4813f6932b36f5a8abe91a9a977f5e0ab4433fe 100644
--- a/briar-core/src/net/sf/briar/plugins/modem/Receiver.java
+++ b/briar-core/src/net/sf/briar/plugins/modem/Receiver.java
@@ -1,21 +1,15 @@
 package net.sf.briar.plugins.modem;
 
-import static java.util.logging.Level.INFO;
-
 import java.io.IOException;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.SortedSet;
 import java.util.TreeSet;
-import java.util.logging.Logger;
 
 class Receiver implements ReadHandler {
 
 	static final int MAX_WINDOW_SIZE = 8 * Data.MAX_PAYLOAD_LENGTH;
 
-	private static final Logger LOG =
-			Logger.getLogger(Receiver.class.getName());
-
 	private final Sender sender;
 	private final SortedSet<Data> dataFrames; // Locking: this
 
@@ -33,24 +27,19 @@ class Receiver implements ReadHandler {
 	synchronized Data read() throws IOException, InterruptedException {
 		while(valid) {
 			if(dataFrames.isEmpty()) {
-				if(LOG.isLoggable(INFO)) LOG.info("Waiting for a data frame");
+				// Wait for a data frame
 				wait();
 			} else {
 				Data d = dataFrames.first();
 				if(d.getSequenceNumber() == nextSequenceNumber) {
-					if(LOG.isLoggable(INFO))
-						LOG.info("Reading #" + d.getSequenceNumber());
 					dataFrames.remove(d);
 					// Update the window
 					windowSize += d.getPayloadLength();
-					if(LOG.isLoggable(INFO))
-						LOG.info("Window at receiver " + windowSize);
 					sender.sendAck(0L, windowSize);
 					nextSequenceNumber++;
 					return d;
 				} else {
-					if(LOG.isLoggable(INFO))
-						LOG.info("Waiting for #" + nextSequenceNumber);
+					// Wait for the next in-order data frame
 					wait();
 				}
 			}
@@ -76,62 +65,45 @@ class Receiver implements ReadHandler {
 			sender.handleAck(b);
 			break;
 		default:
-			if(LOG.isLoggable(INFO))
-				LOG.info("Ignoring unknown frame type: " + b[0]);
+			// Ignore unknown frame type
 			return;
 		}
 	}
 
 	private synchronized void handleData(byte[] b) throws IOException {
 		if(b.length < Data.MIN_LENGTH || b.length > Data.MAX_LENGTH) {
-			if(LOG.isLoggable(INFO))
-				LOG.info("Ignoring data frame with invalid length");
+			// Ignore data frame with invalid length
 			return;
 		}
 		Data d = new Data(b);
 		int payloadLength = d.getPayloadLength();
-		if(payloadLength > windowSize) {
-			if(LOG.isLoggable(INFO)) LOG.info("No space in the window");
-			return;
-		}
+		if(payloadLength > windowSize) return; // No space in the window
 		if(d.getChecksum() != d.calculateChecksum()) {
-			if(LOG.isLoggable(INFO))
-				LOG.info("Incorrect checksum on data frame");
+			// Ignore data frame with invalid checksum
 			return;
 		}
 		long sequenceNumber = d.getSequenceNumber();
 		if(sequenceNumber == 0L) {
-			if(LOG.isLoggable(INFO)) LOG.info("Window probe");
+			// Window probe
 		} else if(sequenceNumber < nextSequenceNumber) {
-			if(LOG.isLoggable(INFO)) LOG.info("Duplicate data frame");
+			// Duplicate data frame
 		} else if(d.isLastFrame()) {
 			finalSequenceNumber = sequenceNumber;
+			// Remove any data frames with higher sequence numbers
 			Iterator<Data> it = dataFrames.iterator();
 			while(it.hasNext()) {
 				Data d1 = it.next();
-				if(d1.getSequenceNumber() >= finalSequenceNumber) {
-					if(LOG.isLoggable(INFO))
-						LOG.info("Received data frame after FIN");
-					it.remove();
-				}
+				if(d1.getSequenceNumber() >= finalSequenceNumber) it.remove();
 			}
-			if(LOG.isLoggable(INFO)) LOG.info("Received #" + sequenceNumber);
 			if(dataFrames.add(d)) {
 				windowSize -= payloadLength;
-				if(LOG.isLoggable(INFO))
-					LOG.info("Window at receiver " + windowSize);
 				notifyAll();
 			}
 		} else if(sequenceNumber < finalSequenceNumber) {
-			if(LOG.isLoggable(INFO)) LOG.info("Received #" + sequenceNumber);
 			if(dataFrames.add(d)) {
 				windowSize -= payloadLength;
-				if(LOG.isLoggable(INFO))
-					LOG.info("Window at receiver " + windowSize);
 				notifyAll();
 			}
-		} else {
-			if(LOG.isLoggable(INFO)) LOG.info("Received data frame after FIN");
 		}
 		// Acknowledge the data frame even if it's a duplicate
 		sender.sendAck(sequenceNumber, windowSize);
diff --git a/briar-core/src/net/sf/briar/plugins/modem/ReliabilityLayer.java b/briar-core/src/net/sf/briar/plugins/modem/ReliabilityLayer.java
index 43321585fa75e1a444ace543f7d8de870708fe86..838c9c46b8516a905f0a769cc4bb53ed5ca460d7 100644
--- a/briar-core/src/net/sf/briar/plugins/modem/ReliabilityLayer.java
+++ b/briar-core/src/net/sf/briar/plugins/modem/ReliabilityLayer.java
@@ -1,6 +1,5 @@
 package net.sf.briar.plugins.modem;
 
-import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 
 import java.io.IOException;
@@ -44,15 +43,13 @@ class ReliabilityLayer implements ReadHandler, WriteHandler {
 					while(running) {
 						byte[] b = writes.take();
 						if(b.length == 0) return; // Poison pill
-						if(LOG.isLoggable(INFO))
-							LOG.info("Writing " + b.length + " bytes");
 						writeHandler.handleWrite(b);
 					}
 				} catch(InterruptedException e) {
 					if(LOG.isLoggable(WARNING))
 						LOG.warning("Interrupted while writing");
-					running = false;
 					Thread.currentThread().interrupt();
+					running = false;
 				} catch(IOException e) {
 					if(LOG.isLoggable(WARNING))
 						LOG.warning("Interrupted while writing");
@@ -73,7 +70,6 @@ class ReliabilityLayer implements ReadHandler, WriteHandler {
 	}
 
 	void stop() {
-		if(LOG.isLoggable(INFO)) LOG.info("Stopping reliability layer");
 		running = false;
 		receiver.invalidate();
 		writes.add(new byte[0]); // Poison pill
@@ -82,14 +78,12 @@ class ReliabilityLayer implements ReadHandler, WriteHandler {
 	// The modem calls this method to pass data up to the SLIP decoder
 	public void handleRead(byte[] b) throws IOException {
 		if(!running) throw new IOException("Connection closed");
-		if(LOG.isLoggable(INFO)) LOG.info("Read " + b.length + " bytes");
 		decoder.handleRead(b);
 	}
 
 	// The SLIP encoder calls this method to pass data down to the modem
 	public void handleWrite(byte[] b) throws IOException {
 		if(!running) throw new IOException("Connection closed");
-		if(LOG.isLoggable(INFO)) LOG.info("Queueing " + b.length + " bytes");
 		if(b.length > 0) writes.add(b);
 	}
 }
diff --git a/briar-core/src/net/sf/briar/plugins/modem/Sender.java b/briar-core/src/net/sf/briar/plugins/modem/Sender.java
index 8a86334c153f932cda849f5a43478333d448ae94..4c9018db52ca2759e2f3661139178d7806b449a3 100644
--- a/briar-core/src/net/sf/briar/plugins/modem/Sender.java
+++ b/briar-core/src/net/sf/briar/plugins/modem/Sender.java
@@ -1,6 +1,5 @@
 package net.sf.briar.plugins.modem;
 
-import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 
 import java.io.IOException;
@@ -41,25 +40,17 @@ class Sender {
 		a.setSequenceNumber(sequenceNumber);
 		a.setWindowSize(windowSize);
 		a.setChecksum(a.calculateChecksum());
-		if(sequenceNumber == 0L) {
-			if(LOG.isLoggable(INFO)) LOG.info("Sending window update");
-		} else {
-			if(LOG.isLoggable(INFO))
-				LOG.info("Acknowledging #" + sequenceNumber);
-		}
 		writeHandler.handleWrite(a.getBuffer());
 	}
 
 	void handleAck(byte[] b) {
 		if(b.length != Ack.LENGTH) {
-			if(LOG.isLoggable(INFO))
-				LOG.info("Ignoring ack frame with invalid length");
+			// Ignore ack frame with invalid length
 			return;
 		}
 		Ack a = new Ack(b);
 		if(a.getChecksum() != a.calculateChecksum()) {
-			if(LOG.isLoggable(INFO))
-				LOG.info("Incorrect checksum on ack frame");
+			// Ignore ack frame with invalid checksum
 			return;
 		}
 		long sequenceNumber = a.getSequenceNumber();
@@ -72,8 +63,6 @@ class Sender {
 			for(int i = 0; it.hasNext(); i++) {
 				Outstanding o = it.next();
 				if(o.data.getSequenceNumber() == sequenceNumber) {
-					if(LOG.isLoggable(INFO))
-						LOG.info("#" + sequenceNumber + " acknowledged");
 					it.remove();
 					outstandingBytes -= o.data.getPayloadLength();
 					foundIndex = i;
@@ -86,8 +75,6 @@ class Sender {
 						timeout = rtt + (rttVar << 2);
 						if(timeout < MIN_TIMEOUT) timeout = MIN_TIMEOUT;
 						else if(timeout > MAX_TIMEOUT) timeout = MAX_TIMEOUT;
-						if(LOG.isLoggable(INFO))
-							LOG.info("RTT " + rtt + ", timeout " + timeout);
 					}
 					break;
 				}
@@ -95,10 +82,6 @@ class Sender {
 			// If any older data frames are outstanding, retransmit the oldest
 			if(foundIndex > 0) {
 				fastRetransmit = outstanding.poll();
-				if(LOG.isLoggable(INFO)) {
-					LOG.info("Fast retransmitting #"
-							+ fastRetransmit.data.getSequenceNumber());
-				}
 				fastRetransmit.lastTransmitted = now;
 				fastRetransmit.retransmitted = true;
 				outstanding.add(fastRetransmit);
@@ -108,7 +91,6 @@ class Sender {
 			int oldWindowSize = windowSize;
 			// Don't accept an unreasonably large window size
 			windowSize = Math.min(a.getWindowSize(), Receiver.MAX_WINDOW_SIZE);
-			if(LOG.isLoggable(INFO)) LOG.info("Window at sender " + windowSize);
 			// If space has become available, notify any waiting writers
 			if(windowSize > oldWindowSize || foundIndex != -1) notifyAll();
 		}
@@ -119,7 +101,7 @@ class Sender {
 				writeHandler.handleWrite(d.getBuffer());
 			} catch(IOException e) {
 				// FIXME: Do something more meaningful
-				if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
+				if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 			}
 		}
 	}
@@ -131,30 +113,21 @@ class Sender {
 		synchronized(this) {
 			if(outstanding.isEmpty()) {
 				if(dataWaiting && now - lastWindowUpdateOrProbe > timeout) {
-					if(LOG.isLoggable(INFO)) LOG.info("Sending window probe");
 					sendProbe = true;
 					timeout <<= 1;
 					if(timeout > MAX_TIMEOUT) timeout = MAX_TIMEOUT;
-					if(LOG.isLoggable(INFO))
-						LOG.info("Increasing timeout to " + timeout);
 				}
 			} else {
 				Iterator<Outstanding> it = outstanding.iterator();
 				while(it.hasNext()) {
 					Outstanding o = it.next();
 					if(now - o.lastTransmitted > timeout) {
-						if(LOG.isLoggable(INFO)) {
-							LOG.info("Retransmitting #"
-									+ o.data.getSequenceNumber());
-						}
 						it.remove();
 						if(retransmit == null)
 							retransmit = new ArrayList<Outstanding>();
 						retransmit.add(o);
 						timeout <<= 1;
 						if(timeout > MAX_TIMEOUT) timeout = MAX_TIMEOUT;
-						if(LOG.isLoggable(INFO))
-							LOG.info("Increasing timeout to " + timeout);
 					}
 				}
 				if(retransmit != null) {
@@ -181,7 +154,7 @@ class Sender {
 			}
 		} catch(IOException e) {
 			// FIXME: Do something more meaningful
-			if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
+			if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 			return;
 		}
 	}
@@ -189,9 +162,8 @@ class Sender {
 	void write(Data d) throws IOException, InterruptedException {
 		int payloadLength = d.getPayloadLength();
 		synchronized(this) {
+			// Wait for space in the window
 			while(outstandingBytes + payloadLength >= windowSize) {
-				if(LOG.isLoggable(INFO))
-					LOG.info("Waiting for space in the window");
 				dataWaiting = true;
 				wait();
 			}
@@ -199,8 +171,6 @@ class Sender {
 			outstandingBytes += payloadLength;
 			dataWaiting = false;
 		}
-		if(LOG.isLoggable(INFO))
-			LOG.info("Transmitting #" + d.getSequenceNumber());
 		writeHandler.handleWrite(d.getBuffer());
 	}
 
diff --git a/briar-core/src/net/sf/briar/plugins/modem/SlipDecoder.java b/briar-core/src/net/sf/briar/plugins/modem/SlipDecoder.java
index 471e774f0caeca7dcff8e5b65b2ddc896232d85c..a179f3d0ab44f78260e9bdc3482ba633bec0dc47 100644
--- a/briar-core/src/net/sf/briar/plugins/modem/SlipDecoder.java
+++ b/briar-core/src/net/sf/briar/plugins/modem/SlipDecoder.java
@@ -1,15 +1,9 @@
 package net.sf.briar.plugins.modem;
 
-import static java.util.logging.Level.INFO;
-
 import java.io.IOException;
-import java.util.logging.Logger;
 
 class SlipDecoder implements ReadHandler {
 
-	private static final Logger LOG =
-			Logger.getLogger(SlipDecoder.class.getName());
-
 	// https://tools.ietf.org/html/rfc1055
 	private static final byte END = (byte) 192, ESC = (byte) 219;
 	private static final byte TEND = (byte) 220, TESC = (byte) 221;
@@ -32,8 +26,6 @@ class SlipDecoder implements ReadHandler {
 					reset(true);
 				} else {
 					if(decodedLength > 0) {
-						if(LOG.isLoggable(INFO))
-							LOG.info("Decoded " + decodedLength + " bytes");
 						byte[] decoded = new byte[decodedLength];
 						System.arraycopy(buf, 0, decoded, 0, decodedLength);
 						readHandler.handleRead(decoded);
@@ -74,10 +66,6 @@ class SlipDecoder implements ReadHandler {
 	}
 
 	private void reset(boolean error) {
-		if(error) {
-			if(LOG.isLoggable(INFO))
-				LOG.info("Decoding error after " + decodedLength + " bytes");
-		}
 		escape = false;
 		decodedLength = 0;
 	}