diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 85df52f909011a6eea584be7490f7cfbae3c044e..3ce88f9b7f2c934efa15eec9e6aeaeb9fd6bd5f7 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -28,6 +28,7 @@ fwd.hh
 null-ptr.hh
 contiifstream.h
 debug.h
+real-time-logger.h
 
 dynamic-graph-api.h
 
diff --git a/include/dynamic-graph/real-time-logger.h b/include/dynamic-graph/real-time-logger.h
new file mode 100644
index 0000000000000000000000000000000000000000..55dea17f3fe0af88181eedf74baea4d8901d86be
--- /dev/null
+++ b/include/dynamic-graph/real-time-logger.h
@@ -0,0 +1,127 @@
+// -*- mode: c++ -*-
+// Copyright 2018, Joseph Mirabel LAAS-CNRS
+//
+// This file is part of dynamic-graph.
+// dynamic-graph is free software: you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation, either version 3 of
+// the License, or (at your option) any later version.
+//
+// dynamic-graph 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 Lesser Public License for more details.  You should have
+// received a copy of the GNU Lesser General Public License along with
+// dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef DYNAMIC_GRAPH_LOGGER_REAL_TIME_H
+# define DYNAMIC_GRAPH_LOGGER_REAL_TIME_H
+# include <sstream>
+# include <vector>
+
+# include <boost/circular_buffer.hpp>
+# include <boost/shared_ptr.hpp>
+
+# include <dynamic-graph/config.hh>
+
+namespace dynamicgraph
+{
+  /// \ingroup debug
+  ///
+  /// \brief Stream for the real-time logger.
+  class LoggerStream
+  {
+    public:
+      virtual void write (const char* c) = 0;
+  };
+
+  class LoggerIOStream : public LoggerStream
+  {
+    public:
+      LoggerIOStream (std::ostream& os) : os_ (os) {}
+      virtual void write (const char* c) { os_ << c; }
+    private:
+      std::ostream& os_;
+  };
+  typedef boost::shared_ptr <LoggerStream> LoggerStreamPtr_t;
+
+  class RealTimeLogger;
+  class RTLoggerStream
+  {
+    public:
+      RTLoggerStream (RealTimeLogger* logger, std::ostream& os) : logger_(logger), os_ (os) {}
+      template <typename T> RTLoggerStream& operator<< (T  t) { os_ << t; return *this; }
+
+      ~RTLoggerStream();
+    private:
+
+      RealTimeLogger* logger_;
+      std::ostream& os_;
+  };
+
+  /// \ingroup debug
+  ///
+  /// \brief Main class of the real-time logger.
+  class DYNAMIC_GRAPH_DLLAPI RealTimeLogger
+  {
+  public:
+    /// \todo add an argument to preallocate the internal string to a given size.
+    RealTimeLogger (const std::size_t& bufferSize);
+
+    inline void clearOutputStreams () { outputs_.clear(); }
+
+    inline void addOutputStream (const LoggerStreamPtr_t& os) { outputs_.push_back(os); }
+
+    /// The function to be called by the thread who exports the outputs
+    //void spin ();
+
+    /// Write next message to output.
+    /// It does nothing if the buffer is empty.
+    /// \return true if it wrote something
+    bool spinOnce ();
+
+    /// Return an object onto which a real-time thread can write.
+    /// The message is considered finished when the object is destroyed.
+    RTLoggerStream front();
+
+    inline void frontReady() { backIdx_ = (backIdx_+1) % buffer_.size(); }
+
+    inline bool empty () const
+    {
+      return frontIdx_ == backIdx_;
+    }
+
+    inline bool full () const
+    {
+      return ((backIdx_ + 1) % buffer_.size()) == frontIdx_;
+    }
+
+    inline std::size_t size () const
+    {
+      if (frontIdx_ <= backIdx_)
+        return backIdx_ - frontIdx_;
+      else
+        return backIdx_ + buffer_.size() - frontIdx_;
+    }
+
+    inline std::size_t getBufferSize () { return buffer_.capacity(); }
+
+    ~RealTimeLogger ();
+
+  private:
+
+    struct Data {
+      std::stringbuf buf;
+    };
+
+    std::vector<LoggerStreamPtr_t> outputs_;
+    std::vector<Data*> buffer_;
+    /// Index of the next value to be read.
+    std::size_t frontIdx_;
+    /// Index of the slot where to write next value (does not contain valid data).
+    std::size_t backIdx_;
+    std::ostream oss_;
+  };
+} // end of namespace dynamicgraph
+
+#endif //! DYNAMIC_GRAPH_LOGGER_REAL_TIME_H
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6a7124c763848bcb6eeff5b2b93882b57110faaf..3e167370cb4079ec41cd6e3c64ec89627755f273 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -40,6 +40,7 @@ link_directories(${Boost_LIBRARY_DIRS})
 ADD_LIBRARY(${LIBRARY_NAME}
   SHARED
   debug/debug.cpp
+  debug/real-time-logger.cpp
 
   dgraph/entity.cpp
   dgraph/factory.cpp
diff --git a/src/debug/real-time-logger.cpp b/src/debug/real-time-logger.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4386b5806bbb419a1b03d2deb8a6da87990278a5
--- /dev/null
+++ b/src/debug/real-time-logger.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2018,
+ * Joseph Mirabel
+ *
+ * LAAS-CNRS
+ *
+ * This file is part of dynamic-graph.
+ * dynamic-graph is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ * dynamic-graph 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 Lesser General Public License for more details.  You should
+ * have received a copy of the GNU Lesser General Public License along
+ * with dynamic-graph.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <dynamic-graph/real-time-logger.h>
+
+namespace dynamicgraph
+{
+  RealTimeLogger::RealTimeLogger (const std::size_t& bufferSize)
+    : buffer_(bufferSize, NULL)
+    , oss_ (NULL)
+  {
+    for (std::size_t i = 0; i < buffer_.size(); ++i)
+      buffer_[i] = new Data;
+  }
+
+  RealTimeLogger::~RealTimeLogger ()
+  {
+    // Check that we are not spinning...
+    for (std::size_t i = 0; i < buffer_.size(); ++i) delete buffer_[i];
+  }
+
+  bool RealTimeLogger::spinOnce ()
+  {
+    if (empty()) return false;
+    Data* data = buffer_[frontIdx_];
+    frontIdx_ = (frontIdx_ + 1) % buffer_.size();
+    std::string str = data->buf.str();
+    // It is important to pass str.c_str() and not str
+    // because the str object may contains a '\0' so
+    // str.size() may be different from strlen(str.c_str())
+    for (std::size_t i = 0; i < outputs_.size(); ++i)
+      outputs_[i]->write (str.c_str());
+    return true;
+  }
+
+  RTLoggerStream RealTimeLogger::front ()
+  {
+    if (outputs_.empty() || full()) {
+      oss_.rdbuf(NULL);
+      return RTLoggerStream (NULL, oss_);
+    }
+    Data* data = buffer_[backIdx_];
+    //backIdx_ = (backIdx_+1) % buffer_.size();
+    // Reset position of cursor
+    data->buf.pubseekpos(0);
+    oss_.rdbuf(&data->buf);
+    return RTLoggerStream (this, oss_);
+  }
+
+  RTLoggerStream::~RTLoggerStream()
+  {
+    os_ << std::ends;
+    if (logger_ != NULL) logger_->frontReady();
+  }
+}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 4d14693d9b7097ffddbb7356779fb4c0d7b6f021..36cd5d1c10c726da06c51bc1ee8e76af2879832c 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -67,3 +67,4 @@ DYNAMIC_GRAPH_TEST(pool)
 DYNAMIC_GRAPH_TEST(signal-time-dependent)
 DYNAMIC_GRAPH_TEST(value)
 DYNAMIC_GRAPH_TEST(signal-ptr)
+DYNAMIC_GRAPH_TEST(real-time-logger)
diff --git a/tests/real-time-logger.cpp b/tests/real-time-logger.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..064ae1003b8a70caa7817c4954acf42f838ec34a
--- /dev/null
+++ b/tests/real-time-logger.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2018,
+ * Joseph Mirabel
+ *
+ * LAAS-CNRS
+ *
+ * This file is part of dynamic-graph.
+ * dynamic-graph is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ * dynamic-graph 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 Lesser General Public License for more details.  You should
+ * have received a copy of the GNU Lesser General Public License along
+ * with dynamic-graph.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <dynamic-graph/real-time-logger.h>
+
+#define BOOST_TEST_MODULE real_time_logger
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/output_test_stream.hpp>
+
+#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+using namespace dynamicgraph;
+
+BOOST_AUTO_TEST_CASE (monothread)
+{
+  RealTimeLogger rtl (10);
+  rtl.addOutputStream (LoggerStreamPtr_t (new LoggerIOStream(std::cout)));
+  for (int i = 0; i < 9; ++i) rtl.front() << "Call number " << i << '\n';
+  BOOST_CHECK (rtl.full());
+  rtl.front() << "This call should not appear in the output" << '\n';
+
+  rtl.spinOnce();
+  BOOST_CHECK (!rtl.full());
+  rtl.front() << "This call should appear in the output" << '\n';
+
+  int spinNb = 0;
+  while (rtl.spinOnce()) { spinNb++; }
+  BOOST_CHECK(spinNb == 9);
+
+  rtl.front() << "This msg should be short." << '\n';
+  rtl.spinOnce();
+}
+
+bool requestShutdown = false;
+void spin (RealTimeLogger* logger)
+{
+  while (!requestShutdown || !logger->empty())
+  {
+    // If the logger did not write anything, it means the buffer is empty.
+    // Do a pause
+    if (!logger->spinOnce())
+      boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+  }
+}
+
+BOOST_AUTO_TEST_CASE (multithread)
+{
+  RealTimeLogger rtl (10);
+  rtl.addOutputStream (LoggerStreamPtr_t (new LoggerIOStream(std::cout)));
+
+  boost::thread loggerThread (spin, &rtl);
+
+  for (int i = 0; i < 10; ++i) {
+    boost::this_thread::sleep(boost::posix_time::milliseconds(20));
+    rtl.front() << "Call number " << i << '\n';
+    BOOST_CHECK (!rtl.full());
+  }
+
+  rtl.front() << "This call should appear in the output" << '\n';
+
+  requestShutdown = true;
+  loggerThread.join();
+}