Index: src/or/or.h
===================================================================
--- src/or/or.h	(revision 18962)
+++ src/or/or.h	(working copy)
@@ -3900,8 +3900,12 @@
 extern uint64_t stats_n_relay_cells_relayed;
 extern uint64_t stats_n_relay_cells_delivered;
 
+int relay_cell_processing_stats_init(void);
+void relay_cell_processing_stats_free_all(void);
+
 int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
-                               cell_direction_t cell_direction);
+                               cell_direction_t cell_direction,
+                               or_connection_t *incoming_conn);
 
 void relay_header_pack(char *dest, const relay_header_t *src);
 void relay_header_unpack(relay_header_t *dest, const char *src);
Index: src/or/relay.c
===================================================================
--- src/or/relay.c	(revision 18962)
+++ src/or/relay.c	(working copy)
@@ -44,6 +44,148 @@
  */
 uint64_t stats_n_relay_cells_delivered = 0;
 
+/** Cipher for anonymizing cell statistics log */
+crypto_cipher_env_t *stats_relay_cell_cipher = NULL;
+
+int
+relay_cell_processing_stats_init(void)
+{
+  int r;
+  char key[CIPHER_KEY_LEN];
+
+  stats_relay_cell_cipher = crypto_new_cipher_env();
+
+  /*
+  if (crypto_cipher_set_key(stats_relay_cell_cipher, "1234567890123456")) {
+    log_warn(LD_GENERAL,
+             "failed to set fixed symmetric key for stats_relay_cell_cipher");
+    goto error;
+  } else {
+    log_debug(LD_GENERAL, "using fixed key for stats_relay_cell_cipher");
+  }
+  */
+
+  if (crypto_rand(key, CIPHER_KEY_LEN) ||
+      crypto_cipher_set_key(stats_relay_cell_cipher, key)) {
+    log_warn(LD_GENERAL,
+             "failed to set random symmetric key for stats_relay_cell_cipher");
+    goto error;
+  } else {
+    log_debug(LD_GENERAL,
+             "generated random key %s for stats_relay_cell_cipher",
+             hex_str(key, CIPHER_KEY_LEN));
+  }
+
+  r = crypto_cipher_encrypt_init_cipher(stats_relay_cell_cipher);
+
+  if (r)
+    goto error;
+  return 0;
+
+ error:
+  if (stats_relay_cell_cipher)
+    crypto_free_cipher_env(stats_relay_cell_cipher);
+  return -1;
+}
+
+void
+relay_cell_processing_stats_free_all(void)
+{
+  crypto_free_cipher_env(stats_relay_cell_cipher);
+}
+
+static void
+relay_cell_processing_stats_log(circid_t circ_id, int direction,
+                                int is_origin, or_connection_t *conn,
+                                char recognized, int is_outgoing)
+{
+  char circuit_id[CIPHER_IV_LEN];
+  char connection[CIPHER_IV_LEN];
+  char e_circuit_id[CIPHER_IV_LEN];
+  char e_connection[CIPHER_IV_LEN];
+  char s_circuit_id[CIPHER_IV_LEN * 2 + 1];
+  char s_connection[CIPHER_IV_LEN * 2 + 1];
+
+  tor_addr_t addr;
+  uint16_t port;
+  char s_addr[48];
+
+  uint32_t ip_address;
+
+  if (conn) {
+    addr = conn->_base.addr;
+    port = conn->_base.port;
+
+    if (tor_addr_to_str(s_addr, &addr, sizeof(s_addr), 0) == NULL) {
+      log_warn(LD_OR, "Could not convert tor_addr_t to string");
+      return;
+    }
+
+    /* Format plaintext for circuit ID */
+    ip_address = tor_addr_to_ipv4n(&addr);
+    tor_assert(ip_address);
+  } else {
+    strncpy(s_addr, "0.0.0.0", sizeof(s_addr));
+    ip_address = 0;
+    port = 0;
+  }
+
+  /* Sanity check */
+  /*
+  tor_assert(sizeof(circuit_id) == 16);
+  tor_assert(sizeof(ip_address) + sizeof(port) + sizeof(circ_id) == 8);
+  tor_assert(sizeof(ip_address) + sizeof(port) == 6);
+  */
+
+  memset(circuit_id, 0, sizeof(circuit_id));
+  memcpy(circuit_id, &ip_address, sizeof(ip_address));
+  memcpy(circuit_id + sizeof(ip_address), &port, sizeof(port));
+  memcpy(circuit_id + sizeof(ip_address) + sizeof(port),
+         &circ_id, sizeof(circ_id));
+
+  circuit_id[CIPHER_IV_LEN - 1] = 0;
+
+  /* Format plaintext for connection */
+  memset(connection, 0, sizeof(connection));
+  memcpy(connection, circuit_id, sizeof(ip_address) + sizeof(port));
+
+  connection[CIPHER_IV_LEN - 1] = 1;
+
+  /* CTR encrypting zero, with IV=P is equivalent to ECB encrypting P */
+
+  /* Encrypt circuit_id */
+  memset(e_circuit_id, 0, sizeof(e_circuit_id));
+  crypto_cipher_set_iv(stats_relay_cell_cipher, circuit_id);
+  crypto_cipher_crypt_inplace(stats_relay_cell_cipher, e_circuit_id,
+                              CIPHER_IV_LEN);
+
+  /* Encrypt connection */
+  memset(e_connection, 0, sizeof(e_connection));
+  crypto_cipher_set_iv(stats_relay_cell_cipher, connection);
+  crypto_cipher_crypt_inplace(stats_relay_cell_cipher, e_connection,
+                              CIPHER_IV_LEN);
+
+  log_debug(LD_OR,
+            "Cell processing -- circuit: %u connection: %s:%d "
+            "direction: %d is_origin: %d "
+            "recognized: %d is_outgoing: %d",
+            (unsigned)circ_id, s_addr, (unsigned)port,
+            direction, is_origin,
+            (int) recognized, is_outgoing);
+
+  base16_encode(s_circuit_id, sizeof(s_circuit_id),
+                e_circuit_id, sizeof(e_circuit_id));
+  base16_encode(s_connection, sizeof(s_connection),
+                e_connection, sizeof(e_connection));
+  log_notice(LD_OR,
+             "Cell processing -- circuit: %s connection: %s "
+             "direction: %d is_origin: %d "
+             "recognized: %d is_outgoing: %d",
+             s_circuit_id, ip_address?s_connection:"<>",
+             direction, is_origin,
+             (int) recognized, is_outgoing);
+}
+
 /** Update digest from the payload of cell. Assign integrity part to
  * cell.
  */
@@ -140,7 +282,8 @@
  */
 int
 circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
-                           cell_direction_t cell_direction)
+                           cell_direction_t cell_direction,
+                           or_connection_t *incoming_conn)
 {
   or_connection_t *or_conn=NULL;
   crypt_path_t *layer_hint=NULL;
@@ -159,6 +302,11 @@
     return -END_CIRC_REASON_INTERNAL;
   }
 
+  /* Log that we have received a relay cell */
+  relay_cell_processing_stats_log(cell->circ_id, cell_direction,
+                                  CIRCUIT_IS_ORIGIN(circ), incoming_conn,
+                                  recognized, 0);
+
   if (recognized) {
     edge_connection_t *conn = relay_lookup_conn(circ, cell, cell_direction,
                                                 layer_hint);
@@ -199,6 +347,11 @@
     return 0;
   }
 
+  /* Log that we are about to send a relay cell */
+  relay_cell_processing_stats_log(cell->circ_id, cell_direction,
+                                  CIRCUIT_IS_ORIGIN(circ), or_conn,
+                                  recognized, 1);
+
   if (!or_conn) {
     // XXXX Can this splice stuff be done more cleanly?
     if (! CIRCUIT_IS_ORIGIN(circ) &&
@@ -209,7 +362,7 @@
       tor_assert(splice->_base.purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
       cell->circ_id = splice->p_circ_id;
       if ((reason = circuit_receive_relay_cell(cell, TO_CIRCUIT(splice),
-                                               CELL_DIRECTION_IN)) < 0) {
+                                               CELL_DIRECTION_IN, NULL)) < 0) {
         log_warn(LD_REND, "Error relaying cell across rendezvous; closing "
                  "circuits");
         /* XXXX Do this here, or just return -1? */
Index: src/or/command.c
===================================================================
--- src/or/command.c	(revision 18962)
+++ src/or/command.c	(working copy)
@@ -415,7 +415,7 @@
     }
   }
 
-  if ((reason = circuit_receive_relay_cell(cell, circ, direction)) < 0) {
+  if ((reason = circuit_receive_relay_cell(cell, circ, direction, conn)) < 0) {
     log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit_receive_relay_cell "
            "(%s) failed. Closing.",
            direction==CELL_DIRECTION_OUT?"forward":"backward");
Index: src/or/main.c
===================================================================
--- src/or/main.c	(revision 18962)
+++ src/or/main.c	(working copy)
@@ -1822,6 +1822,9 @@
     return -1;
   }
 
+  /* Initialize the circuit statistics anonymization crypto */
+  relay_cell_processing_stats_init();
+
   return 0;
 }
 
@@ -1906,6 +1909,7 @@
   routerlist_free_all();
   networkstatus_free_all();
   addressmap_free_all();
+  relay_cell_processing_stats_free_all();
   dirserv_free_all();
   rend_service_free_all();
   rend_cache_free_all();
Index: src/common/log.c
===================================================================
--- src/common/log.c	(revision 18962)
+++ src/common/log.c	(working copy)
@@ -148,8 +148,8 @@
   t = (time_t)now.tv_sec;
 
   n = strftime(buf, buf_len, "%b %d %H:%M:%S", tor_localtime_r(&t, &tm));
-  r = tor_snprintf(buf+n, buf_len-n, ".%.3ld [%s] ",
-                   (long)now.tv_usec / 1000, sev_to_string(severity));
+  r = tor_snprintf(buf+n, buf_len-n, ".%.6ld [%s] ",
+                   (long)now.tv_usec, sev_to_string(severity));
   if (r<0)
     return buf_len-1;
   else
