diff --git a/include/dynamic-graph/real-time-logger.h b/include/dynamic-graph/real-time-logger.h index b0a4283e2994a44ac034343b288777cc407a30fe..f87801101079e236b90d719a58c1964ea00f9057 100644 --- a/include/dynamic-graph/real-time-logger.h +++ b/include/dynamic-graph/real-time-logger.h @@ -21,6 +21,7 @@ # include <boost/circular_buffer.hpp> # include <boost/shared_ptr.hpp> +# include <boost/thread/mutex.hpp> # include <dynamic-graph/config.hh> # include <dynamic-graph/debug.h> @@ -31,12 +32,20 @@ namespace dynamicgraph /// \ingroup debug /// /// \brief Stream for the real-time logger. + /// + /// You should inherit from this class in order to redirect the logs where you + /// want. + /// \sa LoggerIOStream class LoggerStream { public: virtual void write (const char* c) = 0; }; + /// Write to an ostream object. + /// + /// The easieast is to use the macro \ref dgADD_OSTREAM_TO_RTLOG(ostr) where + /// `ostr` can be `std::cout` or an std::ofstream... class LoggerIOStream : public LoggerStream { public: @@ -48,12 +57,18 @@ namespace dynamicgraph typedef boost::shared_ptr <LoggerStream> LoggerStreamPtr_t; class RealTimeLogger; + + /// \cond DEVEL + /// \brief write entries to intenal buffer. + /// + /// The entry starts when an instance is created and ends when is is deleted. + /// This class is only used by RealTimeLogger. class RTLoggerStream { public: RTLoggerStream (RealTimeLogger* logger, std::ostream& os) : logger_(logger), os_ (os) {} - template <typename T> inline RTLoggerStream& operator<< (T t) { os_ << t; return *this; } - inline RTLoggerStream& operator<< (std::ostream& (*pf)(std::ostream&)) { os_ << pf; return *this; } + template <typename T> inline RTLoggerStream& operator<< (T t) { if (logger_!=NULL) os_ << t; return *this; } + inline RTLoggerStream& operator<< (std::ostream& (*pf)(std::ostream&)) { if (logger_!=NULL) os_ << pf; return *this; } ~RTLoggerStream(); private: @@ -61,10 +76,30 @@ namespace dynamicgraph RealTimeLogger* logger_; std::ostream& os_; }; + /// \endcond DEVEL /// \ingroup debug /// /// \brief Main class of the real-time logger. + /// + /// It is intended to be used like this: + /// \code + /// #define ENABLE_RT_LOG + /// #include <dynamic-graph/real-time-logger.h> + /// + /// // Somewhere in the main function of your executable + /// int main (int argc, char** argv) { + /// dgADD_OSTREAM_TO_RTLOG (std::cout); + /// } + /// + /// // Somewhere in your library + /// dgRTLOG() << "your message. Prefer to use \n than std::endl." + /// \endcode + /// + /// \note Thread safety. This class expects to have: + /// - only one reader: the one who take the log entries and write them somewhere. + /// - one writer at a time. Writing to the logs is **never** a blocking + /// operation. If the resource is busy, the log entry is discarded. class DYNAMIC_GRAPH_DLLAPI RealTimeLogger { public: @@ -88,7 +123,7 @@ namespace dynamicgraph /// The message is considered finished when the object is destroyed. RTLoggerStream front(); - inline void frontReady() { backIdx_ = (backIdx_+1) % buffer_.size(); } + inline void frontReady() { backIdx_ = (backIdx_+1) % buffer_.size(); wmutex.unlock(); } inline bool empty () const { @@ -126,6 +161,10 @@ namespace dynamicgraph std::size_t backIdx_; std::ostream oss_; + /// The writer mutex. + boost::mutex wmutex; + std::size_t nbDiscarded_; + struct thread; static RealTimeLogger* instance_; diff --git a/src/debug/real-time-logger.cpp b/src/debug/real-time-logger.cpp index d98f98cc0ec370240488a79d606073678be612ec..9bbcd4094cec0fe7763cc1eb22066157079db3b0 100644 --- a/src/debug/real-time-logger.cpp +++ b/src/debug/real-time-logger.cpp @@ -27,6 +27,7 @@ namespace dynamicgraph RealTimeLogger::RealTimeLogger (const std::size_t& bufferSize) : buffer_(bufferSize, NULL) , oss_ (NULL) + , nbDiscarded_ (0) { for (std::size_t i = 0; i < buffer_.size(); ++i) buffer_[i] = new Data; @@ -54,8 +55,15 @@ namespace dynamicgraph RTLoggerStream RealTimeLogger::front () { + // If no output or if buffer is full, discard message. if (outputs_.empty() || full()) { - oss_.rdbuf(NULL); + nbDiscarded_++; + return RTLoggerStream (NULL, oss_); + } + bool alone = wmutex.try_lock(); + // If someone is writting, discard message. + if (!alone) { + nbDiscarded_++; return RTLoggerStream (NULL, oss_); } Data* data = buffer_[backIdx_]; diff --git a/tests/real-time-logger.cpp b/tests/real-time-logger.cpp index 055a3650dfe2c658c5a48dbece4563007d5da1b1..c96b15644123b50844eea72b86e95dba8ae74303 100644 --- a/tests/real-time-logger.cpp +++ b/tests/real-time-logger.cpp @@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE (monothread) int spinNb = 0; while (rtl.spinOnce()) { spinNb++; } - BOOST_CHECK(spinNb == 9); + BOOST_CHECK_EQUAL(spinNb, 9); rtl.front() << "This msg should be short." << '\n'; rtl.spinOnce(); @@ -58,10 +58,10 @@ BOOST_AUTO_TEST_CASE (multithread) dgADD_OSTREAM_TO_RTLOG (std::cout); for (std::size_t i = 0; i < rtl.getBufferSize()-1; ++i) - rtl.front() << "Call number " << i << '\n'; + dgRTLOG() << "Call number " << i << '\n'; for (std::size_t i = 0; i < 12; ++i) { boost::this_thread::sleep(boost::posix_time::milliseconds(20)); - rtl.front() << "Call number " << i << std::endl; + dgRTLOG() << "Call number " << i << std::endl; BOOST_CHECK (!rtl.full()); }