1 |
|
|
// -*- mode: c++ -*- |
2 |
|
|
// Copyright 2018, Joseph Mirabel LAAS-CNRS |
3 |
|
|
// |
4 |
|
|
|
5 |
|
|
#ifndef DYNAMIC_GRAPH_LOGGER_REAL_TIME_DEF_H |
6 |
|
|
#define DYNAMIC_GRAPH_LOGGER_REAL_TIME_DEF_H |
7 |
|
|
#include <boost/shared_ptr.hpp> |
8 |
|
|
#include <boost/thread/mutex.hpp> |
9 |
|
|
#include <dynamic-graph/config.hh> |
10 |
|
|
#include <sstream> |
11 |
|
|
#include <vector> |
12 |
|
|
|
13 |
|
|
namespace dynamicgraph { |
14 |
|
|
/// \ingroup debug |
15 |
|
|
/// |
16 |
|
|
/// \brief Stream for the real-time logger. |
17 |
|
|
/// |
18 |
|
|
/// You should inherit from this class in order to redirect the logs where you |
19 |
|
|
/// want. |
20 |
|
|
/// \sa LoggerIOStream |
21 |
|
|
class LoggerStream { |
22 |
|
|
public: |
23 |
|
|
virtual void write(const char *c) = 0; |
24 |
|
|
}; |
25 |
|
|
|
26 |
|
|
/// Write to an ostream object. |
27 |
|
|
/// |
28 |
|
|
/// The easieast is to use the macro dgADD_OSTREAM_TO_RTLOG(ostr) where |
29 |
|
|
/// `ostr` can be `std::cout` or an std::ofstream... |
30 |
|
|
class LoggerIOStream : public LoggerStream { |
31 |
|
|
public: |
32 |
|
4 |
LoggerIOStream(std::ostream &os) : os_(os) {} |
33 |
|
16 |
virtual ~LoggerIOStream() {} |
34 |
|
122032 |
virtual void write(const char *c) { os_ << c; } |
35 |
|
|
|
36 |
|
|
private: |
37 |
|
|
std::ostream &os_; |
38 |
|
|
}; |
39 |
|
|
typedef boost::shared_ptr<LoggerStream> LoggerStreamPtr_t; |
40 |
|
|
|
41 |
|
|
class RealTimeLogger; |
42 |
|
|
|
43 |
|
|
/// \cond DEVEL |
44 |
|
|
/// \brief write entries to intenal buffer. |
45 |
|
|
/// |
46 |
|
|
/// The entry starts when an instance is created and ends when is is deleted. |
47 |
|
|
/// This class is only used by RealTimeLogger. |
48 |
|
|
class RTLoggerStream { |
49 |
|
|
public: |
50 |
|
161044 |
inline RTLoggerStream(RealTimeLogger *logger, std::ostream &os) |
51 |
|
161044 |
: ok_(logger != NULL), logger_(logger), os_(os) {} |
52 |
|
|
template <typename T> |
53 |
|
658112 |
inline RTLoggerStream &operator<<(T t) { |
54 |
✓✓ |
658112 |
if (ok_) os_ << t; |
55 |
|
658112 |
return *this; |
56 |
|
|
} |
57 |
|
12 |
inline RTLoggerStream &operator<<(std::ostream &(*pf)(std::ostream &)) { |
58 |
✓✗ |
12 |
if (ok_) os_ << pf; |
59 |
|
12 |
return *this; |
60 |
|
|
} |
61 |
|
|
|
62 |
|
|
inline ~RTLoggerStream(); |
63 |
|
|
|
64 |
|
20 |
inline bool isNull() { return !ok_; } |
65 |
|
|
|
66 |
|
|
private: |
67 |
|
|
const bool ok_; |
68 |
|
|
RealTimeLogger *logger_; |
69 |
|
|
std::ostream &os_; |
70 |
|
|
}; |
71 |
|
|
/// \endcond DEVEL |
72 |
|
|
|
73 |
|
|
/// \ingroup debug |
74 |
|
|
/// |
75 |
|
|
/// \brief Main class of the real-time logger. |
76 |
|
|
/// |
77 |
|
|
/// It is intended to be used like this: |
78 |
|
|
/// \code |
79 |
|
|
/// #define ENABLE_RT_LOG |
80 |
|
|
/// #include <dynamic-graph/real-time-logger.h> |
81 |
|
|
/// |
82 |
|
|
/// // Somewhere in the main function of your executable |
83 |
|
|
/// int main (int argc, char** argv) { |
84 |
|
|
/// dgADD_OSTREAM_TO_RTLOG (std::cout); |
85 |
|
|
/// } |
86 |
|
|
/// |
87 |
|
|
/// // Somewhere in your library |
88 |
|
|
/// dgRTLOG() << "your message. Prefer to use \n than std::endl." |
89 |
|
|
/// \endcode |
90 |
|
|
/// |
91 |
|
|
/// \note Thread safety. This class expects to have: |
92 |
|
|
/// - only one reader: the one who take the log entries and write them |
93 |
|
|
/// somewhere. |
94 |
|
|
/// - one writer at a time. Writing to the logs is **never** a blocking |
95 |
|
|
/// operation. If the resource is busy, the log entry is discarded. |
96 |
|
|
class DYNAMIC_GRAPH_DLLAPI RealTimeLogger { |
97 |
|
|
public: |
98 |
|
|
static RealTimeLogger &instance(); |
99 |
|
|
|
100 |
|
|
static void destroy(); |
101 |
|
|
|
102 |
|
|
/// \todo add an argument to preallocate the internal string |
103 |
|
|
/// to a given size. |
104 |
|
|
RealTimeLogger(const std::size_t &bufferSize); |
105 |
|
|
|
106 |
|
|
inline void clearOutputStreams() { outputs_.clear(); } |
107 |
|
|
|
108 |
|
4 |
inline void addOutputStream(const LoggerStreamPtr_t &os) { |
109 |
|
4 |
outputs_.push_back(os); |
110 |
|
4 |
} |
111 |
|
|
|
112 |
|
|
/// Write next message to output. |
113 |
|
|
/// It does nothing if the buffer is empty. |
114 |
|
|
/// \return true if it wrote something |
115 |
|
|
bool spinOnce(); |
116 |
|
|
|
117 |
|
|
/// Return an object onto which a real-time thread can write. |
118 |
|
|
/// The message is considered finished when the object is destroyed. |
119 |
|
|
RTLoggerStream front(); |
120 |
|
|
|
121 |
|
|
/// Return an empty stream object. |
122 |
|
25210 |
RTLoggerStream emptyStream() { return RTLoggerStream(NULL, oss_); } |
123 |
|
|
|
124 |
|
122032 |
inline void frontReady() { |
125 |
|
122032 |
backIdx_ = (backIdx_ + 1) % buffer_.size(); |
126 |
|
122032 |
wmutex.unlock(); |
127 |
|
122032 |
} |
128 |
|
|
|
129 |
|
123948 |
inline bool empty() const { return frontIdx_ == backIdx_; } |
130 |
|
|
|
131 |
|
131048 |
inline bool full() const { |
132 |
|
131048 |
return ((backIdx_ + 1) % buffer_.size()) == frontIdx_; |
133 |
|
|
} |
134 |
|
|
|
135 |
|
|
inline std::size_t size() const { |
136 |
|
|
if (frontIdx_ <= backIdx_) |
137 |
|
|
return backIdx_ - frontIdx_; |
138 |
|
|
else |
139 |
|
|
return backIdx_ + buffer_.size() - frontIdx_; |
140 |
|
|
} |
141 |
|
|
|
142 |
|
1000 |
inline std::size_t getBufferSize() { return buffer_.size(); } |
143 |
|
|
|
144 |
|
|
~RealTimeLogger(); |
145 |
|
|
|
146 |
|
|
private: |
147 |
|
|
struct Data { |
148 |
|
|
std::stringbuf buf; |
149 |
|
|
}; |
150 |
|
|
|
151 |
|
|
std::vector<LoggerStreamPtr_t> outputs_; |
152 |
|
|
std::vector<Data *> buffer_; |
153 |
|
|
/// Index of the next value to be read. |
154 |
|
|
std::size_t frontIdx_; |
155 |
|
|
/// Index of the slot where to write next value |
156 |
|
|
/// (does not contain valid data). |
157 |
|
|
std::size_t backIdx_; |
158 |
|
|
std::ostream oss_; |
159 |
|
|
|
160 |
|
|
/// The writer mutex. |
161 |
|
|
boost::mutex wmutex; |
162 |
|
|
std::size_t nbDiscarded_; |
163 |
|
|
|
164 |
|
|
struct thread; |
165 |
|
|
|
166 |
|
|
static RealTimeLogger *instance_; |
167 |
|
|
static thread *thread_; |
168 |
|
|
}; |
169 |
|
|
|
170 |
|
322088 |
RTLoggerStream::~RTLoggerStream() { |
171 |
✓✓ |
161044 |
if (ok_) { |
172 |
|
122032 |
os_ << std::ends; |
173 |
|
122032 |
logger_->frontReady(); |
174 |
|
|
} |
175 |
|
161044 |
} |
176 |
|
|
|
177 |
|
|
} // end of namespace dynamicgraph |
178 |
|
|
|
179 |
|
|
#endif //! DYNAMIC_GRAPH_LOGGER_REAL_TIME_DEF_H |