diff --git a/Makefile.am b/Makefile.am
index 07cb3677..c2087f7c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -46,6 +46,9 @@ UTIL_FILES = \
json.hpp \
lerp.cpp \
lerp.hpp \
+ log.cpp \
+ log.hpp \
+ log.ipp \
maths.cpp \
maths.hpp \
maths/matrix.cpp \
diff --git a/log.cpp b/log.cpp
new file mode 100644
index 00000000..73fc8f99
--- /dev/null
+++ b/log.cpp
@@ -0,0 +1,89 @@
+/*
+ * This file is part of libgim.
+ *
+ * libgim is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * libgim is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libgim. If not, see .
+ *
+ * Copyright 2012 Danny Robson
+ */
+
+
+#include "log.hpp"
+
+#include "debug.hpp"
+#include "types.hpp"
+
+#include
+#include
+
+#include
+
+using namespace util;
+using std::string;
+
+void
+check_level (level_t l)
+ { check (l >= 0 && l < NUM_LEVELS); }
+
+
+const string&
+level_to_string (level_t l) {
+ check_level (l);
+
+ static const std::string level_names[] = {
+ "EMERGENCY",
+ "ALERT",
+ "CRITICAL",
+ "ERROR",
+ "WARNING",
+ "NOTICE",
+ "INFORMATIONAL",
+ "DEBUG"
+ };
+
+ return level_names[l];
+}
+
+
+std::ostream&
+util::operator<< (std::ostream& os, level_t l) {
+ os << level_to_string (l);
+ return os;
+}
+
+
+void
+util::log (level_t l, const std::string &format)
+ { detail::log (l, boost::format (format)); }
+
+
+void
+util::detail::log (level_t level, boost::format &&format) {
+ static const boost::format LEVEL_FORMAT ("%s [%s] ");
+
+ char time_string[strlen("YYYY-mm-dd HHMMh") + 1];
+ time_t unix_time = time (nullptr);
+ if (0 == strftime (time_string,
+ elems (time_string),
+ "%Y-%m-%d %H%Mh",
+ localtime (&unix_time))) {
+ unusual ();
+ return;
+ }
+
+ std::cerr << boost::format (LEVEL_FORMAT)
+ % time_string
+ % level
+ << format
+ << "\n";
+}
diff --git a/log.hpp b/log.hpp
new file mode 100644
index 00000000..4abc0920
--- /dev/null
+++ b/log.hpp
@@ -0,0 +1,65 @@
+/*
+ * This file is part of libgim.
+ *
+ * libgim is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * libgim is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libgim. If not, see .
+ *
+ * Copyright 2012 Danny Robson
+ */
+
+#ifndef __UTIL_LOG_HPP
+#define __UTIL_LOG_HPP
+
+#include
+#include
+
+namespace util {
+ // rfc5424 log levels. It is assumed they are contiguous to simplify array
+ // indexing in logging code.
+ //
+ enum level_t {
+ EMERGENCY, /** system is unusable */
+ ALERT, /** action must be taken immediately */
+ CRITICAL, /** critical conditions */
+ ERROR, /** error conditions */
+ WARNING, /** warning conditions */
+ NOTICE, /** normal but significant condition */
+ INFORMATIONAL,
+ INFO = INFORMATIONAL, /** informational messages */
+ DEBUG, /** debug-level messages */
+
+ NUM_LEVELS
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, level_t);
+
+ void log (level_t, const std::string &format);
+
+ template
+ void log (level_t, const std::string &format, tail ..._tail);
+
+ #define LOG_EMERGENCY(...) do { log(util::EMERGENCY, ##__VA_ARGS__); } while (0)
+ #define LOG_ALERT(...) do { log(util::ALERT, ##__VA_ARGS__); } while (0)
+ #define LOG_CRITICAL(...) do { log(util::CRITICAL, ##__VA_ARGS__); } while (0)
+ #define LOG_ERROR(...) do { log(util::ERROR, ##__VA_ARGS__); } while (0)
+ #define LOG_WARNING(...) do { log(util::WARNING, ##__VA_ARGS__); } while (0)
+ #define LOG_NOTICE(...) do { log(util::NOTICE, ##__VA_ARGS__); } while (0)
+ #define LOG_INFO(...) do { log(util::INFO, ##__VA_ARGS__); } while (0)
+ #define LOG_DEBUG(...) do { log(util::DEBUG, ##__VA_ARGS__); } while (0)
+}
+
+#include "log.ipp"
+
+#endif
+
diff --git a/log.ipp b/log.ipp
new file mode 100644
index 00000000..9c70b49e
--- /dev/null
+++ b/log.ipp
@@ -0,0 +1,43 @@
+/*
+ * This file is part of libgim.
+ *
+ * libgim is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * libgim is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libgim. If not, see .
+ *
+ * Copyright 2012 Danny Robson
+ */
+
+#ifndef __UTIL_LOG_IPP
+#define __UTIL_LOG_IPP
+
+#include
+
+namespace util {
+ namespace detail {
+ void
+ log (level_t l, boost::format &&format);
+
+ template
+ void
+ log (level_t l, boost::format &&format, const T &val, tail ..._tail) {
+ ::util::detail::log (l, std::move (format % val), _tail...);
+ }
+ }
+
+ template
+ void log (level_t l, const std::string &format, tail ..._tail)
+ { detail::log (l, std::move (boost::format (format)), _tail...); }
+}
+
+
+#endif