copying branch over from other 2013 repo
diff --git a/aos/atom_code/atom_code.gyp b/aos/atom_code/atom_code.gyp
new file mode 100644
index 0000000..8253588
--- /dev/null
+++ b/aos/atom_code/atom_code.gyp
@@ -0,0 +1,16 @@
+{
+  'targets': [
+    {
+      'target_name': 'init',
+      'type': 'static_library',
+      'sources': [
+        '<(AOS)/atom_code/init.cc',
+      ],
+      'dependencies': [
+        '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
+        '<(AOS)/common/common.gyp:die',
+        '<(AOS)/build/aos.gyp:logging',
+      ],
+    },
+  ],
+}
diff --git a/aos/atom_code/camera/Buffers.cpp b/aos/atom_code/camera/Buffers.cpp
index 22b7337..f580f89 100644
--- a/aos/atom_code/camera/Buffers.cpp
+++ b/aos/atom_code/camera/Buffers.cpp
@@ -1,8 +1,10 @@
-#include "Buffers.h"
-#include "V4L2.h"
+#include "aos/atom_code/camera/Buffers.h"
 
 #include <sys/mman.h>
 
+#include "aos/atom_code/camera/V4L2.h"
+#include "aos/common/logging/logging.h"
+
 namespace aos {
 namespace camera {
 
diff --git a/aos/atom_code/camera/Buffers.h b/aos/atom_code/camera/Buffers.h
index 7f1206d..b72cd3e 100644
--- a/aos/atom_code/camera/Buffers.h
+++ b/aos/atom_code/camera/Buffers.h
@@ -6,7 +6,7 @@
 
 #include <string>
 
-#include "aos/aos_core.h"
+#include "aos/atom_code/ipc_lib/queue.h"
 #include "aos/common/type_traits.h"
 
 namespace aos {
diff --git a/aos/atom_code/camera/camera.gyp b/aos/atom_code/camera/camera.gyp
index e94a6ac..b8e8dd3 100644
--- a/aos/atom_code/camera/camera.gyp
+++ b/aos/atom_code/camera/camera.gyp
@@ -43,13 +43,28 @@
       'hard_dependency': 1,
     },
     {
+      'target_name': 'buffers',
+      'type': 'static_library',
+      'sources': [
+        'Buffers.cpp',
+      ],
+      'dependencies': [
+        '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
+        '<(AOS)/build/aos.gyp:logging',
+      ],
+      'export_dependent_settings': [
+        '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
+      ],
+    },
+    {
       'target_name': 'CameraHTTPStreamer',
       'type': 'executable',
       'sources': [
         'HTTPStreamer.cpp',
       ],
       'dependencies': [
-        '<(AOS)/build/aos.gyp:libaos',
+        'buffers',
+        '<(AOS)/atom_code/atom_code.gyp:init',
       ],
     },
     {
@@ -59,7 +74,8 @@
         'Reader.cpp',
       ],
       'dependencies': [
-        '<(AOS)/build/aos.gyp:libaos',
+        'buffers',
+        '<(AOS)/atom_code/atom_code.gyp:init',
       ],
     },
   ],
diff --git a/aos/atom_code/camera/jni.cpp b/aos/atom_code/camera/jni.cpp
index 7b7eafa..ad5177a 100644
--- a/aos/atom_code/camera/jni.cpp
+++ b/aos/atom_code/camera/jni.cpp
@@ -3,6 +3,8 @@
 #include "jni/aos_Natives.h"
 #include "aos/atom_code/camera/Buffers.h"
 #include "aos/externals/libjpeg/include/jpeglib.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/atom_code/init.h"
 
 using aos::camera::Buffers;
 
@@ -248,8 +250,8 @@
   } else {
     level = DEBUG;
   }
-  // can't use Get/ReleaseStringCritical because log_do might block waiting to
-  // put its message into the queue
+  // Can't use Get/ReleaseStringCritical because log_do might block waiting to
+  // put its message into the queue.
   const char *const message_chars = env->GetStringUTFChars(message, NULL);
   if (message_chars == NULL) return;
   log_do(level, "%s\n", message_chars);
diff --git a/aos/atom_code/core/BinaryLogReader.cpp b/aos/atom_code/core/BinaryLogReader.cpp
index b673c26..7027d21 100644
--- a/aos/atom_code/core/BinaryLogReader.cpp
+++ b/aos/atom_code/core/BinaryLogReader.cpp
@@ -11,16 +11,20 @@
 
 #include <map>
 
-#include "aos/aos_core.h"
+#include "aos/atom_code/logging/atom_logging.h"
 #include "aos/atom_code/core/LogFileCommon.h"
 #include "aos/common/Configuration.h"
+#include "aos/atom_code/init.h"
 
-static const char *const kCRIOName = "CRIO";
+namespace aos {
+namespace logging {
+namespace atom {
+namespace {
 
-int main() {
-  aos::InitNRT();
+int binary_log_reader_main() {
+  InitNRT();
 
-  const char *folder = aos::configuration::GetLoggingDirectory();
+  const char *folder = configuration::GetLoggingDirectory();
   if (access(folder, R_OK | W_OK) == -1) {
     LOG(FATAL, "folder '%s' does not exist. please create it\n", folder);
   }
@@ -28,19 +32,24 @@
 
   const time_t t = time(NULL);
   char *tmp;
-  if (asprintf(&tmp, "%s/aos_log-%jd", folder, static_cast<uintmax_t>(t)) == -1) {
-    fprintf(stderr, "BinaryLogReader: couldn't create final name because of %d (%s)."
+  if (asprintf(&tmp, "%s/aos_log-%jd", folder, static_cast<uintmax_t>(t)) ==
+      -1) {
+    fprintf(stderr,
+            "BinaryLogReader: couldn't create final name because of %d (%s)."
             " exiting\n", errno, strerror(errno));
     return EXIT_FAILURE;
   }
   char *tmp2;
   if (asprintf(&tmp2, "%s/aos_log-current", folder) == -1) {
-    fprintf(stderr, "BinaryLogReader: couldn't create symlink name because of %d (%s)."
+    fprintf(stderr,
+            "BinaryLogReader: couldn't create symlink name because of %d (%s)."
             " not creating current symlink\n", errno, strerror(errno));
   } else {
     if (unlink(tmp2) == -1 && (errno != EROFS && errno != ENOENT)) {
-      fprintf(stderr, "BinaryLogReader: warning: unlink('%s') failed because of %d (%s)\n",
-          tmp2, errno, strerror(errno));
+      fprintf(stderr,
+              "BinaryLogReader: warning: unlink('%s') failed"
+              " because of %d (%s)\n",
+              tmp2, errno, strerror(errno));
     }
     if (symlink(tmp, tmp2) == -1) {
       fprintf(stderr, "BinaryLogReader: warning: symlink('%s', '%s') failed"
@@ -52,11 +61,12 @@
                 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
   free(tmp);
   if (fd == -1) {
-    fprintf(stderr, "BinaryLogReader: couldn't open file '%s' because of %d (%s)."
+    fprintf(stderr,
+            "BinaryLogReader: couldn't open file '%s' because of %d (%s)."
             " exiting\n", tmp, errno, strerror(errno));
     return EXIT_FAILURE;
   }
-  aos::LogFileAccessor writer(fd, true);
+  LogFileAccessor writer(fd, true);
 
   struct timespec timespec;
   time_t last_sec = 0;
@@ -68,47 +78,39 @@
       writer.Sync();
     }
 
-    const log_message *const msg = log_read_next();
+    const LogMessage *const msg = ReadNext();
     if (msg == NULL) continue;
-    const log_crio_message *const crio_msg = reinterpret_cast<const log_crio_message *>(
-        msg);
 
-    size_t name_size, message_size;
-    if (msg->source == -1) {
-      name_size = strlen(kCRIOName);
-      message_size = strlen(crio_msg->message);
-    } else {
-      name_size = strlen(msg->name);
-      message_size = strlen(msg->message);
-    }
-    // add on size for terminating '\0'
-    name_size += 1;
-    message_size += 1;
+    // add 1 for terminating '\0'
+    size_t name_size = strlen(msg->name) + 1;
+    size_t message_size = strlen(msg->message) + 1;
 
-    aos::LogFileMessageHeader *const output = writer.GetWritePosition(
-        sizeof(aos::LogFileMessageHeader) + name_size + message_size);
+    LogFileMessageHeader *const output = writer.GetWritePosition(
+        sizeof(LogFileMessageHeader) + name_size + message_size);
     char *output_strings = reinterpret_cast<char *>(output) + sizeof(*output);
     output->name_size = name_size;
     output->message_size = message_size;
     output->source = msg->source;
-    if (msg->source == -1) {
-      output->level = crio_msg->level;
-      // TODO(brians) figure out what time to put in
-      output->sequence = crio_msg->sequence;
-      memcpy(output_strings, kCRIOName, name_size);
-      memcpy(output_strings + name_size, crio_msg->message, message_size);
-    } else {
-      output->level = msg->level;
-      output->time_sec = msg->time.tv_sec;
-      output->time_nsec = msg->time.tv_nsec;
-      output->sequence = msg->sequence;
-      memcpy(output_strings, msg->name, name_size);
-      memcpy(output_strings + name_size, msg->message, message_size);
-    }
+    output->level = msg->level;
+    output->time_sec = msg->seconds;
+    output->time_nsec = msg->nseconds;
+    output->sequence = msg->sequence;
+    memcpy(output_strings, msg->name, name_size);
+    memcpy(output_strings + name_size, msg->message, message_size);
     condition_set(&output->marker);
 
-    log_free_message(msg);
+    logging::atom::Free(msg);
   }
 
-  aos::Cleanup();
+  Cleanup();
+  return 0;
+}
+
+}  // namespace
+}  // namespace atom
+}  // namespace logging
+}  // namespace aos
+
+int main() {
+  return aos::logging::atom::binary_log_reader_main();
 }
diff --git a/aos/atom_code/core/CRIOLogReader.cpp b/aos/atom_code/core/CRIOLogReader.cpp
index f32e21d..2aa216c 100644
--- a/aos/atom_code/core/CRIOLogReader.cpp
+++ b/aos/atom_code/core/CRIOLogReader.cpp
@@ -8,44 +8,55 @@
 #include <fcntl.h>
 #include <arpa/inet.h>
 #include <errno.h>
+#include <string.h>
 
 #include <map>
 
-#include "aos/aos_core.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/atom_code/logging/atom_logging.h"
 #include "aos/common/Configuration.h"
 #include "aos/common/byteorder.h"
+#include "aos/atom_code/init.h"
+
+namespace aos {
+namespace logging {
+namespace atom {
+namespace {
 
 struct log_buffer {
-  log_crio_message msg;
+  LogMessage *msg;
   size_t used;
 
-  log_buffer() {
+  log_buffer() : msg(NULL) {
     Clear();
   }
   void Clear() {
+    logging::atom::Free(msg);
+    msg = logging::atom::Get();
     used = 0;
   }
-  // returns whether msg is now complete
+
+  // Returns whether msg is now complete.
   bool ReceiveFrom(int sock) {
-    const ssize_t ret = recv(sock, reinterpret_cast<uint8_t *>(&msg) + used,
-                             sizeof(msg) - used, 0);
+    const ssize_t ret = recv(sock, reinterpret_cast<uint8_t *>(msg) + used,
+                             sizeof(*msg) - used, 0);
     if (ret == -1) {
       LOG(ERROR, "recv(%d, %p, %d) failed because of %d: %s\n",
-          sock, reinterpret_cast<uint8_t *>(&msg) + used, sizeof(msg) - used,
+          sock, reinterpret_cast<uint8_t *>(msg) + used, sizeof(*msg) - used,
           errno, strerror(errno));
       return false;
     } else {
       used += ret;
-      if (used > sizeof(msg)) {
-        LOG(WARNING, "used(%zd) is > sizeof(msg)(%zd)\n", used, sizeof(msg));
+      if (used > sizeof(*msg)) {
+        LOG(WARNING, "used(%zd) is > sizeof(*msg)(%zd)\n", used, sizeof(*msg));
       }
-      return used >= sizeof(msg);
+      return used >= sizeof(*msg);
     }
   }
 };
 
-int main() {
-  aos::InitNRT();
+int crio_log_reader_main() {
+  InitNRT();
 
   const int sock = socket(AF_INET, SOCK_STREAM, 0);
   if (sock == -1) {
@@ -59,11 +70,11 @@
   } bind_sockaddr;
   memset(&bind_sockaddr, 0, sizeof(bind_sockaddr));
   bind_sockaddr.in.sin_family = AF_INET;
-  bind_sockaddr.in.sin_port = htons(static_cast<uint16_t>(aos::NetworkPort::kLogs));
+  bind_sockaddr.in.sin_port = htons(static_cast<uint16_t>(NetworkPort::kLogs));
   bind_sockaddr.in.sin_addr.s_addr = htonl(INADDR_ANY);
   if (bind(sock, &bind_sockaddr.addr, sizeof(bind_sockaddr.addr)) == -1) {
-    LOG(ERROR, "bind(%d, %p) failed because of %d: %s\n", sock, &bind_sockaddr.addr,
-        errno, strerror(errno));
+    LOG(ERROR, "bind(%d, %p) failed because of %d: %s\n", sock,
+        &bind_sockaddr.addr, errno, strerror(errno));
     return EXIT_FAILURE;
   }
   if (listen(sock, 10) == -1) {
@@ -109,21 +120,35 @@
             "because of %d: %s\n",
             sock, SOCK_NONBLOCK, errno, strerror(errno));
       } else {
-        socks[new_sock]; // creates using value's default constructor
+        socks[new_sock];  // creates using value's default constructor
       }
     }
 
     for (auto it = socks.begin(); it != socks.end(); ++it) {
       if (FD_ISSET(it->first, &read_fds)) {
         if (it->second.ReceiveFrom(it->first)) {
-          it->second.msg.identifier = -1;
-          it->second.msg.time = aos::ntoh(it->second.msg.time);
-          log_crio_message_send(it->second.msg);
+          it->second.msg->source = ntoh(it->second.msg->source);
+          it->second.msg->sequence = ntoh(it->second.msg->sequence);
+          it->second.msg->level = ntoh(it->second.msg->level);
+          it->second.msg->seconds = ntoh(it->second.msg->seconds);
+          it->second.msg->nseconds = ntoh(it->second.msg->nseconds);
+
+          logging::atom::Write(it->second.msg);
+          it->second.msg = NULL;
           it->second.Clear();
         }
       }
     }
   }
 
-  aos::Cleanup();
+  Cleanup();
+}
+
+}  // namespace
+}  // namespace atom
+}  // namespace logging
+}  // namespace aos
+
+int main() {
+  return aos::logging::atom::crio_log_reader_main();
 }
diff --git a/aos/atom_code/core/LogDisplayer.cpp b/aos/atom_code/core/LogDisplayer.cpp
index 9dee08f..d8d9f94 100644
--- a/aos/atom_code/core/LogDisplayer.cpp
+++ b/aos/atom_code/core/LogDisplayer.cpp
@@ -9,6 +9,7 @@
 
 #include "aos/atom_code/core/LogFileCommon.h"
 #include "aos/aos_core.h"
+#include "aos/common/logging/logging_impl.h"
 
 namespace {
 
@@ -81,7 +82,7 @@
         filter_name = optarg;
         break;
       case 'l':
-        filter_level = str_log(optarg);
+        filter_level = aos::logging::str_log(optarg);
         if (filter_level == LOG_UNKNOWN) {
           fprintf(stderr, "LogDisplayer: unknown log level '%s'\n", optarg);
           exit(EXIT_FAILURE);
@@ -121,7 +122,7 @@
   }
 
   fprintf(stderr, "displaying down to level %s from file '%s'\n",
-          log_str(filter_level), filename);
+          aos::logging::log_str(filter_level), filename);
   if (optind < argc) {
     fprintf(stderr, "non-option ARGV-elements: ");
     while (optind < argc) {
@@ -135,23 +136,33 @@
             filename, strerror(errno));
     exit(EXIT_FAILURE);
   }
-  aos::LogFileAccessor accessor(fd, false);
+  aos::logging::LogFileAccessor accessor(fd, false);
   if (!start_at_beginning) {
     accessor.MoveToEnd();
   }
-  const aos::LogFileMessageHeader *msg;
+  const aos::logging::LogFileMessageHeader *msg;
+  aos::logging::LogMessage log_message;
   do {
     msg = accessor.ReadNextMessage(follow);
     if (msg == NULL) continue;
-    if (log_gt_important(filter_level, msg->level)) continue;
+    if (aos::logging::log_gt_important(filter_level, msg->level)) continue;
     if (filter_name != NULL &&
         strcmp(filter_name, reinterpret_cast<const char *>(msg) + sizeof(*msg)) != 0) {
       continue;
     }
-    printf("%s(%"PRId32")(%03"PRId8"): %s at %010"PRIu64"s%09"PRIu64"ns: %s",
-           reinterpret_cast<const char *>(msg) + sizeof(*msg), msg->source,
-           msg->sequence, log_str(msg->level), msg->time_sec, msg->time_nsec,
-           reinterpret_cast<const char *>(msg) + sizeof(*msg) + msg->name_size);
+
+    log_message.source = msg->source;
+    log_message.sequence = msg->sequence;
+    log_message.level = msg->level;
+    log_message.seconds = msg->time_sec;
+    log_message.nseconds = msg->time_nsec;
+    strncpy(log_message.name,
+            reinterpret_cast<const char *>(msg) + sizeof(*msg),
+            sizeof(log_message.name));
+    strncpy(log_message.message,
+            reinterpret_cast<const char *>(msg) + sizeof(*msg) +
+            msg->name_size,
+            sizeof(log_message.message));
+    aos::logging::internal::PrintMessage(stdout, log_message);
   } while (msg != NULL);
 }
-
diff --git a/aos/atom_code/core/LogFileCommon.h b/aos/atom_code/core/LogFileCommon.h
index 235c55b..afb86b0 100644
--- a/aos/atom_code/core/LogFileCommon.h
+++ b/aos/atom_code/core/LogFileCommon.h
@@ -11,18 +11,22 @@
 
 #include <algorithm>
 
-#include "aos/aos_core.h"
+#include "aos/common/logging/logging_impl.h"
 
 namespace aos {
+namespace logging {
 
 // File format: {
 //   LogFileMessageHeader header;
-//   char *name; // of the process that wrote the message
+//   char *name;  // of the process that wrote the message
 //   char *message;
 // } not crossing kPageSize boundaries into the file.
 //
-// Field sizes designed to fit the various values from log_message even on
+// Field sizes designed to fit the various values from LogMessage even on
 // other machines (hopefully) because they're baked into the files.
+//
+// A lot of the fields don't have comments because they're the same as the
+// identically named fields in LogMessage.
 struct __attribute__((aligned)) LogFileMessageHeader {
   // gets condition_set once this one has been written
   // for readers keeping up with a live writer
@@ -36,18 +40,17 @@
   log_level level;
   static_assert(sizeof(level) == 1, "log_level changed size!");
 
-  uint64_t time_sec;
-  static_assert(sizeof(time_sec) >= sizeof(log_message::time.tv_sec), "tv_sec won't fit");
-  uint64_t time_nsec;
-  static_assert(sizeof(time_nsec) >= sizeof(log_message::time.tv_nsec),
+  uint32_t time_sec;
+  static_assert(sizeof(time_sec) >= sizeof(LogMessage::seconds),
+                "tv_sec won't fit");
+  uint32_t time_nsec;
+  static_assert(sizeof(time_nsec) >= sizeof(LogMessage::nseconds),
                 "tv_nsec won't fit");
 
-  int32_t source; // pid or -1 for crio
-  static_assert(sizeof(source) >= sizeof(log_message::source), "PIDs won't fit");
-  uint8_t sequence;
-  static_assert(sizeof(sequence) == sizeof(log_crio_message::sequence),
-                "something changed");
-  static_assert(sizeof(sequence) == sizeof(log_message::sequence),
+  int32_t source;
+  static_assert(sizeof(source) >= sizeof(LogMessage::source), "PIDs won't fit");
+  uint16_t sequence;
+  static_assert(sizeof(sequence) == sizeof(LogMessage::sequence),
                 "something changed");
 
   // both including the terminating '\0'
@@ -129,7 +132,7 @@
         sizeof(mutex) > kPageSize) {
       char *const temp = current_;
       MapNextPage();
-      if (condition_set_value(reinterpret_cast<mutex *>(&temp[position_]), 2) != 0) {
+      if (condition_set_value(reinterpret_cast<mutex *>(&temp[position_]), 2) == -1) {
         fprintf(stderr, "LogFileCommon: condition_set_value(%p, 2) failed with %d: %s."
                 " readers will hang\n", &temp[position_], errno, strerror(errno));
       }
@@ -186,7 +189,7 @@
   }
 };
 
-};
+}  // namespace logging
+}  // namespace aos
 
 #endif
-
diff --git a/aos/atom_code/core/LogStreamer.cpp b/aos/atom_code/core/LogStreamer.cpp
index ceec18d..ffecac8 100644
--- a/aos/atom_code/core/LogStreamer.cpp
+++ b/aos/atom_code/core/LogStreamer.cpp
@@ -10,35 +10,43 @@
 #include <fcntl.h>
 #include <inttypes.h>
 
-#include "aos/aos_core.h"
+#include "aos/atom_code/logging/atom_logging.h"
 #include "aos/atom_code/core/LogFileCommon.h"
+#include "aos/atom_code/init.h"
+#include "aos/atom_code/ipc_lib/queue.h"
+#include "aos/common/logging/logging_impl.h"
 
-static const char *const kCRIOName = "CRIO";
+namespace aos {
+namespace logging {
+namespace atom {
+namespace {
 
-int main() {
-  aos::InitNRT();
+int log_streamer_main() {
+  InitNRT();
 
   const time_t t = time(NULL);
-  printf("starting at %jd----------------------------------\n", static_cast<uintmax_t>(t));
+  printf("starting at %jd----------------------------------\n",
+         static_cast<uintmax_t>(t));
 
   int index = 0;
   while (true) {
-    const log_message *const msg = log_read_next2(BLOCK, &index);
+    const LogMessage *const msg = ReadNext(BLOCK, &index);
     if (msg == NULL) continue;
-    const log_crio_message *const crio_msg = reinterpret_cast<const log_crio_message *>(
-        msg);
 
-    if (msg->source == -1) {
-      printf("CRIO(%03"PRId8"): %s at %f: %s", crio_msg->sequence,
-             log_str(crio_msg->level), crio_msg->time, crio_msg->message);
-    } else {
-      printf("%s(%"PRId32")(%03"PRId8"): %s at %010jus%09luns: %s",
-             msg->name, msg->source, msg->sequence, log_str(msg->level),
-             static_cast<uintmax_t>(msg->time.tv_sec), msg->time.tv_nsec, msg->message);
-    }
+    internal::PrintMessage(stdout, *msg);
 
-    log_free_message(msg);
+    logging::atom::Free(msg);
   }
 
-  aos::Cleanup();
+  Cleanup();
+  return 0;
+}
+
+}  // namespace
+}  // namespace atom
+}  // namespace logging
+}  // namespace aos
+
+int main() {
+  return aos::logging::atom::log_streamer_main();
 }
diff --git a/aos/atom_code/core/core.cc b/aos/atom_code/core/core.cc
index b34169b..4179a0a 100644
--- a/aos/atom_code/core/core.cc
+++ b/aos/atom_code/core/core.cc
@@ -3,10 +3,10 @@
 // Purposes: All shared memory gets allocated here.
 //
 
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include "aos/aos_core.h"
+#include <sys/select.h>
+#include <stdlib.h>
+
+#include "aos/atom_code/init.h"
 
 int main() {
   aos::InitCreate();
diff --git a/aos/atom_code/core/core.gyp b/aos/atom_code/core/core.gyp
index 04d3cc8..4e13e51 100644
--- a/aos/atom_code/core/core.gyp
+++ b/aos/atom_code/core/core.gyp
@@ -7,7 +7,7 @@
         'core.cc',
       ],
       'dependencies': [
-        '<(AOS)/build/aos.gyp:libaos',
+        '<(AOS)/atom_code/atom_code.gyp:init',
       ],
     },
     {
@@ -17,7 +17,9 @@
         'BinaryLogReader.cpp',
       ],
       'dependencies': [
-        '<(AOS)/build/aos.gyp:libaos',
+        '<(AOS)/build/aos.gyp:logging',
+        '<(AOS)/atom_code/atom_code.gyp:init',
+        '<(AOS)/common/common.gyp:common',
       ],
     },
     {
@@ -27,7 +29,8 @@
         'LogStreamer.cpp',
       ],
       'dependencies': [
-        '<(AOS)/build/aos.gyp:libaos',
+        '<(AOS)/build/aos.gyp:logging',
+        '<(AOS)/atom_code/atom_code.gyp:init',
       ],
     },
     {
@@ -37,7 +40,8 @@
         'LogDisplayer.cpp',
       ],
       'dependencies': [
-        '<(AOS)/build/aos.gyp:libaos',
+        '<(AOS)/build/aos.gyp:logging',
+        '<(AOS)/atom_code/atom_code.gyp:init',
       ],
     },
     {
@@ -47,7 +51,8 @@
         'CRIOLogReader.cpp',
       ],
       'dependencies': [
-        '<(AOS)/build/aos.gyp:libaos',
+        '<(AOS)/build/aos.gyp:logging',
+        '<(AOS)/atom_code/atom_code.gyp:init',
       ],
     },
   ],
diff --git a/aos/atom_code/init.cc b/aos/atom_code/init.cc
index 4776261..7eecde3 100644
--- a/aos/atom_code/init.cc
+++ b/aos/atom_code/init.cc
@@ -12,8 +12,9 @@
 #include <stdlib.h>
 #include <stdint.h>
 
-#include "aos/aos_core.h"
 #include "aos/common/die.h"
+#include "aos/atom_code/logging/atom_logging.h"
+#include "aos/atom_code/ipc_lib/shared_mem.h"
 
 namespace aos {
 
@@ -69,10 +70,7 @@
     Die("%s-init: creating shared memory reference failed\n",
         program_invocation_short_name);
   }
-  if (log_init(program_invocation_short_name) != 0) {
-    Die("%s-init: initializing logging failed\n",
-        program_invocation_short_name);
-  }
+  logging::atom::Register();
 }
 
 const char *const kNoRealtimeEnvironmentVariable = "AOS_NO_REALTIME";
diff --git a/aos/atom_code/ipc_lib/mutex.cpp b/aos/atom_code/ipc_lib/mutex.cpp
index d1f0ef2..4753e78 100644
--- a/aos/atom_code/ipc_lib/mutex.cpp
+++ b/aos/atom_code/ipc_lib/mutex.cpp
@@ -2,6 +2,8 @@
 
 #include <inttypes.h>
 #include <errno.h>
+#include <stdio.h>
+#include <string.h>
 
 #include "aos/aos_core.h"
 #include "aos/common/type_traits.h"
diff --git a/aos/atom_code/ipc_lib/queue.c b/aos/atom_code/ipc_lib/queue.c
index 5cfd2ac..2e45326 100644
--- a/aos/atom_code/ipc_lib/queue.c
+++ b/aos/atom_code/ipc_lib/queue.c
@@ -6,6 +6,8 @@
 #include <errno.h>
 #include <assert.h>
 
+#include "aos/common/logging/logging.h"
+
 #define READ_DEBUG 0
 #define WRITE_DEBUG 0
 #define REF_DEBUG 0
@@ -36,7 +38,7 @@
 		abort();
 	}
 #if REF_DEBUG
-	printf("ref_free_count: %p\n", msg);
+	printf("ref free: %p\n", msg);
 #endif
 	--pool->used;
 
@@ -484,10 +486,7 @@
 		msg = pool->pool[pool->used];
 	} else {
 		if (pool->length >= pool->mem_length) {
-			//TODO(brians) log this if it isn't the log queue
-			fprintf(stderr, "queue: overused_pool\n");
-			msg = NULL;
-			goto exit;
+			LOG(FATAL, "overused pool %p\n", pool);
 		}
 		msg = pool->pool[pool->length] = aos_alloc_msg(pool);
 		++pool->length;
@@ -500,7 +499,6 @@
 #endif
 	header->index = pool->used;
 	++pool->used;
-exit:
 	mutex_unlock(&pool->pool_lock);
 	return msg;
 }
diff --git a/aos/atom_code/logging/atom_logging.cc b/aos/atom_code/logging/atom_logging.cc
new file mode 100644
index 0000000..c9b1195
--- /dev/null
+++ b/aos/atom_code/logging/atom_logging.cc
@@ -0,0 +1,145 @@
+#include "aos/atom_code/logging/atom_logging.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/prctl.h>
+
+#include <algorithm>
+
+#include "aos/common/die.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/atom_code/thread_local.h"
+#include "aos/atom_code/ipc_lib/queue.h"
+
+namespace aos {
+namespace logging {
+namespace {
+
+using internal::Context;
+
+AOS_THREAD_LOCAL Context *my_context(NULL);
+
+std::string GetMyName() {
+  // The maximum number of characters that can make up a thread name.
+  // The docs are unclear if it can be 16 characters with no '\0', so we'll be
+  // safe by adding our own where necessary.
+  static const size_t kThreadNameLength = 16;
+
+  std::string process_name(program_invocation_short_name);
+
+  char thread_name_array[kThreadNameLength + 1];
+  if (prctl(PR_GET_NAME, thread_name_array) != 0) {
+    Die("prctl(PR_GET_NAME, %p) failed with %d: %s\n",
+        thread_name_array, errno, strerror(errno));
+  }
+  thread_name_array[sizeof(thread_name_array) - 1] = '\0';
+  std::string thread_name(thread_name_array);
+
+  // If the first bunch of characters are the same.
+  // We cut off comparing at the shorter of the 2 strings because one or the
+  // other often ends up cut off.
+  if (strncmp(thread_name.c_str(), process_name.c_str(),
+              std::min(thread_name.length(), process_name.length())) == 0) {
+    // This thread doesn't have an actual name.
+    return process_name;
+  }
+
+  return process_name + '.' + thread_name;
+}
+
+static const aos_type_sig message_sig = {sizeof(LogMessage), 1234, 1500};
+static aos_queue *queue;
+
+}  // namespace
+namespace internal {
+
+Context *Context::Get() {
+  if (my_context == NULL) {
+    my_context = new Context();
+    std::string name = GetMyName();
+    char *name_chars = new char[name.size() + 1];
+    my_context->name_size = std::min(name.size() + 1, sizeof(LogMessage::name));
+    memcpy(name_chars, name.c_str(), my_context->name_size);
+    name_chars[my_context->name_size - 1] = '\0';
+    my_context->name = name_chars;
+    my_context->source = getpid();
+  }
+  return my_context;
+}
+
+void Context::Delete() {
+  delete my_context;
+  my_context = NULL;
+}
+
+}  // namespace internal
+namespace atom {
+namespace {
+
+class AtomLogImplementation : public LogImplementation {
+  virtual void DoLog(log_level level, const char *format, va_list ap) {
+    LogMessage *message = static_cast<LogMessage *>(aos_queue_get_msg(queue));
+    if (message == NULL) {
+      LOG(FATAL, "queue get message failed\n");
+    }
+
+    internal::FillInMessage(level, format, ap, message);
+
+    Write(message);
+  }
+};
+
+}  // namespace
+
+void Register() {
+  Init();
+
+  queue = aos_fetch_queue("LoggingQueue", &message_sig);
+  if (queue == NULL) {
+    Die("logging: couldn't fetch queue\n");
+  }
+
+  AddImplementation(new AtomLogImplementation());
+}
+
+const LogMessage *ReadNext(int flags, int *index) {
+  return static_cast<const LogMessage *>(
+      aos_queue_read_msg_index(queue, flags, index));
+}
+
+const LogMessage *ReadNext() {
+  return ReadNext(BLOCK);
+}
+
+const LogMessage *ReadNext(int flags) {
+  const LogMessage *r = NULL;
+  do {
+    r = static_cast<const LogMessage *>(aos_queue_read_msg(queue, flags));
+    // not blocking means return a NULL if that's what it gets
+  } while ((flags & BLOCK) && r == NULL);
+  return r;
+}
+
+LogMessage *Get() {
+  return static_cast<LogMessage *>(aos_queue_get_msg(queue));
+}
+
+void Free(const LogMessage *msg) {
+  aos_queue_free_msg(queue, msg);
+}
+
+void Write(LogMessage *msg) {
+  if (aos_queue_write_msg_free(queue, msg, OVERRIDE) < 0) {
+    LOG(FATAL, "writing failed");
+  }
+}
+
+}  // namespace atom
+}  // namespace logging
+}  // namespace aos
diff --git a/aos/atom_code/logging/atom_logging.cpp b/aos/atom_code/logging/atom_logging.cpp
deleted file mode 100644
index e98078b..0000000
--- a/aos/atom_code/logging/atom_logging.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include <algorithm>
-
-#include "aos/aos_core.h"
-#include "aos/common/die.h"
-
-#define DECL_LEVEL(name, value) const log_level name = value;
-DECL_LEVELS
-#undef DECL_LEVEL
-
-log_level log_min = 0;
-
-static const aos_type_sig message_sig = {sizeof(log_queue_message), 1234, 1500};
-static const char *name;
-static size_t name_size;
-static aos_queue *queue;
-static log_message corked_message;
-static int cork_line_min, cork_line_max;
-bool log_initted = false;
-
-static inline void cork_init() {
-  corked_message.message[0] = '\0'; // make strlen of it 0
-  cork_line_min = INT_MAX;
-  cork_line_max = -1;
-}
-int log_init(const char *name_in){
-  if (log_initted) {
-    return 1;
-  }
-
-  const size_t name_in_len = strlen(name_in);
-  const char *last_slash = static_cast<const char *>(memrchr(name_in,
-                                                                   '/', name_in_len));
-  if (last_slash == NULL) {
-    name_size = name_in_len;
-    last_slash = name_in - 1;
-  } else {
-    name_size = name_in + name_in_len - last_slash;
-  }
-  if (name_size >= sizeof(log_message::name)) {
-    fprintf(stderr, "logging: error: name '%s' (going to use %zu bytes) is too long\n",
-        name_in, name_size);
-    return -1;
-  }
-  char *const tmp = static_cast<char *>(malloc(name_size + 1));
-  if (tmp == NULL) {
-    fprintf(stderr, "logging: error: couldn't malloc(%zd)\n", name_size + 1);
-    return -1;
-  }
-  name = tmp;
-  memcpy(tmp, last_slash + 1, name_size);
-  tmp[name_size] = 0;
-  queue = aos_fetch_queue("LoggingQueue", &message_sig);
-  if (queue == NULL) {
-    fprintf(stderr, "logging: error: couldn't fetch queue\n");
-    return -1;
-  }
-
-  cork_init();
-
-  log_initted = true;
-  return 0;
-}
-void log_uninit() {
-  free(const_cast<char *>(name));
-  name = NULL;
-  name_size = 0;
-  queue = NULL;
-  log_initted = false;
-}
-
-static inline void check_init() {
-	if (!log_initted) {
-		fprintf(stderr, "logging: warning: not initialized in %jd."
-            " initializing using \"<null>\" as name\n", static_cast<intmax_t>(getpid()));
-		log_init("<null>");
-	}
-}
-
-const log_message *log_read_next2(int flags, int *index) {
-  check_init();
-  return static_cast<const log_message *>(aos_queue_read_msg_index(queue, flags, index));
-}
-const log_message *log_read_next1(int flags) {
-	check_init();
-	const log_message *r = NULL;
-	do {
-		r = static_cast<const log_message *>(aos_queue_read_msg(queue, flags));
-	} while ((flags & BLOCK) && r == NULL); // not blocking means return a NULL if that's what it gets
-	return r;
-}
-void log_free_message(const log_message *msg) {
-	check_init();
-	aos_queue_free_msg(queue, msg);
-}
-
-int log_crio_message_send(log_crio_message &to_send) {
-	check_init();
-
-	log_crio_message *msg = static_cast<log_crio_message *>(aos_queue_get_msg(queue));
-	if (msg == NULL) {
-		fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n",
-            to_send.message);
-		return -1;
-	}
-  //*msg = to_send;
-  static_assert(sizeof(to_send) == sizeof(*msg), "something is very wrong here");
-  memcpy(msg, &to_send, sizeof(to_send));
-	if (aos_queue_write_msg(queue, msg, OVERRIDE) < 0) {
-		fprintf(stderr, "logging: error: writing crio message '%s' failed\n", msg->message);
-		aos_queue_free_msg(queue, msg);
-		return -1;
-	}
-
-	return 0;
-}
-
-// Prints format (with ap) into output and correctly deals with the message
-// being too long etc.
-// Returns whether it succeeded or not.
-static inline bool vsprintf_in(char *output, size_t output_size,
-                               const char *format, va_list ap) {
-	static const char *continued = "...\n";
-	const size_t size = output_size - strlen(continued);
-	const int ret = vsnprintf(output, size, format, ap);
-	if (ret < 0) {
-		fprintf(stderr, "logging: error: vsnprintf failed with %d (%s)\n",
-            errno, strerror(errno));
-    return false;
-	} else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) {
-		// overwrite the NULL at the end of the existing one and
-    // copy in the one on the end of continued
-		memcpy(&output[size - 1], continued, strlen(continued) + 1);
-	}
-  return true;
-}
-static inline bool write_message(log_message *msg, log_level level) {
-	msg->level = level;
-	msg->source = getpid();
-	memcpy(msg->name, name, name_size + 1);
-	if (clock_gettime(CLOCK_REALTIME, &msg->time) == -1) {
-		fprintf(stderr, "logging: warning: couldn't get the current time "
-            "because of %d (%s)\n", errno, strerror(errno));
-		msg->time.tv_sec = 0;
-		msg->time.tv_nsec = 0;
-	}
-
-  static uint8_t local_sequence = -1;
-  msg->sequence = ++local_sequence;
-
-	if (aos_queue_write_msg(queue, msg, OVERRIDE) < 0) {
-		fprintf(stderr, "logging: error: writing message '%s' failed\n", msg->message);
-		aos_queue_free_msg(queue, msg);
-    return false;
-	}
-  return true;
-}
-static inline int vlog_do(log_level level, const char *format, va_list ap) {
-	log_message *msg = static_cast<log_message *>(aos_queue_get_msg(queue));
-	if (msg == NULL) {
-		fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n", format);
-		return -1;
-	}
-
-  if (!vsprintf_in(msg->message, sizeof(msg->message), format, ap)) {
-    return -1;
-  }
-
-  if (!write_message(msg, level)) {
-    return -1;
-  }
-
-  if (level == FATAL) {
-    aos::Die("%s", msg->message);
-  }
-
-	return 0;
-}
-int log_do(log_level level, const char *format, ...) {
-	check_init();
-	va_list ap;
-	va_start(ap, format);
-	const int ret = vlog_do(level, format, ap);
-	va_end(ap);
-  return ret;
-}
-
-static inline int vlog_cork(int line, const char *format, va_list ap) {
-  const size_t message_length = strlen(corked_message.message);
-  if (line > cork_line_max) cork_line_max = line;
-  if (line < cork_line_min) cork_line_min = line;
-  return vsprintf_in(corked_message.message + message_length,
-                     sizeof(corked_message.message) - message_length, format, ap) ? 0 : -1;
-}
-int log_cork(int line, const char *format, ...) {
-  check_init();
-	va_list ap;
-	va_start(ap, format);
-	const int ret = vlog_cork(line, format, ap);
-	va_end(ap);
-  return ret;
-}
-static inline bool log_uncork_helper(char *output, size_t output_size,
-                                     const char *format, ...) {
-  check_init();
-	va_list ap;
-	va_start(ap, format);
-	const bool ret = vsprintf_in(output, output_size, format, ap);
-	va_end(ap);
-  return ret;
-}
-int log_uncork(int line, log_level level, const char *begin_format,
-               const char *format, ...) {
-  check_init();
-	va_list ap;
-	va_start(ap, format);
-	const int ret = vlog_cork(line, format, ap);
-	va_end(ap);
-  if (ret != 0) {
-    return ret;
-  }
-
-	log_message *msg = static_cast<log_message *>(aos_queue_get_msg(queue));
-	if (msg == NULL) {
-		fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n", format);
-    cork_init();
-		return -1;
-  }
-
-  static char new_format[LOG_MESSAGE_LEN];
-  if (!log_uncork_helper(new_format, sizeof(new_format), begin_format,
-                         cork_line_min, cork_line_max)) {
-    cork_init();
-    return -1;
-  }
-  const size_t new_length = strlen(new_format);
-  memcpy(msg->message, new_format, new_length);
-  memcpy(msg->message + new_length, corked_message.message,
-          std::min(strlen(corked_message.message) + 1,
-              sizeof(msg->message) - new_length));
-  // in case corked_message.message was too long, it'll still be NULL-terminated
-  msg->message[sizeof(msg->message) - 1] = '\0';
-  cork_init();
-
-  if (!write_message(msg, level)) {
-    return -1;
-  }
-
-  return 0;
-}
-
diff --git a/aos/atom_code/logging/atom_logging.h b/aos/atom_code/logging/atom_logging.h
index 6211e22..45f2a07 100644
--- a/aos/atom_code/logging/atom_logging.h
+++ b/aos/atom_code/logging/atom_logging.h
@@ -1,109 +1,30 @@
 #ifndef AOS_ATOM_CODE_LOGGING_LOGGING_H_
 #define AOS_ATOM_CODE_LOGGING_LOGGING_H_
 
-// IWYU pragma: private, include "aos/common/logging/logging.h"
+#include "aos/common/logging/logging_impl.h"
 
-#ifndef AOS_COMMON_LOGGING_LOGGING_H_
-#error This file may only be #included through common/logging/logging.h!!!
-#endif
+namespace aos {
+namespace logging {
+namespace atom {
 
-#include "aos/aos_core.h"
+// Calls AddImplementation to register the usual atom logging implementation
+// which sends the messages through a queue. This implementation relies on
+// another process(es) to read the log messages that it puts into the queue.
+// It gets called by aos::Init*.
+void Register();
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+// Fairly simple wrappers around the raw queue calls.
 
-int log_init(const char *name);
-// WARNING: THIS LEAKS MEMORY AND SHARED MEMORY
-void log_uninit(void);
+// This one never returns NULL if flags contains BLOCK.
+const LogMessage *ReadNext(int flags);
+const LogMessage *ReadNext(int flags, int *index);
+const LogMessage *ReadNext();
+LogMessage *Get();
+void Free(const LogMessage *msg);
+void Write(LogMessage *msg);
 
-extern log_level log_min;
-
-// The basic structure that goes into the shared memory queue.
-// It is packed so the pid_t at the front is at the same location as
-// the one in log_crio_message.
-typedef struct log_message_t_ {
-	pid_t source;
-	log_level level;
-	char message[LOG_MESSAGE_LEN];
-	char name[40];
-	struct timespec time;
-  uint8_t sequence; // per process
-} __attribute__((packed)) log_message;
-
-#ifdef __cplusplus
-#define LOG_BOOL bool
-#else
-#define LOG_BOOL uint8_t
-#endif
-extern LOG_BOOL log_initted;
-#undef LOG_BOOL
-
-// Unless explicitly stated otherwise, format must always be a string constant
-// and args are printf-style arguments for format.
-// The validitiy of format and args together will be checked at compile time
-// using a gcc function attribute.
-
-// Logs the specified thing.
-#define LOG(level, format, args...) do { \
-	if (level >= log_min) { \
-		log_do(level, LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": " format, ##args); \
-	} \
-} while (0)
-// Allows "bottling up" multiple log fragments which can then all be logged in
-// one message with LOG_UNCORK.
-// format does not have to be a constant
-#define LOG_CORK(format, args...) do { \
-  log_cork(__LINE__, format, ##args); \
-} while (0)
-// Actually logs all of the saved up log fragments (including format and args on
-// the end).
-#define LOG_UNCORK(level, format, args...) do { \
-  log_uncork(__LINE__, level, LOG_SOURCENAME ": %d-%d: ", format, ##args); \
-} while (0)
-// Makes a normal logging call if possible or just prints it out on stderr.
-#define LOG_IFINIT(level, format, args...) do{ \
-	if(log_initted) { \
-		LOG(level, format, args); \
-	} else { \
-		fprintf(stderr, "%s-noinit: " format, log_str(level), ##args); \
-	} \
-}while(0)
-
-// All functions return 0 for success and - for error.
-
-// Actually implements the basic logging call.
-// Does not check that level is valid.
-// TODO(brians): Fix this so it works with clang.
-int log_do(log_level level, const char *format, ...)
-  __attribute__((format(gnu_printf, 2, 3)));
-
-// TODO(brians): Fix this so it works with clang.
-int log_cork(int line, const char *format, ...)
-  __attribute__((format(gnu_printf, 2, 3)));
-// Implements the uncork logging call.
-// IMPORTANT: begin_format must have 2 %d formats as its only 2 format specifiers
-// which will get passed the minimum and maximum line numbers that have been
-// corked into this call.
-// TODO(brians): Fix this so it works with clang.
-int log_uncork(int line, log_level level, const char *begin_format,
-               const char *format, ...)
-  __attribute__((format(gnu_printf, 4, 5)));
-
-const log_message *log_read_next1(int flags);
-const log_message *log_read_next2(int flags, int *index);
-inline const log_message *log_read_next(void) { return log_read_next1(BLOCK); }
-void log_free_message(const log_message *msg);
-
-// The structure that is actually in the shared memory queue.
-union log_queue_message {
-  log_message atom;
-  log_crio_message crio;
-};
-
-#ifdef __cplusplus
-}
-#endif
+}  // namespace atom
+}  // namespace logging
+}  // namespace aos
 
 #endif
-
diff --git a/aos/atom_code/logging/atom_logging_test.cpp b/aos/atom_code/logging/atom_logging_test.cpp
deleted file mode 100644
index a97d1e9..0000000
--- a/aos/atom_code/logging/atom_logging_test.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include <string>
-
-#include "gtest/gtest.h"
-
-#include "aos/aos_core.h"
-#include "aos/atom_code/ipc_lib/sharedmem_test_setup.h"
-#include "aos/common/control_loop/Timing.h"
-#include "aos/common/inttypes.h"
-#include "aos/common/time.h"
-
-using ::aos::time::Time;
-using ::testing::AssertionResult;
-using ::testing::AssertionSuccess;
-using ::testing::AssertionFailure;
-
-namespace aos {
-namespace testing {
-
-static const std::string kLoggingName = "LoggingTestName";
-
-class LoggingTest : public SharedMemTestSetup {
-  virtual void SetUp() {
-    SharedMemTestSetup::SetUp();
-    ASSERT_EQ(0, log_init(kLoggingName.c_str()));
-  }
-  virtual void TearDown() {
-    log_uninit();
-    SharedMemTestSetup::TearDown();
-  }
-
- public:
-  AssertionResult WasAnythingLogged() {
-    const log_message *msg = log_read_next1(NON_BLOCK);
-    if (msg != NULL) {
-      char bad_msg[LOG_MESSAGE_LEN];
-      memcpy(bad_msg, msg->message, sizeof(bad_msg));
-      log_free_message(msg);
-      return AssertionSuccess() << "read message '" << bad_msg << "'";
-    }
-    return AssertionFailure();
-  }
-  AssertionResult WasLogged(log_level level, const std::string value) {
-    const log_message *msg = NULL;
-    char bad_msg[LOG_MESSAGE_LEN];
-    bad_msg[0] = '\0';
-    const pid_t owner = getpid();
-    while (true) {
-      if (msg != NULL) {
-        static_assert(sizeof(bad_msg) == sizeof(msg->message),
-                      "something is wrong");
-        if (bad_msg[0] != '\0') {
-          printf("read bad message: %s", bad_msg);
-        }
-        memcpy(bad_msg, msg->message, sizeof(bad_msg));
-        log_free_message(msg);
-        msg = NULL;
-      }
-      msg = log_read_next1(NON_BLOCK);
-      if (msg == NULL) {
-        return AssertionFailure() << "last message read was '" << bad_msg << "'"
-            " instead of '" << value << "'";
-      }
-      if (msg->source != owner) continue;
-      if (msg->level != level) continue;
-      if (strcmp(msg->name, kLoggingName.c_str()) != 0) continue;
-      if (strcmp(msg->message + strlen(msg->message) - value.length(),
-                 value.c_str()) != 0) continue;
-
-      // if it's gotten this far, then the message is correct
-      log_free_message(msg);
-      return AssertionSuccess();
-    }
-  }
-};
-typedef LoggingTest LoggingDeathTest;
-
-// Tests both basic logging functionality and that the test setup works
-// correctly.
-TEST_F(LoggingTest, Basic) {
-  EXPECT_FALSE(WasAnythingLogged());
-  LOG(INFO, "test log 1\n");
-  EXPECT_TRUE(WasLogged(INFO, "test log 1\n"));
-  LOG(INFO, "test log 1.5\n");
-  // there's a subtle typo on purpose...
-  EXPECT_FALSE(WasLogged(INFO, "test log 15\n"));
-  LOG(ERROR, "test log 2=%d\n", 55);
-  EXPECT_TRUE(WasLogged(ERROR, "test log 2=55\n"));
-  LOG(ERROR, "test log 3\n");
-  EXPECT_FALSE(WasLogged(WARNING, "test log 3\n"));
-  LOG(WARNING, "test log 4\n");
-  EXPECT_TRUE(WasAnythingLogged());
-}
-TEST_F(LoggingTest, Cork) {
-  static const int begin_line = __LINE__;
-  LOG_CORK("first part ");
-  LOG_CORK("second part (=%d) ", 19);
-  EXPECT_FALSE(WasAnythingLogged());
-  LOG_CORK("third part ");
-  static const int end_line = __LINE__;
-  LOG_UNCORK(WARNING, "last part %d\n", 5);
-  char *expected;
-  ASSERT_GT(asprintf(&expected, "atom_logging_test.cpp: %d-%d: "
-                        "first part second part (=19) third part last part 5\n",
-                        begin_line + 1, end_line + 1), 0);
-  EXPECT_TRUE(WasLogged(WARNING, std::string(expected)));
-}
-
-TEST_F(LoggingDeathTest, Fatal) {
-  ASSERT_EXIT(LOG(FATAL, "this should crash it\n"),
-              ::testing::KilledBySignal(SIGABRT),
-              "this should crash it");
-}
-
-TEST_F(LoggingTest, PrintfDirectives) {
-  LOG(INFO, "test log %%1 %%d\n");
-  EXPECT_TRUE(WasLogged(INFO, "test log %1 %d\n"));
-  LOG_DYNAMIC(WARNING, "test log %%2 %%f\n");
-  EXPECT_TRUE(WasLogged(WARNING, "test log %2 %f\n"));
-  LOG_CORK("log 3 part %%1 %%d ");
-  LOG_UNCORK(DEBUG, "log 3 part %%2 %%f\n");
-  EXPECT_TRUE(WasLogged(DEBUG, "log 3 part %1 %d log 3 part %2 %f\n"));
-}
-
-// For writing only.
-TEST_F(LoggingTest, Timing) {
-  static const long kTimingCycles = 5000;
-  Time start = Time::Now();
-  for (long i = 0; i < kTimingCycles; ++i) {
-    LOG(INFO, "a\n");
-  }
-  Time elapsed = Time::Now() - start;
-
-  printf("short message took %"PRId64" nsec for %ld\n", elapsed.ToNSec(),
-      kTimingCycles);
-
-  start = Time::Now();
-  for (long i = 0; i < kTimingCycles; ++i) {
-    LOG(INFO, "something longer than just \"a\" to log to test timing\n");
-  }
-  elapsed = Time::Now() - start;
-  printf("long message took %"PRId64" nsec for %ld\n", elapsed.ToNSec(),
-      kTimingCycles);
-}
-
-}  // namespace testing
-}  // namespace aos
diff --git a/aos/atom_code/logging/logging.gyp b/aos/atom_code/logging/logging.gyp
index 5d29da8..dfb189c 100644
--- a/aos/atom_code/logging/logging.gyp
+++ b/aos/atom_code/logging/logging.gyp
@@ -1,15 +1,4 @@
 {
   'targets': [
-    {
-      'target_name': 'atom_logging_test',
-      'type': 'executable',
-      'sources': [
-        'atom_logging_test.cpp',
-      ],
-      'dependencies': [
-        '<(EXTERNALS):gtest',
-        '<(AOS)/build/aos.gyp:libaos',
-      ],
-    },
   ],
 }
diff --git a/aos/atom_code/output/HTTPServer.cpp b/aos/atom_code/output/HTTPServer.cpp
index 88e3c6f..b0d6b93 100644
--- a/aos/atom_code/output/HTTPServer.cpp
+++ b/aos/atom_code/output/HTTPServer.cpp
@@ -4,6 +4,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <string.h>
 
 #include <memory>
 
diff --git a/aos/atom_code/starter/starter.cpp b/aos/atom_code/starter/starter.cpp
index 0cfddd5..e47e213 100644
--- a/aos/atom_code/starter/starter.cpp
+++ b/aos/atom_code/starter/starter.cpp
@@ -8,54 +8,56 @@
 #include <sys/inotify.h>
 #include <sys/stat.h>
 
-#include "aos/aos_core.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/atom_code/init.h"
 
 void niceexit(int status);
 
 pid_t start(const char *cmd, uint8_t times) {
   char *which_cmd, *which_cmd_stm;
   if (asprintf(&which_cmd, "which %s", cmd) == -1) {
-    LOG_IFINIT(ERROR, "creating \"which %s\" failed with %d: %s\n",
-               cmd, errno, strerror(errno));
+    LOG(ERROR, "creating \"which %s\" failed with %d: %s\n",
+        cmd, errno, strerror(errno));
     niceexit(EXIT_FAILURE);
   }
   if (asprintf(&which_cmd_stm, "which %s.stm", cmd) == -1) {
-    LOG_IFINIT(ERROR, "creating \"which %s.stm\" failed with %d: %s\n",
-               cmd, errno, strerror(errno));
+    LOG(ERROR, "creating \"which %s.stm\" failed with %d: %s\n",
+        cmd, errno, strerror(errno));
     niceexit(EXIT_FAILURE);
   }
   FILE *which = popen(which_cmd, "r");
   char exe[CMDLEN + 5], orig_exe[CMDLEN];
   size_t ret;
   if ((ret = fread(orig_exe, 1, sizeof(orig_exe), which)) == CMDLEN) {
-    LOG_IFINIT(ERROR, "`which %s` was too long. not starting '%s'\n", cmd, cmd);
+    LOG(ERROR, "`which %s` was too long. not starting '%s'\n", cmd, cmd);
     return 0;
   }
   orig_exe[ret] = '\0';
   if (pclose(which) == -1) {
-    LOG_IFINIT(WARNING, "pclose failed with %d: %s\n", errno, strerror(errno));
+    LOG(WARNING, "pclose failed with %d: %s\n", errno, strerror(errno));
   }
   free(which_cmd);
   if (strlen(orig_exe) == 0) { // which returned nothing; check if stm exists
-    LOG_IFINIT(INFO, "%s didn't exist. trying %s.stm\n", cmd, cmd);
+    LOG(INFO, "%s didn't exist. trying %s.stm\n", cmd, cmd);
     FILE *which_stm = popen(which_cmd_stm, "r");
     if ((ret = fread(orig_exe, 1, sizeof(orig_exe), which_stm)) == CMDLEN) {
-      LOG_IFINIT(ERROR, "`which %s.stm` was too long. not starting %s\n", cmd, cmd);
+      LOG(ERROR, "`which %s.stm` was too long. not starting %s\n", cmd, cmd);
       return 0;
     }
     orig_exe[ret] = '\0';
     if (pclose(which) == -1) {
-      LOG_IFINIT(WARNING, "pclose failed with %d: %s\n", errno, strerror(errno));
+      LOG(WARNING, "pclose failed with %d: %s\n", errno, strerror(errno));
     }
   }
   if (strlen(orig_exe) == 0) {
-    LOG_IFINIT(WARNING, "couldn't find file '%s[.stm]'. not going to start it\n",
-               cmd);
+    LOG(WARNING, "couldn't find file '%s[.stm]'. not going to start it\n",
+        cmd);
     return 0;
   }
   if (orig_exe[strlen(orig_exe) - 1] != '\n') {
-    LOG_IFINIT(WARNING, "no \\n on the end of `which %s[.stm]` output ('%s')\n",
-               cmd, orig_exe);
+    LOG(WARNING, "no \\n on the end of `which %s[.stm]` output ('%s')\n",
+        cmd, orig_exe);
   } else {
     orig_exe[strlen(orig_exe) - 1] = '\0'; // get rid of the \n
   }
@@ -65,11 +67,11 @@
   struct stat st;
   errno = 0;
   if (stat(orig_exe, &st) != 0 && errno != ENOENT) {
-    LOG_IFINIT(ERROR, "killing everything because stat('%s') failed with %d: %s\n",
-               orig_exe, errno, strerror(errno));
+    LOG(ERROR, "killing everything because stat('%s') failed with %d: %s\n",
+        orig_exe, errno, strerror(errno));
     niceexit(EXIT_FAILURE);
   } else if (errno == ENOENT) {
-    LOG_IFINIT(WARNING, "binary '%s' doesn't exist. not starting it\n", orig_exe);
+    LOG(WARNING, "binary '%s' doesn't exist. not starting it\n", orig_exe);
     return 0;
   }
   struct stat st2;
@@ -78,38 +80,38 @@
   if (!orig_zero) {
     // if it failed and it wasn't because it was missing
     if (unlink(exe) != 0 && (errno != EROFS && errno != ENOENT)) {
-      LOG_IFINIT(ERROR,
-                 "killing everything because unlink('%s') failed with %d: %s\n",
-                 exe, errno, strerror(errno));
+      LOG(ERROR,
+          "killing everything because unlink('%s') failed with %d: %s\n",
+          exe, errno, strerror(errno));
       niceexit(EXIT_FAILURE);
     }
     if (link(orig_exe, exe) != 0) {
-      LOG_IFINIT(ERROR,
-                 "killing everything because link('%s', '%s') failed with %d: %s\n",
-                 orig_exe, exe, errno, strerror(errno));
+      LOG(ERROR,
+          "killing everything because link('%s', '%s') failed with %d: %s\n",
+          orig_exe, exe, errno, strerror(errno));
       niceexit(EXIT_FAILURE);
     }
   }
   if (errno == EEXIST) {
-    LOG_IFINIT(INFO, "exe ('%s') already existed\n", exe);
+    LOG(INFO, "exe ('%s') already existed\n", exe);
   }
 
   pid_t child;
   if ((child = fork()) == 0) {
     execlp(exe, orig_exe, static_cast<char *>(NULL));
-    LOG_IFINIT(ERROR,
-               "killing everything because execlp('%s', '%s', NULL) "
-               "failed with %d: %s\n",
-               exe, cmd, errno, strerror(errno));
+    LOG(ERROR,
+        "killing everything because execlp('%s', '%s', NULL) "
+        "failed with %d: %s\n",
+        exe, cmd, errno, strerror(errno));
     _exit(EXIT_FAILURE); // don't niceexit or anything because this is the child!!
   }
   if (child == -1) {
-    LOG_IFINIT(WARNING, "fork on '%s' failed with %d: %s",
-               cmd, errno, strerror(errno));
+    LOG(WARNING, "fork on '%s' failed with %d: %s",
+        cmd, errno, strerror(errno));
     if (times < 100) {
       return start(cmd, times + 1);
     } else {
-      LOG_IFINIT(ERROR, "tried to start '%s' too many times. giving up\n", cmd);
+      LOG(ERROR, "tried to start '%s' too many times. giving up\n", cmd);
       return 0;
     }
   } else {
@@ -117,9 +119,9 @@
     files[child] = orig_exe;
     int ret = inotify_add_watch(notifyfd, orig_exe, IN_ATTRIB | IN_MODIFY);
     if (ret < 0) {
-      LOG_IFINIT(WARNING, "inotify_add_watch('%s') failed: "
-                 "not going to watch for changes to it because of %d: %s\n",
-                 orig_exe, errno, strerror(errno));
+      LOG(WARNING, "inotify_add_watch('%s') failed: "
+          "not going to watch for changes to it because of %d: %s\n",
+          orig_exe, errno, strerror(errno));
     } else {
       watches[ret] = child;
       mtimes[ret] = st2.st_mtime;
@@ -164,6 +166,8 @@
 
   atexit(exit_handler);
 
+  aos::logging::Init();
+
   notifyfd = inotify_init1(IN_NONBLOCK);
 
   pid_t core = start("core", 0);
@@ -330,18 +334,6 @@
           niceexit(EXIT_FAILURE);
         }
 
-        /*// remove all of the watches assigned to the pid that just died
-        for (auto it = watches.begin(); it != watches.end(); ++it) {
-          if (it->second == infop.si_pid) {
-            watches_to_ignore.insert(it->first);
-          }
-        }
-        for (auto it = watches_to_ignore.begin();
-             it != watches_to_ignore.end(); ++it) {
-          LOG(DEBUG, "watch id %d was on PID %d\n", *it, infop.si_pid);
-          watches.erase(*it);
-        }*/
-
         start(children[infop.si_pid], 0);
         LOG(DEBUG, "erasing %d from children\n", infop.si_pid);
         children.erase(infop.si_pid);
diff --git a/aos/atom_code/starter/starter.gyp b/aos/atom_code/starter/starter.gyp
index 44e4b08..cff6d07 100644
--- a/aos/atom_code/starter/starter.gyp
+++ b/aos/atom_code/starter/starter.gyp
@@ -7,7 +7,7 @@
         'starter.cpp',
       ],
       'dependencies': [
-        '<(AOS)/build/aos.gyp:libaos',
+        '<(AOS)/atom_code/atom_code.gyp:init',
       ],
       'copies': [
         {
diff --git a/aos/atom_code/thread_local.h b/aos/atom_code/thread_local.h
new file mode 100644
index 0000000..9563a87
--- /dev/null
+++ b/aos/atom_code/thread_local.h
@@ -0,0 +1,13 @@
+#ifndef AOS_ATOM_CODE_THREAD_LOCAL_H_
+#define AOS_ATOM_CODE_THREAD_LOCAL_H_
+
+// The storage class to use when declaring thread-local variables. This provides
+// a single place to change it if/when we want to switch to something standard.
+//
+// Example: AOS_THREAD_LOCAL void *bla;  // at namespace (aka global) scope
+//
+// C++11 has thread_local, but it's not clear whether Clang supports that as of
+// 12/18/12.
+#define AOS_THREAD_LOCAL __thread
+
+#endif  // AOS_ATOM_CODE_THREAD_LOCAL_H_
diff --git a/aos/build/aos.gyp b/aos/build/aos.gyp
index b6bd3ee..0c1506c 100644
--- a/aos/build/aos.gyp
+++ b/aos/build/aos.gyp
@@ -8,17 +8,12 @@
     'conditions': [
       ['OS=="crio"', {
           'libaos_source_files': [
-            '<!@(find <(AOS)/crio/controls <(AOS)/crio/messages <(AOS)/crio/motor_server <(AOS)/crio/shared_libs -name *.c -or -name *.cpp -or -name *.cc)',
+            '<!@(find <(AOS)/crio/controls <(AOS)/crio/messages <(AOS)/crio/motor_server -name *.c -or -name *.cpp -or -name *.cc)',
             '<(AOS)/crio/Talon.cpp',
-            '<(AOS)/common/die.cc',
           ],
         }, {
           'libaos_source_files': [
-            '<(AOS)/atom_code/camera/Buffers.cpp',
             '<(AOS)/atom_code/async_action/AsyncAction_real.cpp',
-            '<(AOS)/atom_code/init.cc',
-            '<(AOS)/atom_code/ipc_lib/mutex.cpp',
-            '<(AOS)/common/die.cc',
           ],
         }
       ],
@@ -28,17 +23,20 @@
     {
       'target_name': 'logging',
       'type': 'static_library',
+      'sources': [
+        '<(AOS)/common/logging/logging_impl.cc',
+      ],
       'conditions': [
         ['OS=="crio"', {
           'sources': [
-            '<(AOS)/crio/logging/crio_logging.cpp',
+            '<(AOS)/crio/logging/crio_logging.cc',
           ],
           'dependencies': [
             '<(EXTERNALS):WPILib',
           ]
         }, {
           'sources': [
-            '<(AOS)/atom_code/logging/atom_logging.cpp'
+            '<(AOS)/atom_code/logging/atom_logging.cc',
           ],
           'dependencies': [
             '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
@@ -50,6 +48,9 @@
       ],
       'dependencies': [
         '<(AOS)/common/common.gyp:time',
+        '<(AOS)/common/common.gyp:once',
+        '<(AOS)/common/common.gyp:mutex',
+        '<(AOS)/common/common.gyp:die',
       ],
     },
     {
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index 68439e3..d1f446a 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -19,7 +19,6 @@
         '../crio/crio.gyp:unsafe_queue_test',
         '../common/common.gyp:queue_test',
         #'../common/messages/messages.gyp:*', # TODO(brians) did this test ever exist?
-        '../atom_code/logging/logging.gyp:*',
         '../common/common.gyp:die_test',
         '../common/util/util.gyp:trapezoid_profile_test',
         'Common',
@@ -44,6 +43,7 @@
         '<(AOS)/common/common.gyp:time_test',
         '<(AOS)/common/common.gyp:mutex_test',
         '<(AOS)/common/common.gyp:once_test',
+        '<(AOS)/common/logging/logging.gyp:logging_impl_test',
       ],
     },
   ],
diff --git a/aos/common/byteorder.h b/aos/common/byteorder.h
index 3444f98..87ef39a 100644
--- a/aos/common/byteorder.h
+++ b/aos/common/byteorder.h
@@ -4,6 +4,7 @@
 #ifndef __VXWORKS__
 #include <endian.h> // endian(3)
 #endif
+#include <string.h>
 
 // Contains functions for converting between host and network byte order for
 // things other than 16/32 bit integers (those are provided by byteorder(3)).
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index 967eb51..81e2da7 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -132,12 +132,19 @@
     {
       'target_name': 'controls',
       'type': 'static_library',
-      'sources': [],
+      'sources': [
+        # 'control_loop/ControlLoop-tmpl.h',
+      ],
       'dependencies': [
         '<(AOS)/common/messages/messages.gyp:aos_queues',
         '<(AOS)/build/aos.gyp:logging',
         'timing',
       ],
+      'export_dependent_settings': [
+        '<(AOS)/common/messages/messages.gyp:aos_queues',
+        '<(AOS)/build/aos.gyp:logging',
+        'timing',
+      ],
     },
     {
       'target_name': 'queue_test',
@@ -208,6 +215,34 @@
       ],
     },
     {
+      'target_name': 'die',
+      'type': 'static_library',
+      'sources': [
+        'die.cc',
+      ],
+    },
+    {
+      'target_name': 'mutex',
+      'type': 'static_library',
+      'conditions': [
+        ['OS=="crio"', {
+          'sources': [
+            '<(AOS)/crio/shared_libs/mutex.cpp',
+          ],
+        }, {
+          'sources': [
+            '<(AOS)/atom_code/ipc_lib/mutex.cpp',
+          ],
+          'dependencies': [
+            '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
+          ],
+          'export_dependent_settings': [
+            '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
+          ],
+        }],
+      ],
+    },
+    {
       'target_name': 'mutex_test',
       'type': '<(aos_target)',
       'sources': [
diff --git a/aos/common/control_loop/ControlLoop.h b/aos/common/control_loop/ControlLoop.h
index b69cf01..e866bb7 100644
--- a/aos/common/control_loop/ControlLoop.h
+++ b/aos/common/control_loop/ControlLoop.h
@@ -5,8 +5,8 @@
 
 #include "aos/aos_core.h"
 #include "aos/common/control_loop/Timing.h"
-#include "aos/common/messages/RobotState.q.h"
 #include "aos/common/type_traits.h"
+#include "aos/common/queue.h"
 
 namespace aos {
 namespace control_loops {
diff --git a/aos/common/control_loop/Timing.cpp b/aos/common/control_loop/Timing.cpp
index 63fda44..6db179c 100644
--- a/aos/common/control_loop/Timing.cpp
+++ b/aos/common/control_loop/Timing.cpp
@@ -1,6 +1,8 @@
-#include "aos/common/logging/logging.h"
 #include "aos/common/control_loop/Timing.h"
 
+#include <string.h>
+
+#include "aos/common/logging/logging.h"
 #include "aos/common/time.h"
 
 namespace aos {
diff --git a/aos/common/inttypes.h b/aos/common/inttypes.h
index e10a33b..d243860 100644
--- a/aos/common/inttypes.h
+++ b/aos/common/inttypes.h
@@ -8,12 +8,15 @@
 #ifndef __VXWORKS__
 #include <inttypes.h>
 #else
+#define PRId64 "lld"
+
 // It warns about just "d", but not this, which is kind of weird because
 // sizeof(int) == sizeof(long) == sizeof(int32_t) == 4, but oh well.
 #define PRId32 "ld"
 #define PRIx32 "lx"
-#define PRId64 "lld"
-#define PRIu16 "u"
+#define PRIu32 "lu"
+
+#define PRIu16 "hu"
 #endif
 
 #endif  // AOS_COMMON_INTTYPES_H_
diff --git a/aos/common/logging/logging.gyp b/aos/common/logging/logging.gyp
new file mode 100644
index 0000000..52d2527
--- /dev/null
+++ b/aos/common/logging/logging.gyp
@@ -0,0 +1,15 @@
+{
+  'targets': [
+    {
+      'target_name': 'logging_impl_test',
+      'type': '<(aos_target)',
+      'sources': [
+        'logging_impl_test.cc',
+      ],
+      'dependencies': [
+        '<(EXTERNALS):gtest',
+        '<(AOS)/build/aos.gyp:libaos',
+      ],
+    },
+  ],
+}
diff --git a/aos/common/logging/logging.h b/aos/common/logging/logging.h
index e3ff839..392fc5b 100644
--- a/aos/common/logging/logging.h
+++ b/aos/common/logging/logging.h
@@ -1,102 +1,114 @@
 #ifndef AOS_COMMON_LOGGING_LOGGING_H_
-// must be kept in sync with crio/logging/crio_logging.h and atom_code/logging/atom_logging.h
 #define AOS_COMMON_LOGGING_LOGGING_H_
 
+// This file contains the logging client interface. It works with both C and C++
+// code.
+
+#include <stdio.h>
 #include <stdint.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <string.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 typedef uint8_t log_level;
+
 #define DECL_LEVELS \
 DECL_LEVEL(DEBUG, 0); /* stuff that gets printed out every cycle */ \
 DECL_LEVEL(INFO, 1); /* things like PosEdge/NegEdge */ \
-/* things that might still work if they happen occasionally but should be watched */ \
+/* things that might still work if they happen occasionally */ \
 DECL_LEVEL(WARNING, 2); \
 /*-1 so that vxworks macro of same name will have same effect if used*/ \
 DECL_LEVEL(ERROR, -1); /* errors */ \
-DECL_LEVEL(FATAL, 4); /* serious errors. the logging code will terminate the process/task */ \
+/* serious errors. the logging code will terminate the process/task */ \
+DECL_LEVEL(FATAL, 4); \
 DECL_LEVEL(LOG_UNKNOWN, 5); /* unknown logging level */
-#define DECL_LEVEL(name, value) extern const log_level name;
+#define DECL_LEVEL(name, value) static const log_level name = value;
 #undef ERROR
-DECL_LEVELS
+DECL_LEVELS;
 #undef DECL_LEVEL
 
 #define STRINGIFY(x) TO_STRING(x)
 #define TO_STRING(x) #x
 
 //not static const size_t for c code
-#define LOG_MESSAGE_LEN 300
+#define LOG_MESSAGE_LEN 500
+
+#ifdef __VXWORKS__
+// We're using ancient glibc, so sticking to just what the syscall can handle is
+// probably safer.
+#define LOG_PRINTF_FORMAT_TYPE printf
+#else
+#define LOG_PRINTF_FORMAT_TYPE gnu_printf
+#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Actually implements the basic logging call.
+// Does not check that level is valid.
+void log_do(log_level level, const char *format, ...)
+  __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 2, 3)));
+
+void log_cork(int line, const char *function, const char *format, ...)
+  __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 3, 4)));
+// Implements the uncork logging call.
+void log_uncork(int line, const char *function, log_level level,
+                const char *file, const char *format, ...)
+  __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 5, 6)));
+#ifdef __cplusplus
+}
+#endif
+
+// A magical static const char[] or string literal that communicates the name
+// of the enclosing function.
+// It's currently using __PRETTY_FUNCTION__ because both GCC and Clang support
+// that and it gives nicer results in C++ than the standard __func__ (which
+// would also work).
+#define LOG_CURRENT_FUNCTION __PRETTY_FUNCTION__
+
+// The basic logging call.
+#define LOG(level, format, args...) do {\
+  log_do(level, LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": %s: " format, \
+         LOG_CURRENT_FUNCTION, ##args); \
+  /* so that GCC knows that it won't return */ \
+  if (level == FATAL) { \
+    fprintf(stderr, "log_do(FATAL) fell through!!!!!\n"); \
+    printf("see stderr\n"); \
+    abort(); \
+  } \
+} while (0)
 
 // Allows format to not be a string constant.
-#define LOG_DYNAMIC(level, format, args...) do{ \
+#define LOG_DYNAMIC(level, format, args...) do { \
 	static char log_buf[LOG_MESSAGE_LEN]; \
 	int ret = snprintf(log_buf, sizeof(log_buf), format, ##args); \
-	if(ret < 0 || (uintmax_t)ret >= LOG_MESSAGE_LEN){ \
-		LOG(WARNING, "next message was too long so not subbing in args\n"); \
+	if (ret < 0 || (uintmax_t)ret >= LOG_MESSAGE_LEN) { \
+		LOG(ERROR, "next message was too long so not subbing in args\n"); \
 		LOG(level, "%s", format); \
 	}else{ \
 		LOG(level, "%s", log_buf); \
 	} \
-}while(0)
+} while (0)
 
-// The struct that the crio-side code uses for making logging calls.
-// Packed so it's the same on the atom and the crio.
-typedef struct {
-  // pid_t here at the front like in log_message
-  pid_t identifier; // must ALWAYS be -1 to identify that this is a crio log message
-  log_level level;
-  // still has to fit in LOG_MESSAGE_LEN on the atom side
-	char message[LOG_MESSAGE_LEN - 50];
-  double time;
-  uint8_t sequence;
-} __attribute__((packed)) log_crio_message;
+// Allows "bottling up" multiple log fragments which can then all be logged in
+// one message with LOG_UNCORK.
+// Calls from a given thread/task will be grouped together.
+#define LOG_CORK(format, args...) do { \
+  log_cork(__LINE__, LOG_CURRENT_FUNCTION, format, ##args); \
+} while (0)
+// Actually logs all of the saved up log fragments (including format and args on
+// the end).
+#define LOG_UNCORK(level, format, args...) do { \
+  log_uncork(__LINE__, LOG_CURRENT_FUNCTION, level, LOG_SOURCENAME, \
+             format, ##args); \
+} while (0)
 
-#ifdef __cplusplus
-// Just sticks the message into the shared memory queue.
-int log_crio_message_send(log_crio_message &to_send);
-// Returns left > right. LOG_UNKNOWN is most important.
-static inline bool log_gt_important(log_level left, log_level right) {
-  log_level l = left, r = right;
-  if (l == ERROR) l = 3;
-  if (r == ERROR) r = 3;
-  return left > right;
-}
-#endif
-
-// Returns a string representing level or "unknown".
-static inline const char *log_str(log_level level) {
-  // c doesn't really have const variables so they don't work in case statements
-	if (level == DEBUG) return "DEBUG";
-	if (level == INFO) return "INFO";
-	if (level == WARNING) return "WARNING";
-	if (level == ERROR) return "ERROR";
-	if (level == FATAL) return "FATAL";
-	return "unknown";
-}
-// Returns the log level represented by str or LOG_UNKNOWN.
-static inline log_level str_log(const char *str) {
-  if (!strcmp(str, "DEBUG")) return DEBUG;
-  if (!strcmp(str, "INFO")) return INFO;
-  if (!strcmp(str, "WARNING")) return WARNING;
-  if (!strcmp(str, "ERROR")) return ERROR;
-  if (!strcmp(str, "FATAL")) return FATAL;
-  return LOG_UNKNOWN;
-}
+// TODO(brians) add CHECK macros like glog
+// (<http://google-glog.googlecode.com/svn/trunk/doc/glog.html>)
+// and replace assert with one
 
 #ifdef __cplusplus
 }
 #endif
 
-#ifdef __unix
-#include "aos/atom_code/logging/atom_logging.h"  // IWYU pragma: export
-#else
-#include "aos/crio/logging/crio_logging.h"  // IWYU pragma: export
 #endif
-
-#endif
-
diff --git a/aos/common/logging/logging_impl.cc b/aos/common/logging/logging_impl.cc
new file mode 100644
index 0000000..66e64d8
--- /dev/null
+++ b/aos/common/logging/logging_impl.cc
@@ -0,0 +1,230 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include <assert.h>
+
+#include "aos/common/die.h"
+#include "aos/common/time.h"
+#include "aos/common/inttypes.h"
+#include "aos/common/once.h"
+
+namespace aos {
+namespace logging {
+namespace {
+
+using internal::Context;
+
+LogImplementation *global_top_implementation(NULL);
+// Just going to use a mutex instead of getting fancy because speed doesn't
+// really matter when accessing global_top_implementation.
+Mutex global_top_implementation_mutex;
+LogImplementation *get_global_top_implementation() {
+  MutexLocker locker(&global_top_implementation_mutex);
+  return global_top_implementation;
+}
+
+// The root LogImplementation. It only logs to stderr/stdout.
+// Some of the things specified in the LogImplementation documentation doesn't
+// apply here (mostly the parts about being able to use LOG) because this is the
+// root one.
+class RootLogImplementation : public LogImplementation {
+  virtual void set_next(LogImplementation *) {
+    LOG(FATAL, "can't have a next logger from here\n");
+  }
+
+  virtual void DoLog(log_level level, const char *format, va_list ap) {
+    LogMessage message;
+    internal::FillInMessage(level, format, ap, &message);
+    internal::PrintMessage(stderr, message);
+    fputs("root logger got used, see stderr for message\n", stdout);
+  }
+};
+
+void SetGlobalImplementation(LogImplementation *implementation) {
+  Context *context = Context::Get();
+
+  context->implementation = implementation;
+  {
+    MutexLocker locker(&global_top_implementation_mutex);
+    global_top_implementation = implementation;
+  }
+}
+
+// Prints format (with ap) into output and correctly deals with the result
+// being too long etc.
+void ExecuteFormat(char *output, size_t output_size,
+                   const char *format, va_list ap) {
+  static const char *continued = "...\n";
+  const size_t size = output_size - strlen(continued);
+  const int ret = vsnprintf(output, size, format, ap);
+  if (ret < 0) {
+    LOG(FATAL, "vsnprintf(%p, %zd, %s, %p) failed with %d (%s)\n",
+        output, size, format, ap, errno, strerror(errno));
+  } else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) {
+    // Overwrite the '\0' at the end of the existing data and
+    // copy in the one on the end of continued.
+    memcpy(&output[size - 1], continued, strlen(continued) + 1);
+  }
+}
+
+void *DoInit() {
+  SetGlobalImplementation(new RootLogImplementation());
+  return NULL;
+}
+
+}  // namespace
+namespace internal {
+
+Context::Context()
+    : implementation(get_global_top_implementation()),
+      sequence(0) {
+  cork_data.Reset();
+}
+
+void FillInMessage(log_level level, const char *format, va_list ap,
+                   LogMessage *message) {
+  Context *context = Context::Get();
+
+  ExecuteFormat(message->message, sizeof(message->message), format, ap);
+
+  message->level = level;
+  message->source = context->source;
+  memcpy(message->name, context->name, context->name_size);
+
+  time::Time now = time::Time::Now();
+  message->seconds = now.sec();
+  message->nseconds = now.nsec();
+
+  message->sequence = context->sequence++;
+}
+
+void PrintMessage(FILE *output, const LogMessage &message) {
+  fprintf(output, "%s(%"PRId32")(%05"PRIu16"): %s at"
+          " %010"PRId32".%09"PRId32"s: %s",
+          message.name, message.source, message.sequence,
+          log_str(message.level), message.seconds, message.nseconds,
+          message.message);
+}
+
+}  // namespace internal
+
+void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
+                   int levels) {
+  Context *context = Context::Get();
+
+  LogImplementation *top_implementation = context->implementation;
+  LogImplementation *new_implementation = top_implementation;
+  LogImplementation *implementation = NULL;
+  assert(levels >= 1);
+  for (int i = 0; i < levels; ++i) {
+    implementation = new_implementation;
+    if (new_implementation == NULL) {
+      Die("no logging implementation to use\n");
+    }
+    new_implementation = new_implementation->next();
+  }
+  context->implementation = new_implementation;
+  implementation->DoLog(level, format, ap);
+  context->implementation = top_implementation;
+
+  if (level == FATAL) {
+    VDie(format, ap);
+  }
+}
+
+void VLog(log_level level, const char *format, va_list ap) {
+  LogImplementation::DoVLog(level, format, ap, 1);
+}
+
+void VCork(int line, const char *function, const char *format, va_list ap) {
+  Context *context = Context::Get();
+
+  const size_t message_length = strlen(context->cork_data.message);
+  if (line > context->cork_data.line_max) context->cork_data.line_max = line;
+  if (line < context->cork_data.line_min) context->cork_data.line_min = line;
+
+  if (context->cork_data.function == NULL) {
+    context->cork_data.function = function;
+  } else {
+    if (strcmp(context->cork_data.function, function) != 0) {
+      LOG(FATAL, "started corking data in function %s but then moved to %s\n",
+          context->cork_data.function, function);
+    }
+  }
+
+  ExecuteFormat(context->cork_data.message + message_length,
+                sizeof(context->cork_data.message) - message_length,
+                format, ap);
+}
+
+void VUnCork(int line, const char *function, log_level level, const char *file,
+             const char *format, va_list ap) {
+  Context *context = Context::Get();
+
+  VCork(line, function, format, ap);
+
+  log_do(level, "%s: %d-%d: %s: %s", file,
+         context->cork_data.line_min, context->cork_data.line_max, function,
+         context->cork_data.message);
+
+  context->cork_data.Reset();
+}
+
+void LogNext(log_level level, const char *format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  LogImplementation::DoVLog(level, format, ap, 2);
+  va_end(ap);
+}
+
+void AddImplementation(LogImplementation *implementation) {
+  Context *context = Context::Get();
+
+  if (implementation->next() != NULL) {
+    LOG(FATAL, "%p already has a next implementation, but it's not"
+        " being used yet\n", implementation);
+  }
+
+  LogImplementation *old = context->implementation;
+  if (old != NULL) {
+    implementation->set_next(old);
+  }
+  SetGlobalImplementation(implementation);
+}
+
+void Init() {
+  static Once<void> once(DoInit);
+  once.Get();
+}
+
+void Load() {
+  Context::Get();
+}
+
+void Cleanup() {
+  Context::Delete();
+}
+
+}  // namespace logging
+}  // namespace aos
+
+void log_do(log_level level, const char *format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  aos::logging::VLog(level, format, ap);
+  va_end(ap);
+}
+
+void log_cork(int line, const char *function, const char *format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  aos::logging::VCork(line, function, format, ap);
+  va_end(ap);
+}
+
+void log_uncork(int line, const char *function, log_level level,
+                const char *file, const char *format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  aos::logging::VUnCork(line, function, level, file, format, ap);
+  va_end(ap);
+}
diff --git a/aos/common/logging/logging_impl.h b/aos/common/logging/logging_impl.h
new file mode 100644
index 0000000..0b1e256
--- /dev/null
+++ b/aos/common/logging/logging_impl.h
@@ -0,0 +1,205 @@
+#ifndef AOS_COMMON_LOGGING_LOGGING_IMPL_H_
+#define AOS_COMMON_LOGGING_LOGGING_IMPL_H_
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <string>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/type_traits.h"
+#include "aos/common/mutex.h"
+
+// This file has all of the logging implementation. It can't be #included by C
+// code like logging.h can.
+
+namespace aos {
+namespace logging {
+
+// Unless explicitly stated otherwise, format must always be a string constant,
+// args are printf-style arguments for format, and ap is a va_list of args.
+// The validitiy of format and args together will be checked at compile time
+// using a gcc function attribute.
+
+// The struct that the code uses for making logging calls.
+// Packed so that it ends up the same under both linux and vxworks.
+struct __attribute__((packed)) LogMessage {
+#ifdef __VXWORKS__
+  static_assert(sizeof(pid_t) == sizeof(int),
+                "we use task IDs (aka ints) and pid_t interchangeably");
+#endif
+  // Actually the task ID (aka a pointer to the TCB) on the cRIO.
+  pid_t source;
+  static_assert(sizeof(source) == 4, "that's how they get printed");
+  // Per task/thread.
+  uint16_t sequence;
+  log_level level;
+  int32_t seconds, nseconds;
+  char name[100];
+  char message[LOG_MESSAGE_LEN];
+};
+static_assert(shm_ok<LogMessage>::value, "it's going in a queue");
+
+// Returns left > right. LOG_UNKNOWN is most important.
+static inline bool log_gt_important(log_level left, log_level right) {
+  if (left == ERROR) left = 3;
+  if (right == ERROR) right = 3;
+  return left > right;
+}
+
+// Returns a string representing level or "unknown".
+static inline const char *log_str(log_level level) {
+#define DECL_LEVEL(name, value) if (level == name) return #name;
+  DECL_LEVELS;
+#undef DECL_LEVEL
+  return "unknown";
+}
+// Returns the log level represented by str or LOG_UNKNOWN.
+static inline log_level str_log(const char *str) {
+#define DECL_LEVEL(name, value) if (!strcmp(str, #name)) return name;
+  DECL_LEVELS;
+#undef DECL_LEVEL
+  return LOG_UNKNOWN;
+}
+
+// Takes a message and logs it. It will set everything up and then call DoLog
+// for the current LogImplementation.
+void VLog(log_level level, const char *format, va_list ap);
+// Adds to the saved up message.
+void VCork(int line, const char *format, va_list ap);
+// Actually logs the saved up message.
+void VUnCork(int line, log_level level, const char *file,
+             const char *format, va_list ap);
+
+// Will call VLog with the given arguments for the next logger in the chain.
+void LogNext(log_level level, const char *format, ...)
+  __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 2, 3)));
+
+// Represents a system that can actually take log messages and do something
+// useful with them.
+// All of the code (transitively too!) in the DoLog here can make
+// normal LOG and LOG_DYNAMIC calls but can NOT call LOG_CORK/LOG_UNCORK. These
+// calls will not result in DoLog recursing. However, implementations must be
+// safe to call from multiple threads/tasks at the same time. Also, any other
+// overriden methods may end up logging through a given implementation's DoLog.
+class LogImplementation {
+ public:
+  LogImplementation() : next_(NULL) {}
+
+  // The one that this one's implementation logs to.
+  // NULL means that there is no next one.
+  LogImplementation *next() { return next_; }
+  // Virtual in case a subclass wants to perform checks. There will be a valid
+  // logger other than this one available while this is called.
+  virtual void set_next(LogImplementation *next) { next_ = next; }
+
+ private:
+  // Actually logs the given message. Implementations should somehow create a
+  // LogMessage and then call internal::FillInMessage.
+  virtual void DoLog(log_level level, const char *format, va_list ap) = 0;
+
+  // Function of this class so that it can access DoLog.
+  // Levels is how many LogImplementations to not use off the stack.
+  static void DoVLog(log_level, const char *format, va_list ap, int levels);
+  // Friends so that they can access DoVLog.
+  friend void VLog(log_level, const char *, va_list);
+  friend void LogNext(log_level, const char *, ...);
+
+  LogImplementation *next_;
+};
+
+// Adds another implementation to the stack of implementations in this
+// task/thread.
+// Any tasks/threads created after this call will also use this implementation.
+// The cutoff is when the state in a given task/thread is created (either lazily
+// when needed or by calling Load()).
+// The logging system takes ownership of implementation. It will delete it if
+// necessary, so it must be created with new.
+void AddImplementation(LogImplementation *implementation);
+
+// Must be called at least once per process/load before anything else is
+// called. This function is safe to call multiple times from multiple
+// tasks/threads.
+void Init();
+
+// Forces all of the state that is usually lazily created when first needed to
+// be created when called. Cleanup() will delete it.
+void Load();
+// Resets all information in this task/thread to its initial state.
+// NOTE: This is not the opposite of Init(). The state that this deletes is
+// lazily created when needed. It is actually the opposite of Load().
+void Cleanup();
+
+// This is where all of the code that is only used by actual LogImplementations
+// goes.
+namespace internal {
+
+// An separate instance of this class is accessible from each task/thread.
+struct Context {
+  Context();
+
+  // Gets the Context object for this task/thread. Will create one the first
+  // time it is called.
+  //
+  // The implementation for each platform will lazily instantiate a new instance
+  // and then initialize name the first time.
+  // IMPORTANT: The implementation of this can not use logging.
+  static Context *Get();
+  // Deletes the Context object for this task/thread so that the next Get() is
+  // called it will create a new one.
+  // It is valid to call this when Get() has never been called.
+  static void Delete();
+
+  // Which one to log to right now.
+  // Will be NULL if there is no logging implementation to use right now.
+  LogImplementation *implementation;
+
+  // A string representing this task/(process and thread).
+  const char *name;
+  // The number of bytes in name (including the terminating '\0').
+  // Must be <= sizeof(LogMessage::name).
+  size_t name_size;
+
+  // What to assign LogMessage::source to in this task/thread.
+  pid_t source;
+
+  // The sequence value to send out with the next message.
+  uint16_t sequence;
+
+  // Contains all of the information related to implementing LOG_CORK and
+  // LOG_UNCORK.
+  struct {
+    char message[LOG_MESSAGE_LEN];
+    int line_min, line_max;
+    // Sets the data up to record a new series of corked logs.
+    void Reset() {
+      message[0] = '\0';  // make strlen of it 0
+      line_min = INT_MAX;
+      line_max = -1;
+      function = NULL;
+    }
+    // The function that the calls are in.
+    // REMEMBER: While the compiler/linker will probably optimize all of the
+    // identical strings to point to the same data, it might not, so using == to
+    // compare this with another value is a bad idea.
+    const char *function;
+  } cork_data;
+};
+
+// Fills in *message according to the given inputs. Used for implementing
+// LogImplementation::DoLog.
+void FillInMessage(log_level level, const char *format, va_list ap,
+                   LogMessage *message);
+
+// Prints message to output.
+void PrintMessage(FILE *output, const LogMessage &message);
+
+}  // namespace internal
+}  // namespace logging
+}  // namespace aos
+
+#endif  // AOS_COMMON_LOGGING_LOGGING_IMPL_H_
diff --git a/aos/common/logging/logging_impl_test.cc b/aos/common/logging/logging_impl_test.cc
new file mode 100644
index 0000000..bc411ce
--- /dev/null
+++ b/aos/common/logging/logging_impl_test.cc
@@ -0,0 +1,176 @@
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/time.h"
+#include "aos/common/die.h"
+#include "aos/common/inttypes.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::AssertionFailure;
+
+namespace aos {
+namespace logging {
+namespace testing {
+
+class TestLogImplementation : public LogImplementation {
+  virtual void DoLog(log_level level, const char *format, va_list ap) {
+    internal::FillInMessage(level, format, ap, &message_);
+
+    used_ = true;
+  }
+
+  LogMessage message_;
+
+ public:
+  const LogMessage &message() { return message_; }
+  bool used() { return used_; }
+  void reset_used() { used_ = false; }
+
+  TestLogImplementation() : used_(false) {}
+
+  bool used_;
+};
+class LoggingTest : public ::testing::Test {
+ protected:
+  AssertionResult WasAnythingLogged() {
+    if (log_implementation->used()) {
+      return AssertionSuccess() << "read message '" <<
+          log_implementation->message().message << "'";
+    }
+    return AssertionFailure();
+  }
+  AssertionResult WasLogged(log_level level, const std::string message) {
+    if (!log_implementation->used()) {
+      return AssertionFailure() << "nothing was logged";
+    }
+    if (log_implementation->message().level != level) {
+      return AssertionFailure() << "a message with level " <<
+          log_str(log_implementation->message().level) <<
+          " was logged instead of " << log_str(level);
+    }
+    internal::Context *context = internal::Context::Get();
+    if (log_implementation->message().source != context->source) {
+      Die("got a message from %"PRIu32", but we're %"PRIu32"\n",
+          static_cast<uint32_t>(log_implementation->message().source),
+          static_cast<uint32_t>(context->source));
+    }
+    if (strcmp(log_implementation->message().name, context->name) != 0) {
+      Die("got a message from %s, but we're %s\n",
+          log_implementation->message().name, context->name);
+    }
+    if (strstr(log_implementation->message().message, message.c_str())
+        == NULL) {
+      return AssertionFailure() << "got a message of '" <<
+          log_implementation->message().message <<
+          "' but expected it to contain '" << message << "'";
+    }
+
+    return AssertionSuccess() << log_implementation->message().message;
+  }
+
+ private:
+  virtual void SetUp() {
+    static bool first = true;
+    if (first) {
+      first = false;
+
+      Init();
+      // We'll end up with several of them stacked up, but it really doesn't
+      // matter.
+      AddImplementation(log_implementation = new TestLogImplementation());
+    }
+
+    log_implementation->reset_used();
+  }
+  virtual void TearDown() {
+    Cleanup();
+  }
+
+  static TestLogImplementation *log_implementation;
+};
+TestLogImplementation *LoggingTest::log_implementation(NULL);
+typedef LoggingTest LoggingDeathTest;
+
+// Tests both basic logging functionality and that the test setup works
+// correctly.
+TEST_F(LoggingTest, Basic) {
+  EXPECT_FALSE(WasAnythingLogged());
+  LOG(INFO, "test log 1\n");
+  EXPECT_TRUE(WasLogged(INFO, "test log 1\n"));
+  LOG(INFO, "test log 1.5\n");
+  // there's a subtle typo on purpose...
+  EXPECT_FALSE(WasLogged(INFO, "test log 15\n"));
+  LOG(ERROR, "test log 2=%d\n", 55);
+  EXPECT_TRUE(WasLogged(ERROR, "test log 2=55\n"));
+  LOG(ERROR, "test log 3\n");
+  EXPECT_FALSE(WasLogged(WARNING, "test log 3\n"));
+  LOG(WARNING, "test log 4\n");
+  EXPECT_TRUE(WasAnythingLogged());
+}
+TEST_F(LoggingTest, Cork) {
+  static const int begin_line = __LINE__;
+  LOG_CORK("first part ");
+  LOG_CORK("second part (=%d) ", 19);
+  EXPECT_FALSE(WasAnythingLogged());
+  LOG_CORK("third part ");
+  static const int end_line = __LINE__;
+  LOG_UNCORK(WARNING, "last part %d\n", 5);
+  std::stringstream expected;
+  expected << "logging_impl_test.cc: ";
+  expected << (begin_line + 1);
+  expected << "-";
+  expected << (end_line + 1);
+  expected << ": ";
+  expected << __PRETTY_FUNCTION__;
+  expected << ": first part second part (=19) third part last part 5\n";
+  EXPECT_TRUE(WasLogged(WARNING, expected.str()));
+}
+
+#ifndef __VXWORKS__
+TEST_F(LoggingDeathTest, Fatal) {
+  ASSERT_EXIT(LOG(FATAL, "this should crash it\n"),
+              ::testing::KilledBySignal(SIGABRT),
+              "this should crash it");
+}
+#endif
+
+TEST_F(LoggingTest, PrintfDirectives) {
+  LOG(INFO, "test log %%1 %%d\n");
+  EXPECT_TRUE(WasLogged(INFO, "test log %1 %d\n"));
+  LOG_DYNAMIC(WARNING, "test log %%2 %%f\n");
+  EXPECT_TRUE(WasLogged(WARNING, "test log %2 %f\n"));
+  LOG_CORK("log 3 part %%1 %%d ");
+  LOG_UNCORK(DEBUG, "log 3 part %%2 %%f\n");
+  EXPECT_TRUE(WasLogged(DEBUG, "log 3 part %1 %d log 3 part %2 %f\n"));
+}
+
+TEST_F(LoggingTest, Timing) {
+  // For writing only.
+  //static const long kTimingCycles = 5000000;
+  static const long kTimingCycles = 5000;
+
+  time::Time start = time::Time::Now();
+  for (long i = 0; i < kTimingCycles; ++i) {
+    LOG(INFO, "a\n");
+  }
+  time::Time end = time::Time::Now();
+  time::Time diff = end - start;
+  printf("short message took %lld nsec for %ld\n",
+         diff.ToNSec(), kTimingCycles);
+
+  start = time::Time::Now();
+  for (long i = 0; i < kTimingCycles; ++i) {
+    LOG(INFO, "something longer than just \"a\" to log to test timing\n");
+  }
+  end = time::Time::Now();
+  diff = end - start;
+  printf("long message took %lld nsec for %ld\n",
+         diff.ToNSec(), kTimingCycles);
+}
+
+}  // namespace testing
+}  // namespace logging
+}  // namespace aos
diff --git a/aos/common/messages/QueueHolder.h b/aos/common/messages/QueueHolder.h
index 6cf2835..2b7c0d7 100644
--- a/aos/common/messages/QueueHolder.h
+++ b/aos/common/messages/QueueHolder.h
@@ -2,6 +2,7 @@
 #define __AOS_MESSAGES_QUEUE_HOLDER_H_
 
 #include <stddef.h>
+#include <string.h>
 
 #include <algorithm>
 
diff --git a/aos/common/mutex.h b/aos/common/mutex.h
index c1d6f95..035889b 100644
--- a/aos/common/mutex.h
+++ b/aos/common/mutex.h
@@ -5,9 +5,9 @@
 #include <semLib.h>
 #endif
 
-#include "aos/aos_core.h"
 #include "aos/common/macros.h"
 #include "aos/common/type_traits.h"
+#include "aos/atom_code/ipc_lib/aos_sync.h"
 
 namespace aos {
 
diff --git a/aos/common/queue_testutils.cc b/aos/common/queue_testutils.cc
index 6cce012..d35dc45 100644
--- a/aos/common/queue_testutils.cc
+++ b/aos/common/queue_testutils.cc
@@ -1,4 +1,7 @@
 #include "aos/common/queue_testutils.h"
+
+#include <string.h>
+
 #include "aos/common/queue.h"
 
 namespace aos {
diff --git a/aos/common/time.cc b/aos/common/time.cc
index 1d97b80..00c936d 100644
--- a/aos/common/time.cc
+++ b/aos/common/time.cc
@@ -3,6 +3,8 @@
 #ifdef __VXWORKS__
 #include <taskLib.h>
 #endif
+#include <errno.h>
+#include <string.h>
 
 #include "aos/common/logging/logging.h"
 #include "aos/common/inttypes.h"
diff --git a/aos/crio/controls/ControlsManager.cpp b/aos/crio/controls/ControlsManager.cpp
index a37ec6f..c1f11cd 100644
--- a/aos/crio/controls/ControlsManager.cpp
+++ b/aos/crio/controls/ControlsManager.cpp
@@ -3,7 +3,7 @@
 
 #include "WPILib/Compressor.h"
 
-#include "aos/aos_core.h"
+#include "aos/crio/logging/crio_logging.h"
 #include "aos/crio/controls/ControlsManager.h"
 #include "aos/common/Configuration.h"
 #include "aos/crio/aos_ctdt.h"
@@ -22,7 +22,7 @@
   printf("aos::ControlsManager::RobotMain\n");
   (new Compressor(14, 1))->Start();
 
-  logging::Start();
+  logging::crio::Register();
   LOG(INFO, "logging started\n");
 
   GetWatchdog().SetEnabled(false);
diff --git a/aos/crio/logging/crio_logging.cc b/aos/crio/logging/crio_logging.cc
new file mode 100644
index 0000000..af55e88
--- /dev/null
+++ b/aos/crio/logging/crio_logging.cc
@@ -0,0 +1,196 @@
+#include "aos/crio/logging/crio_logging.h"
+
+#include <string.h>
+#include <msgQLib.h>
+#include <stdint.h>
+#include <taskLib.h>
+
+#include "WPILib/Timer.h"
+#include "WPILib/Task.h"
+
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/network/SendSocket.h"
+#include "aos/common/Configuration.h"
+#include "aos/common/die.h"
+
+namespace aos {
+namespace logging {
+namespace {
+
+using internal::Context;
+
+MSG_Q_ID queue;
+static const int kQueueLength = 150;
+
+// This gets run in a low-priority task to take the logs from the queue and send
+// them to the atom over a TCP socket.
+void DoTask() {
+  SendSocket sock;
+  LogMessage msg;
+  while (true) {
+    const int ret = msgQReceive(queue, reinterpret_cast<char *>(&msg),
+                                sizeof(msg), WAIT_FOREVER);
+    if (ret == ERROR) {
+      LOG(WARNING, "receiving a message failed with %d (%s)",
+          errno, strerror(errno));
+      continue;
+    }
+    if (ret != sizeof(msg)) {
+      LOG(WARNING, "received a message of size %d instead of %d\n",
+          ret, sizeof(msg));
+      continue;
+    }
+
+    if (sock.LastStatus() != 0) {
+      int numMsgs = msgQNumMsgs(queue);
+      if (numMsgs == ERROR) {
+        LOG(FATAL, "msgQNumMsgs(%p) failed with %d: %s\n", queue,
+            errno, strerror(errno));
+      }
+      // If the queue is more than a little bit full.
+      // Otherwise, without a connection on the other end, the queue just fills
+      // up and then all the senders start failing.
+      if (numMsgs > kQueueLength * 3 / 10) {
+        // DEBUG and INFO aren't worth recording here.
+        if (log_gt_important(msg.level, INFO)) {
+          LogNext(WARNING, "log_sender: dropping %s", msg.message);
+        }
+        continue;
+      } else {
+        if (sock.Connect(NetworkPort::kLogs,
+                         configuration::GetIPAddress(
+                             configuration::NetworkDevice::kAtom),
+                         SOCK_STREAM) != 0) {
+          LOG(WARNING, "connecting failed because of %d: %s\n",
+              errno, strerror(errno));
+          LogNext(WARNING, "log_sender: couldn't send %s", msg.message);
+          continue;
+        }
+      }
+    }
+    sock.Send(&msg, sizeof(msg));
+    if (sock.LastStatus() != 0) {
+      LOG(WARNING, "sending '%s' failed because of %d: %s\n",
+          msg.message, errno, strerror(errno));
+    }
+  }
+}
+
+// A simple linked list of Contexts.
+struct ContextHolder {
+  static ContextHolder *head;
+  // Which task this one belongs to.
+  const int task;
+  ContextHolder *next;
+  Context context;
+
+  ContextHolder() : task(taskIdSelf()) {
+    MutexLocker locker(&list_mutex);
+    next = head;
+    head = this;
+  }
+  ~ContextHolder() {
+    MutexLocker locker(&list_mutex);
+    head = next;
+  }
+
+  static ContextHolder *Get() {
+    int thisTask = taskIdSelf();
+    for (ContextHolder *c = head; c != NULL; c = c->next) {
+      if (c->task == thisTask) {
+        return c;
+      }
+    }
+    return NULL;
+  }
+
+ private:
+  static Mutex list_mutex;
+};
+ContextHolder *ContextHolder::head(NULL);
+Mutex ContextHolder::list_mutex;
+
+}  // namespace
+namespace internal {
+
+Context *Context::Get() {
+  ContextHolder *holder = ContextHolder::Get();
+
+  if (holder == NULL) {
+    holder = new ContextHolder();
+
+    errno = 0;  // in case taskName decides not to set it
+    // We're going to make a copy of it because vxworks might allocate the
+    // memory for it from some funky place or something.
+    const char *my_name = taskName(0);
+    if (my_name == NULL) {
+      Die("logging: taskName(0) failed with %d: %s\n",
+          errno, strerror(errno));
+    }
+    holder->context.name_size = strlen(my_name);
+    if (holder->context.name_size > sizeof(LogMessage::name)) {
+      Die("logging: somebody chose a task name ('%s') that's too long\n",
+          my_name);
+    }
+    char *name_chars = new char[holder->context.name_size];
+    memcpy(name_chars, my_name, holder->context.name_size);
+    name_chars[holder->context.name_size - 1] = '\0';
+    holder->context.name = name_chars;
+    holder->context.source = taskIdSelf();
+  }
+
+  return &holder->context;
+}
+
+void Context::Delete() {
+  delete ContextHolder::Get();
+}
+
+}  // namespace internal
+
+namespace crio {
+namespace {
+
+class CrioLogImplementation : public LogImplementation {
+  virtual void DoLog(log_level level, const char *format, va_list ap) {
+    LogMessage message;
+
+    internal::FillInMessage(level, format, ap, &message);
+
+    if (msgQSend(queue, reinterpret_cast<char *>(&message), sizeof(message),
+                 NO_WAIT, MSG_PRI_NORMAL) != 0) {
+      if (errno == S_objLib_OBJ_UNAVAILABLE) {
+        LOG(WARNING, "no space for sending %s", message.message);
+      } else {
+        LOG(FATAL, "msgQSend(%p, %p, %d, NO_WAIT, MSG_PRI_NORMAL) failed"
+            " with %d: %s\n", queue, &message, sizeof(message),
+            errno, strerror(errno));
+      }
+    }
+  }
+};
+
+}  // namespace
+
+void Register() {
+  queue = msgQCreate(kQueueLength,
+                     sizeof(LogMessage),
+                     MSG_Q_PRIORITY);
+  if (queue == NULL) {
+    Die("msgQCreate(%d, %d, MSG_Q_PRIORITY) failed with %d: %s\n",
+        kQueueLength, sizeof(LogMessage), errno, strerror(errno));
+  }
+
+  Task *task = new Task("LogSender",
+                        (FUNCPTR)(DoTask),
+                        150);  // low priority
+  task->Start();
+
+  Init();
+  
+  AddImplementation(new CrioLogImplementation());
+}
+
+}  // namespace crio
+}  // namespace logging
+}  // namespace aos
diff --git a/aos/crio/logging/crio_logging.cpp b/aos/crio/logging/crio_logging.cpp
deleted file mode 100644
index 8ebafc0..0000000
--- a/aos/crio/logging/crio_logging.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-#include <string.h>
-
-#include "WPILib/Timer.h"
-#include "WPILib/Task.h"
-
-#include "aos/aos_core.h"
-#include "aos/common/network/SendSocket.h"
-#include "aos/common/Configuration.h"
-#include "aos/common/die.h"
-
-#undef ERROR
-#define DECL_LEVEL(name, value) const log_level name = value;
-DECL_LEVELS
-#undef DECL_LEVEL
-
-//#define fprintf(...)
-
-namespace aos {
-namespace logging {
-namespace {
-
-MSG_Q_ID queue;
-uint8_t sequence = 0;
-
-// This gets run in a low-priority task to take the logs from the queue and send
-// them to the atom over a TCP socket.
-void DoTask() {
-  SendSocket sock;
-  log_crio_message msg;
-  while (true) {
-    const int ret = msgQReceive(queue, reinterpret_cast<char *>(&msg),
-                                sizeof(msg), WAIT_FOREVER);
-    if (ret == ERROR) {
-        fprintf(stderr, "logging: warning: receiving a message failed"
-                " with %d (%s)", errno, strerror(errno));
-        continue;
-    }
-    if (ret != sizeof(msg)) {
-      fprintf(stderr, "logging: warning: received a message of size %d "
-              "instead of %zd\n", ret, sizeof(msg));
-      continue;
-    }
-
-    if (sock.LastStatus() != 0) {
-      if (sock.Connect(NetworkPort::kLogs,
-                       configuration::GetIPAddress(
-                           configuration::NetworkDevice::kAtom),
-                       SOCK_STREAM) != 0) {
-        fprintf(stderr, "logging: warning: connecting failed"
-                " because of %d: %s\n", errno, strerror(errno));
-      }
-    }
-    sock.Send(&msg, sizeof(msg));
-    if (sock.LastStatus() != 0) {
-      fprintf(stderr, "logging: warning: sending '%s' failed"
-              " because of %d: %s\n", msg.message, errno, strerror(errno));
-    }
-  }
-}
-
-}  // namespace
-
-void Start() {
-  queue = msgQCreate(100,  // max messages
-                     sizeof(log_crio_message),
-                     MSG_Q_PRIORITY);
-  Task *task = new Task("LogSender",
-                        (FUNCPTR)(DoTask),
-                        150);  // priority
-  task->Start();
-}
-
-int Do(log_level level, const char *format, ...) {
-  log_crio_message msg;
-  msg.time = Timer::GetFPGATimestamp();
-  msg.level = level;
-  msg.sequence = __sync_fetch_and_add(&sequence, 1);
-
-  const char *continued = "...";
-  const size_t size = sizeof(msg.message) - strlen(continued);
-  va_list ap;
-  va_start(ap, format);
-  const int ret = vsnprintf(msg.message, size, format, ap);
-  va_end(ap);
-
-  if (ret < 0) {
-    fprintf(stderr, "logging: error: vsnprintf failed with %d (%s)\n",
-            errno, strerror(errno));
-    return -1;
-  } else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) {
-    // overwrite the NULL at the end of the existing one and
-    // copy in the one on the end of continued
-    memcpy(&msg.message[size - 1], continued, strlen(continued) + 1);
-  }
-  if (msgQSend(queue, reinterpret_cast<char *>(&msg), sizeof(msg),
-               NO_WAIT, MSG_PRI_NORMAL) == ERROR) {
-    fprintf(stderr, "logging: warning: sending message '%s'"
-            " failed with %d (%s)", msg.message, errno, strerror(errno));
-    return -1;
-  }
-
-  if (level == FATAL) {
-    aos::Die("%s", msg.message);
-  }
-
-  return 0;
-}
-
-}  // namespace logging
-}  // namespace aos
diff --git a/aos/crio/logging/crio_logging.h b/aos/crio/logging/crio_logging.h
index c3dbf2a..3fad750 100644
--- a/aos/crio/logging/crio_logging.h
+++ b/aos/crio/logging/crio_logging.h
@@ -1,35 +1,17 @@
 #ifndef AOS_CRIO_CRIO_LOGGING_LOGGING_H_
 #define AOS_CRIO_CRIO_LOGGING_LOGGING_H_
 
-#ifndef AOS_COMMON_LOGGING_LOGGING_H_
-#error This file may only be #included through common/logging/logging.h!!!
-#endif
-
-#undef extern
-#undef const
-#undef ERROR
-
-#include <msgQLib.h>
-#include <stdint.h>
-
-//#define LOG(level, fmt, args...) printf(STRINGIFY(level) ": " fmt, ##args)
-#define LOG(level, fmt, args...) \
-    ::aos::logging::Do(level, \
-                       LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": " fmt, \
-                       ##args)
-//#define LOG(...)
-
 namespace aos {
 namespace logging {
+namespace crio {
 
-// Initialize internal variables and start up the task that sends the logs to
-// the atom. Must be called before Do.
-void Start();
-// The function that the LOG macro actually calls. Queues up a message for the
-// task to send. Start must be called before this function is.
-int Do(log_level level, const char *format, ...)
-    __attribute__((format(printf, 2, 3)));
+// Calls AddImplementation to register the usual crio logging implementation
+// which sends the messages to the atom. This function will start the task that
+// does the actual sending. It relies on a process being running on the atom to
+// receive them.
+void Register();
 
+}  // namespace crio
 }  // namespace logging
 }  // namespace aos
 
diff --git a/aos/crio/shared_libs/interrupt_bridge-tmpl.h b/aos/crio/shared_libs/interrupt_bridge-tmpl.h
index cc00ab8..fbd3e98 100644
--- a/aos/crio/shared_libs/interrupt_bridge-tmpl.h
+++ b/aos/crio/shared_libs/interrupt_bridge-tmpl.h
@@ -5,6 +5,7 @@
 #include "WPILib/Task.h"
 
 #include "aos/common/logging/logging.h"
+#include "aos/crio/motor_server/MotorServer.h"
 #include "aos/common/time.h"
 
 extern "C" {
diff --git a/aos/crio/shared_libs/interrupt_bridge.h b/aos/crio/shared_libs/interrupt_bridge.h
index 85ecd0f..f8f68b4 100644
--- a/aos/crio/shared_libs/interrupt_bridge.h
+++ b/aos/crio/shared_libs/interrupt_bridge.h
@@ -8,7 +8,7 @@
 
 #include "aos/common/scoped_ptr.h"
 
-#include "aos/aos_core.h"
+#include "aos/common/macros.h"
 
 class Task;
 
diff --git a/aos/crio/shared_libs/interrupt_bridge_demo.cc b/aos/crio/shared_libs/interrupt_bridge_demo.cc
index d7e66be..0755c97 100644
--- a/aos/crio/shared_libs/interrupt_bridge_demo.cc
+++ b/aos/crio/shared_libs/interrupt_bridge_demo.cc
@@ -4,7 +4,6 @@
 
 #include "WPILib/Timer.h"
 
-#include "aos/aos_core.h"
 #include "aos/common/time.h"
 
 using aos::time::Time;
diff --git a/aos/crio/shared_libs/mutex.cpp b/aos/crio/shared_libs/mutex.cpp
index 35ac4e3..d921492 100644
--- a/aos/crio/shared_libs/mutex.cpp
+++ b/aos/crio/shared_libs/mutex.cpp
@@ -1,6 +1,9 @@
 #include "aos/common/mutex.h"
 
 #include <semLib.h>
+#include <string.h>
+
+#include "aos/common/logging/logging.h"
 
 namespace aos {
 
diff --git a/aos/crio/shared_libs/shared_libs.gyp b/aos/crio/shared_libs/shared_libs.gyp
new file mode 100644
index 0000000..90b3cf0
--- /dev/null
+++ b/aos/crio/shared_libs/shared_libs.gyp
@@ -0,0 +1,25 @@
+{
+  'targets': [
+    {
+# This one includes interrupt_bridge.h too.
+      'target_name': 'interrupt_notifier',
+      'type': 'static_library',
+      'sources': [
+        'interrupt_bridge.cc',
+        'interrupt_bridge_c.c',
+        'interrupt_bridge_demo.cc',
+      ],
+      'dependencies': [
+        '<(AOS)/common/common.gyp:time',
+        '<(AOS)/build/aos.gyp:logging',
+        '<(EXTERNALS):WPILib',
+        '<(AOS)/common/messages/messages.gyp:aos_queues',
+      ],
+      'export_dependent_settings': [
+        '<(AOS)/common/common.gyp:time',
+        '<(AOS)/build/aos.gyp:logging',
+        '<(EXTERNALS):WPILib',
+      ],
+    },
+  ],
+}
diff --git a/frc971/control_loops/control_loops.gyp b/frc971/control_loops/control_loops.gyp
index a9ddb12..8bdc7a0 100644
--- a/frc971/control_loops/control_loops.gyp
+++ b/frc971/control_loops/control_loops.gyp
@@ -13,12 +13,10 @@
         'header_path': 'frc971/control_loops',
       },
       'dependencies': [
-        '<(AOS)/build/aos.gyp:libaos',
         '<(AOS)/common/common.gyp:control_loop_queues',
         '<(AOS)/common/common.gyp:queues',
       ],
       'export_dependent_settings': [
-        '<(AOS)/build/aos.gyp:libaos',
         '<(AOS)/common/common.gyp:control_loop_queues',
         '<(AOS)/common/common.gyp:queues',
       ],
@@ -33,10 +31,10 @@
       'dependencies': [
         '<(AOS)/build/aos.gyp:logging',
         '<(AOS)/common/common.gyp:controls',
-        '<(AOS)/build/aos.gyp:libaos',
         'control_loops',
         '<(DEPTH)/frc971/queues/queues.gyp:queues',
         '<(EXTERNALS):eigen',
+        '<(AOS)/atom_code/atom_code.gyp:init',
       ],
     },
   ],
diff --git a/frc971/input/GyroReader.cc b/frc971/input/GyroReader.cc
index a6e1d00..9fbee44 100644
--- a/frc971/input/GyroReader.cc
+++ b/frc971/input/GyroReader.cc
@@ -3,7 +3,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 
-#include "aos/aos_core.h"
+#include "aos/common/logging/logging.h"
 
 #include "frc971/queues/GyroAngle.q.h"
 
diff --git a/frc971/input/input.gyp b/frc971/input/input.gyp
index 43f591a..c62365b 100644
--- a/frc971/input/input.gyp
+++ b/frc971/input/input.gyp
@@ -26,6 +26,7 @@
         'actions',
         '<(DEPTH)/frc971/control_loops/control_loops.gyp:control_loops',
         '<(DEPTH)/frc971/queues/queues.gyp:queues',
+        '<(AOS)/atom_code/atom_code.gyp:init',
       ],
     },
     {
@@ -40,6 +41,13 @@
         '<(DEPTH)/frc971/queues/queues.gyp:queues',
         '<(AOS)/common/network/network.gyp:socket',
       ],
+      'conditions': [
+        ['OS!="crio"', {
+          'dependencies': [
+            '<(AOS)/atom_code/atom_code.gyp:init',
+          ],
+        }],
+      ],
     },
     {
       'target_name': 'SensorWriter',
@@ -50,6 +58,7 @@
       'dependencies': [
         '<(AOS)/build/aos.gyp:libaos',
         '<(DEPTH)/frc971/control_loops/control_loops.gyp:control_loops',
+        '<(AOS)/crio/shared_libs/shared_libs.gyp:interrupt_notifier',
       ],
     },
     {
@@ -59,8 +68,9 @@
         'GyroReader.cc',
       ],
       'dependencies': [
-        '<(AOS)/build/aos.gyp:libaos',
         '<(DEPTH)/frc971/queues/queues.gyp:queues',
+        '<(AOS)/atom_code/atom_code.gyp:init',
+        '<(AOS)/build/aos.gyp:logging',
       ],
     },
     {
@@ -74,6 +84,8 @@
         '<(DEPTH)/frc971/control_loops/control_loops.gyp:control_loops',
         '<(DEPTH)/frc971/queues/queues.gyp:queues',
         'actions',
+# TODO(brians) this shouldn't need to be here
+        '<(AOS)/atom_code/atom_code.gyp:init',
       ],
     },
   ],
diff --git a/frc971/output/output.gyp b/frc971/output/output.gyp
index 6cb28d1..780eadf 100644
--- a/frc971/output/output.gyp
+++ b/frc971/output/output.gyp
@@ -10,6 +10,7 @@
         '<(AOS)/build/aos.gyp:libaos',
         '<(AOS)/atom_code/output/output.gyp:http_server',
         '../frc971.gyp:common',
+        '<(AOS)/atom_code/atom_code.gyp:init',
       ],
       'copies': [
         {
@@ -30,6 +31,7 @@
               '../frc971.gyp:common',
               '<(AOS)/atom_code/output/output.gyp:motor_output',
               '<(AOS)/atom_code/messages/messages.gyp:messages',
+              '<(AOS)/atom_code/atom_code.gyp:init',
             ],
           }, {
             'sources': ['CRIOMotorWriter.cc'],