diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2db14613465be0ed39a2aa45c30fd67c2481919e..15959caacb606bda2115692acfafcc46d1923a7c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -44,6 +44,7 @@ list (APPEND opendht_HEADERS
 	include/opendht/value.h
 	include/opendht/dht.h
 	include/opendht/securedht.h
+	include/opendht/log.h
 	include/opendht.h
 )
 
diff --git a/include/opendht.h b/include/opendht.h
index 29fab07990a874380debac18b741e65c374f29ee..d7caf3dc627b8e1439fcae1767179f9dbf640f60 100644
--- a/include/opendht.h
+++ b/include/opendht.h
@@ -24,4 +24,5 @@
 #include "opendht/infohash.h"
 #include "opendht/securedht.h"
 #include "opendht/dhtrunner.h"
+#include "opendht/log.h"
 #include "opendht/default_types.h"
diff --git a/include/opendht/log.h b/include/opendht/log.h
new file mode 100644
index 0000000000000000000000000000000000000000..0838ed6d7c18321f02a097b4f4a700f02c6bdcc4
--- /dev/null
+++ b/include/opendht/log.h
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (C) 2014-2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  This program 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.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#pragma once
+
+#include "dhtrunner.h"
+
+#include <iostream>
+#include <fstream>
+
+namespace dht {
+namespace log {
+
+/**
+ * Terminal colors for logging
+ */
+namespace Color {
+	enum Code {
+		FG_RED      = 31,
+		FG_GREEN    = 32,
+		FG_YELLOW   = 33,
+		FG_BLUE     = 34,
+		FG_DEFAULT  = 39,
+		BG_RED      = 41,
+		BG_GREEN    = 42,
+		BG_BLUE     = 44,
+		BG_DEFAULT  = 49
+	};
+	class Modifier {
+		const Code code;
+	public:
+		constexpr Modifier(Code pCode) : code(pCode) {}
+		friend std::ostream&
+		operator<<(std::ostream& os, const Modifier& mod) {
+			return os << "\033[" << mod.code << 'm';
+		}
+	};
+}
+
+constexpr const Color::Modifier def(Color::FG_DEFAULT);
+constexpr const Color::Modifier red(Color::FG_RED);
+constexpr const Color::Modifier yellow(Color::FG_YELLOW);
+
+/**
+ * Print va_list to std::ostream (used for logging).
+ */
+void
+printLog(std::ostream& s, char const* m, va_list args) {
+	static constexpr int BUF_SZ = 8192;
+	char buffer[BUF_SZ];
+	int ret = vsnprintf(buffer, sizeof(buffer), m, args);
+	if (ret < 0)
+		return;
+	s.write(buffer, std::min(ret, BUF_SZ));
+	if (ret >= BUF_SZ)
+		s << "[[TRUNCATED]]";
+	s.put('\n');
+}
+
+void
+enableLogging(dht::DhtRunner& dht)
+{
+	dht.setLoggers(
+		[](char const* m, va_list args){ std::cerr << red; printLog(std::cerr, m, args); std::cerr << def; },
+		[](char const* m, va_list args){ std::cout << yellow; printLog(std::cout, m, args); std::cout << def; },
+		[](char const* m, va_list args){ printLog(std::cout, m, args); }
+	);
+}
+
+void
+enableFileLogging(dht::DhtRunner& dht, const std::string& path)
+{
+	auto logfile = std::make_shared<std::fstream>();
+	logfile->open(path, std::ios::out);
+
+	dht.setLoggers(
+		[=](char const* m, va_list args){ printLog(*logfile, m, args); },
+		[=](char const* m, va_list args){ printLog(*logfile, m, args); },
+		[=](char const* m, va_list args){ printLog(*logfile, m, args); }
+	);
+}
+
+void
+disableLogging(dht::DhtRunner& dht)
+{
+	dht.setLoggers(dht::NOLOG, dht::NOLOG, dht::NOLOG);
+}
+
+} /* log */
+} /* dht  */
+
diff --git a/src/Makefile.am b/src/Makefile.am
index d066626914410fdaa324f0c3819e5cea823cf205..4a645504e5cc3061a22816a75a262dc73e404a3c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -28,4 +28,5 @@ nobase_include_HEADERS = \
         ../include/opendht/securedht.h \
         ../include/opendht/dhtrunner.h \
         ../include/opendht/default_types.h \
+        ../include/opendht/log.h \
         ../include/opendht/rng.h
diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp
index 092bbd6bc51c6aba194941e1e56df687c4211588..718733bbf131ec07e779e3010e054829551956a0 100644
--- a/tools/dhtnode.cpp
+++ b/tools/dhtnode.cpp
@@ -137,9 +137,9 @@ void cmd_loop(DhtRunner& dht, dht_params& params)
         } else if (op == "log") {
             params.log = !params.log;
             if (params.log)
-                enableLogging(dht);
+                log::enableLogging(dht);
             else
-                disableLogging(dht);
+                log::disableLogging(dht);
             continue;
         }
 
@@ -261,9 +261,9 @@ main(int argc, char **argv)
 
         if (params.log) {
             if (not params.logfile.empty())
-                enableFileLogging(dht, params.logfile);
+                log::enableFileLogging(dht, params.logfile);
             else
-                enableLogging(dht);
+                log::enableLogging(dht);
         }
 
         if (not params.bootstrap.first.empty()) {