got logging structs working (theoretically) and used it
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index 7c38c53..7259c32 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -182,6 +182,7 @@
         'timing',
         'time',
         'control_loop_queues',
+        '<(AOS)/common/logging/logging.gyp:queue_logging',
       ],
       'export_dependent_settings': [
         '<(AOS)/common/messages/messages.gyp:aos_queues',
@@ -189,6 +190,7 @@
         'timing',
         'time',
         'control_loop_queues',
+        '<(AOS)/common/logging/logging.gyp:queue_logging',
       ],
     },
     {
diff --git a/aos/common/control_loop/ControlLoop-tmpl.h b/aos/common/control_loop/ControlLoop-tmpl.h
index 69c3121..3173d2c 100644
--- a/aos/common/control_loop/ControlLoop-tmpl.h
+++ b/aos/common/control_loop/ControlLoop-tmpl.h
@@ -3,6 +3,7 @@
 #include "aos/common/logging/logging.h"
 #include "aos/common/control_loop/Timing.h"
 #include "aos/common/messages/RobotState.q.h"
+#include "aos/common/logging/queue_logging.h"
 
 namespace aos {
 namespace control_loops {
@@ -19,9 +20,6 @@
 
 template <class T, bool has_position, bool fail_no_position>
 void ControlLoop<T, has_position, fail_no_position>::Iterate() {
-  // Temporary storage for printing out inputs and outputs.
-  char state[1024];
-
   // Fetch the latest control loop goal and position.  If there is no new
   // goal, we will just reuse the old one.
   // If there is no goal, we haven't started up fully.  It isn't worth
@@ -35,8 +33,7 @@
     ZeroOutputs();
     return;
   }
-  goal->Print(state, sizeof(state));
-  LOG(DEBUG, "goal={%s}\n", state);
+  LOG_STRUCT(DEBUG, "goal", *goal);
 
   // Only pass in a position if we got one this cycle.
   const PositionType *position = NULL;
@@ -67,8 +64,7 @@
       }
     }
     if (position) {
-      position->Print(state, sizeof(state));
-      LOG(DEBUG, "position={%s}\n", state);
+      LOG_STRUCT(DEBUG, "position", *position);
     }
   }
 
@@ -100,8 +96,7 @@
         control_loop_->output.MakeMessage();
     RunIteration(goal, position, output.get(), status.get());
 
-    output->Print(state, sizeof(state));
-    LOG(DEBUG, "output={%s}\n", state);
+    LOG_STRUCT(DEBUG, "output", *output);
     output.Send();
   } else {
     // The outputs are disabled, so pass NULL in for the output.
@@ -109,8 +104,7 @@
     ZeroOutputs();
   }
 
-  status->Print(state, sizeof(state));
-  LOG(DEBUG, "status={%s}\n", state);
+  LOG_STRUCT(DEBUG, "status", *status);
   status.Send();
 }
 
diff --git a/aos/common/logging/logging.gyp b/aos/common/logging/logging.gyp
index ad20166..e230e47 100644
--- a/aos/common/logging/logging.gyp
+++ b/aos/common/logging/logging.gyp
@@ -18,8 +18,13 @@
         'queue_logging.cc',
       ],
       'dependencies': [
+        '<(AOS)/build/aos.gyp:logging',
+        '<(AOS)/common/common.gyp:die',
         '<(AOS)/common/common.gyp:queue_types',
       ],
+      'export_dependent_settings': [
+        '<(AOS)/build/aos.gyp:logging',
+      ],
     },
   ],
 }
diff --git a/aos/common/logging/logging.h b/aos/common/logging/logging.h
index c71ee46..673cc9a 100644
--- a/aos/common/logging/logging.h
+++ b/aos/common/logging/logging.h
@@ -71,41 +71,45 @@
 #define LOG_SOURCENAME __FILE__
 
 // 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)
+#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 { \
-	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(ERROR, "next message was too long so not subbing in args\n"); \
-		LOG(level, "%s", format); \
-	}else{ \
-		LOG(level, "%s", log_buf); \
-	} \
-} while (0)
+#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(ERROR, "next message was too long so not subbing in args\n"); \
+      LOG(level, "%s", format);                                         \
+    } else {                                                            \
+      LOG(level, "%s", log_buf);                                        \
+    }                                                                   \
+  } while (0)
 
 // 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)
+#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)
+#define LOG_UNCORK(level, format, args...)                                    \
+  do {                                                                        \
+    log_uncork(__LINE__, LOG_CURRENT_FUNCTION, level, LOG_SOURCENAME, format, \
+               ##args);                                                       \
+  } while (0)
 
 #ifdef __cplusplus
 }
@@ -154,8 +158,8 @@
 // controlled by NDEBUG, so the check will be executed regardless of
 // compilation mode.  Therefore, it is safe to do things like:
 //    CHECK(fp->Write(x) == 4)
-#define CHECK(condition)  \
-  if (__builtin_expect(!(condition), 0)) { \
+#define CHECK(condition)                          \
+  if (__builtin_expect(!(condition), 0)) {        \
     LOG(FATAL, "CHECK(%s) failed\n", #condition); \
   }
 
@@ -163,16 +167,16 @@
 // The (int, int) specialization works around the issue that the compiler
 // will not instantiate the template version of the function on values of
 // unnamed enum type.
-#define DEFINE_CHECK_OP_IMPL(name, op) \
-  template <typename T1, typename T2> \
-  inline void LogImpl##name(const T1& v1, const T2& v2,    \
-                            const char* exprtext) { \
-    if (!__builtin_expect(v1 op v2, 1)) { \
-      LOG(FATAL, "CHECK(%s) failed\n", exprtext); \
-    } \
-  } \
-  inline void LogImpl##name(int v1, int v2, const char* exprtext) { \
-    ::aos::LogImpl##name<int, int>(v1, v2, exprtext); \
+#define DEFINE_CHECK_OP_IMPL(name, op)                              \
+  template <typename T1, typename T2>                               \
+  inline void LogImpl##name(const T1 &v1, const T2 &v2,             \
+                            const char *exprtext) {                 \
+    if (!__builtin_expect(v1 op v2, 1)) {                           \
+      LOG(FATAL, "CHECK(%s) failed\n", exprtext);                   \
+    }                                                               \
+  }                                                                 \
+  inline void LogImpl##name(int v1, int v2, const char *exprtext) { \
+    ::aos::LogImpl##name<int, int>(v1, v2, exprtext);               \
   }
 
 // We use the full name Check_EQ, Check_NE, etc. in case the file including
@@ -186,7 +190,7 @@
 DEFINE_CHECK_OP_IMPL(Check_GE, >=)
 DEFINE_CHECK_OP_IMPL(Check_GT, > )
 
-#define CHECK_OP(name, op, val1, val2) \
+#define CHECK_OP(name, op, val1, val2)  \
   ::aos::LogImplCheck##name(val1, val2, \
                             STRINGIFY(val1) STRINGIFY(op) STRINGIFY(val2))
 
@@ -208,8 +212,7 @@
 
 // Check that the input is non NULL.  This very useful in constructor
 // initializer lists.
-#define CHECK_NOTNULL(val) \
-  ::aos::CheckNotNull(STRINGIFY(val), val)
+#define CHECK_NOTNULL(val) ::aos::CheckNotNull(STRINGIFY(val), val)
 
 }  // namespace aos
 
diff --git a/aos/common/logging/logging_impl.h b/aos/common/logging/logging_impl.h
index ca19dbc..bdbe585 100644
--- a/aos/common/logging/logging_impl.h
+++ b/aos/common/logging/logging_impl.h
@@ -9,11 +9,18 @@
 #include <stdio.h>
 
 #include <string>
+#include <functional>
 
 #include "aos/common/logging/logging.h"
 #include "aos/common/type_traits.h"
 #include "aos/common/mutex.h"
 
+namespace aos {
+
+class MessageType;
+
+}  // namespace aos
+
 // This file has all of the logging implementation. It can't be #included by C
 // code like logging.h can.
 // It is useful for the rest of the logging implementation and other C++ code
@@ -85,6 +92,10 @@
 void LogNext(log_level level, const char *format, ...)
   __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 2, 3)));
 
+// Will take a structure and log it.
+template <class T>
+void DoLogStruct(log_level, const ::std::string &, const T &);
+
 // 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
@@ -108,12 +119,31 @@
   // 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.
+  // Logs the contents of an auto-generated structure. The implementation here
+  // just converts it to a string with PrintMessage and then calls DoLog with
+  // that, however some implementations can be a lot more efficient than that.
+  // size and type are the result of calling Size() and Type() on the type of
+  // the message.
+  // serialize will call Serialize on the message.
+  virtual void LogStruct(log_level level, const ::std::string &message,
+                         size_t size, const MessageType *type,
+                         const ::std::function<size_t(char *)> &serialize);
+
+  // These functions call similar methods on the "current" LogImplementation or
+  // Die if they can't find one.
+  // 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.
+  // This one is implemented in queue_logging.cc.
+  static void DoLogStruct(log_level level, const ::std::string &message,
+                          size_t size, const MessageType *type,
+                          const ::std::function<size_t(char *)> &serialize,
+                          int levels);
+
+  // Friends so that they can access the static Do* functions.
   friend void VLog(log_level, const char *, va_list);
   friend void LogNext(log_level, const char *, ...);
+  template <class T>
+  friend void DoLogStruct(log_level, const ::std::string &, const T &);
 
   LogImplementation *next_;
 };
@@ -221,6 +251,11 @@
 void ExecuteFormat(char *output, size_t output_size, const char *format,
                    va_list ap);
 
+// Runs the given function with the current LogImplementation (handles switching
+// it out while running function etc).
+void RunWithCurrentImplementation(
+    int levels, ::std::function<void(LogImplementation *)> function);
+
 }  // namespace internal
 }  // namespace logging
 }  // namespace aos
diff --git a/aos/common/logging/logging_interface.cc b/aos/common/logging/logging_interface.cc
index 01fb708..9f1168f 100644
--- a/aos/common/logging/logging_interface.cc
+++ b/aos/common/logging/logging_interface.cc
@@ -36,15 +36,11 @@
   }
 }
 
-}  // namespace internal
-
-using internal::Context;
-
-void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
-                   int levels) {
+void RunWithCurrentImplementation(
+    int levels, ::std::function<void(LogImplementation *)> function) {
   Context *context = Context::Get();
 
-  LogImplementation *top_implementation = context->implementation;
+  LogImplementation *const top_implementation = context->implementation;
   LogImplementation *new_implementation = top_implementation;
   LogImplementation *implementation = NULL;
   assert(levels >= 1);
@@ -56,12 +52,34 @@
     new_implementation = new_implementation->next();
   }
   context->implementation = new_implementation;
-  implementation->DoLog(level, format, ap);
+  function(implementation);
   context->implementation = top_implementation;
+}
 
-  if (level == FATAL) {
-    VDie(format, ap);
-  }
+}  // namespace internal
+
+using internal::Context;
+
+void LogImplementation::LogStruct(
+    log_level level, const ::std::string &message, size_t size,
+    const MessageType *type, const ::std::function<size_t(char *)> &serialize) {
+  (void)level;
+  (void)message;
+  (void)size;
+  (void)type;
+  (void)serialize;
+}
+
+void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
+                               int levels) {
+  internal::RunWithCurrentImplementation(
+      levels, [&](LogImplementation * implementation) {
+    implementation->DoLog(level, format, ap);
+
+    if (level == FATAL) {
+      VDie(format, ap);
+    }
+  });
 }
 
 void VLog(log_level level, const char *format, va_list ap) {
diff --git a/aos/common/logging/queue_logging-tmpl.h b/aos/common/logging/queue_logging-tmpl.h
new file mode 100644
index 0000000..e0a89e4
--- /dev/null
+++ b/aos/common/logging/queue_logging-tmpl.h
@@ -0,0 +1,19 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include <functional>
+
+namespace aos {
+namespace logging {
+
+template <class T>
+void DoLogStruct(log_level level, const ::std::string &message,
+                 const T &structure) {
+  LogImplementation::DoLogStruct(level, message, T::Size(), T::GetType(),
+                                 [&structure](char * buffer)->size_t{
+    return structure.Serialize(buffer);
+  },
+                                 1);
+}
+
+}  // namespace logging
+}  // namespace aos
diff --git a/aos/common/logging/queue_logging.cc b/aos/common/logging/queue_logging.cc
new file mode 100644
index 0000000..eefb4ac
--- /dev/null
+++ b/aos/common/logging/queue_logging.cc
@@ -0,0 +1,35 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include "aos/common/die.h"
+#include "aos/common/queue_types.h"
+
+namespace aos {
+namespace logging {
+
+void LogImplementation::DoLogStruct(
+    log_level level, const ::std::string &message, size_t size,
+    const MessageType *type, const ::std::function<size_t(char *)> &serialize,
+    int levels) {
+  internal::RunWithCurrentImplementation(
+      levels, [&](LogImplementation * implementation) {
+    implementation->LogStruct(level, message, size, type, serialize);
+
+    if (level == FATAL) {
+      char serialized[1024];
+      if (size > sizeof(serialize)) {
+        Die("LOG(FATAL) structure too big to serialize");
+      }
+      size_t used = serialize(serialized);
+      char printed[LOG_MESSAGE_LEN];
+      size_t printed_bytes = sizeof(printed);
+      if (!PrintMessage(printed, &printed_bytes, serialized, &used, *type)) {
+        Die("LOG(FATAL) PrintMessage call failed");
+      }
+      Die("%.*s: %.*s\n", static_cast<int>(message.size()), message.data(),
+          static_cast<int>(printed_bytes), printed);
+    }
+  });
+}
+
+}  // namespace logging
+}  // namespace aos
diff --git a/aos/common/logging/queue_logging.h b/aos/common/logging/queue_logging.h
new file mode 100644
index 0000000..191d4c7
--- /dev/null
+++ b/aos/common/logging/queue_logging.h
@@ -0,0 +1,36 @@
+#ifndef AOS_COMMON_LOGGING_QUEUE_LOGGING_H_
+#define AOS_COMMON_LOGGING_QUEUE_LOGGING_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace logging {
+
+#define LOG_STRUCT(level, message, structure)                          \
+  do {                                                                 \
+    static const ::std::string kAosLoggingMessage(                     \
+        LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": " message);         \
+    ::aos::logging::DoLogStruct(level, kAosLoggingMessage, structure); \
+    /* so that GCC knows that it won't return */                       \
+    if (level == FATAL) {                                              \
+      fprintf(stderr, "DoLogStruct(FATAL) fell through!!!!!\n");       \
+      printf("see stderr\n");                                          \
+      abort();                                                         \
+    }                                                                  \
+  } while (false)
+
+template <class T>
+void DoLogStruct(log_level level, const ::std::string &message,
+                 const T &structure);
+
+}  // namespace logging
+}  // namespace aos
+
+#include "aos/common/logging/queue_logging-tmpl.h"
+
+#endif  // AOS_COMMON_LOGGING_QUEUE_LOGGING_H_