hmbdc
simplify-high-performance-messaging-programming
LoggerT.hpp
1 #include "hmbdc/Copyright.hpp"
2 #pragma once
3 
4 #include "hmbdc/app/Client.hpp"
5 #include "hmbdc/text/LfbStream.hpp"
6 #include "hmbdc/time/Time.hpp"
7 #include "hmbdc/pattern/GuardedSingleton.hpp"
8 #include <mutex>
9 
10 #ifdef HMBDC_LOG_CONTEXT //if defined, it has to be 'compile time sized' Context
11 #include "hmbdc/app/Context.hpp"
12 #include <iostream>
13 #include <string>
14 #include <memory>
15 #include <tuple>
16 
17 #define HMBDC_LOG_D(...) hmbdc::app::LoggerT<HMBDC_LOG_CONTEXT>::instance().LOG_D(__VA_ARGS__, LogTrailer(__FILE__, __LINE__))
18 #define HMBDC_LOG_N(...) hmbdc::app::LoggerT<HMBDC_LOG_CONTEXT>::instance().LOG_N(__VA_ARGS__, LogTrailer(__FILE__, __LINE__))
19 #define HMBDC_LOG_W(...) hmbdc::app::LoggerT<HMBDC_LOG_CONTEXT>::instance().LOG_W(__VA_ARGS__, LogTrailer(__FILE__, __LINE__))
20 #define HMBDC_LOG_C(...) hmbdc::app::LoggerT<HMBDC_LOG_CONTEXT>::instance().LOG_C(__VA_ARGS__, LogTrailer(__FILE__, __LINE__))
21 #define HMBDC_LOG_DEBUG(x) hmbdc::app::LoggerT<HMBDC_LOG_CONTEXT>::instance().LOG_D(#x "=", x, LogTrailer(__FILE__, __LINE__))
22 
23 #define HMBDC_LOG_d(...) hmbdc::app::LoggerT<HMBDC_LOG_CONTEXT>::instance().LOG_D(__VA_ARGS__, EmptyLogTrailer())
24 #define HMBDC_LOG_n(...) hmbdc::app::LoggerT<HMBDC_LOG_CONTEXT>::instance().LOG_N(__VA_ARGS__, EmptyLogTrailer())
25 #define HMBDC_LOG_w(...) hmbdc::app::LoggerT<HMBDC_LOG_CONTEXT>::instance().LOG_W(__VA_ARGS__, EmptyLogTrailer())
26 #define HMBDC_LOG_c(...) hmbdc::app::LoggerT<HMBDC_LOG_CONTEXT>::instance().LOG_C(__VA_ARGS__, EmptyLogTrailer())
27 
28 #define HMBDC_LOGGER hmbdc::app::LoggerT<HMBDC_LOG_CONTEXT>
29 
30 #else
31 
32 #define HMBDC_LOG_D(...) if (&hmbdc::app::SimpleLogger::instance()) hmbdc::app::SimpleLogger::instance().LOG_D(hmbdc::time::SysTime::now(), g_SimpleLogLevelStr[0], __VA_ARGS__, LogTrailer(__FILE__, __LINE__))
33 #define HMBDC_LOG_N(...) if (&hmbdc::app::SimpleLogger::instance()) hmbdc::app::SimpleLogger::instance().LOG_N(hmbdc::time::SysTime::now(), g_SimpleLogLevelStr[1], __VA_ARGS__, LogTrailer(__FILE__, __LINE__))
34 #define HMBDC_LOG_W(...) if (&hmbdc::app::SimpleLogger::instance()) hmbdc::app::SimpleLogger::instance().LOG_W(hmbdc::time::SysTime::now(), g_SimpleLogLevelStr[2], __VA_ARGS__, LogTrailer(__FILE__, __LINE__))
35 #define HMBDC_LOG_C(...) if (&hmbdc::app::SimpleLogger::instance()) hmbdc::app::SimpleLogger::instance().LOG_C(hmbdc::time::SysTime::now(), g_SimpleLogLevelStr[3], __VA_ARGS__, LogTrailer(__FILE__, __LINE__))
36 #define HMBDC_LOG_DEBUG(x) if (&hmbdc::app::SimpleLogger::instance()) hmbdc::app::SimpleLogger::instance().LOG_D(hmbdc::time::SysTime::now(), g_SimpleLogLevelStr[0], #x "=", x, LogTrailer(__FILE__, __LINE__))
37 #define HMBDC_LOG_d(...) if (&hmbdc::app::SimpleLogger::instance()) hmbdc::app::SimpleLogger::instance().LOG_D(hmbdc::time::SysTime::now(), g_SimpleLogLevelStr[0], __VA_ARGS__, '\n')
38 #define HMBDC_LOG_n(...) if (&hmbdc::app::SimpleLogger::instance()) hmbdc::app::SimpleLogger::instance().LOG_N(hmbdc::time::SysTime::now(), g_SimpleLogLevelStr[1], __VA_ARGS__, '\n')
39 #define HMBDC_LOG_w(...) if (&hmbdc::app::SimpleLogger::instance()) hmbdc::app::SimpleLogger::instance().LOG_W(hmbdc::time::SysTime::now(), g_SimpleLogLevelStr[2], __VA_ARGS__, '\n')
40 #define HMBDC_LOG_c(...) if (&hmbdc::app::SimpleLogger::instance()) hmbdc::app::SimpleLogger::instance().LOG_C(hmbdc::time::SysTime::now(), g_SimpleLogLevelStr[3], __VA_ARGS__, '\n')
41 
42 #define HMBDC_LOGGER hmbdc::app::SimpleLogger
43 #endif //HMBDC_LOG_CONTEXT
44 
45 namespace hmbdc { namespace app {
46 
47 char const g_LogLevelStr[][12] = {
48  "DEBUG : ",
49  "NOTICE: ",
50  "WARNING: ",
51  "CRITICAL: "
52 };
53 
54 char const g_SimpleLogLevelStr[][12 + 1] = {
55  " debug : ",
56  " notice: ",
57  " warning: ",
58  " critical: "
59 };
60 
61 struct LogTrailer {
62  LogTrailer(char const* const file, int line)
63  : f(file)
64  , l(line) {
65  }
66  char const* const f;
67  int l;
68 
69  friend std::ostream& operator << (std::ostream& os, LogTrailer const& t) {
70  os << ' ' << t.f << ':' << t.l << std::endl;
71  return os;
72  }
73 };
75  friend std::ostream& operator << (std::ostream& os, EmptyLogTrailer const&) {
76  os << std::endl;
77  return os;
78  }
79 };
80 
81 /**
82  * @class LoggerT<>
83  * @brief a high performance async logger that doesn't penalize logging threads as much
84  * when the logging load is heavy
85  * @details see @example hmbdc-log.cpp
86  *
87  */
88 template <typename Ctx>
89 struct LoggerT
90 : Client<LoggerT<Ctx>, LoggingT<Ctx::MAX_MESSAGE_SIZE>>
91 , text::lfb_stream::OStringStream<typename Ctx::Buffer
92  , MessageWrap<LoggingT<Ctx::MAX_MESSAGE_SIZE>>
93  , LoggingT<Ctx::MAX_MESSAGE_SIZE>::typeTag
94  >
95 , pattern::GuardedSingleton<LoggerT<Ctx>> {
96  static_assert(Ctx::MAX_MESSAGE_SIZE != 0
97  , "HMBDC_LOG_CONTEXT needs to be 'compile time sized'");
99  friend struct pattern::SingletonGuardian<LoggerT<Ctx>>;
100 
101  enum Level {
102  L_DEBUG = 0,
103  L_NOTICE,
104  L_WARNING,
105  L_CRITICAL,
106  L_OFF
107  };
108 private:
109  std::ostream& log_;
110  static std::unique_ptr<Ctx> pCtx_s; //Logger is a singleton
111  Ctx& ctx_;
112  Level minLevel_;
113  std::string schedPolicy_;
114  int priority_;
115 
116  using Stream = text::lfb_stream::OStringStream<typename Ctx::Buffer
117  , MessageWrap<Logging>, Logging::typeTag>;
118 
119  LoggerT(std::ostream& log
120  , uint64_t cpuAffinityMask = 0
121  , char const* schedPolicy = "SCHED_IDLE"
122  , int priority = 0
123  , uint16_t logBufferSizePower2Num = 8)
124  : Stream((pCtx_s = std::unique_ptr<Ctx>(new Ctx(logBufferSizePower2Num)))->buffer())
125  , log_(log)
126  , ctx_(*pCtx_s)
127  , minLevel_(L_DEBUG)
128  , schedPolicy_(schedPolicy)
129  , priority_(priority) {
130 #ifdef HMBDC_LOG_CONTEXT
131  ctx_.start(*this
132  , cpuAffinityMask?cpuAffinityMask:(1ul << std::thread::hardware_concurrency()) - 1ul);
133 #endif
134  }
135 
136  ~LoggerT() {
137  if (pCtx_s) {
138  pCtx_s->stop();
139  pCtx_s->join();
140  }
141  }
142 
143  LoggerT(std::ostream& log, Ctx& ctx
144  , char const* schedPolicy = "SCHED_IDLE"
145  , int priority = 0)
146  : Stream(ctx.buffer())
147  , log_(log)
148  , ctx_(ctx)
149  , minLevel_(L_DEBUG)
150  , schedPolicy_(schedPolicy)
151  , priority_(priority) {
152  }
153 
154  struct LogHeader {
155  LogHeader(Level level)
156  : l(level)
157  , ts(hmbdc::time::SysTime::now()) {
158  }
159 
160  Level l;
162 
163  friend std::ostream& operator << (std::ostream& os, LogHeader const& h) {
164  os << h.ts << " " << g_LogLevelStr[h.l];
165  return os;
166  }
167  };
168 
169 public:
170  char const* hmbdcName() const { return "logger"; }
171  void setMinLogLevel(Level minLevel) {
172  minLevel_ = minLevel;
173  }
174 
175  std::tuple<char const*, int> schedSpec() const {
176  return std::make_tuple(schedPolicy_.c_str(), priority_);
177  }
178 
179  template <typename ...Args>
180  void LOG_D(Args&&... args) {
181 #ifndef NDEBUG
182  Stream::operator()(LogHeader(L_DEBUG), std::forward<Args>(args)...);
183 #endif
184  }
185 
186  template <typename ...Args>
187  void LOG_N(Args&&... args) {
188  if (minLevel_ <= L_NOTICE)
189  Stream::operator()(LogHeader(L_NOTICE), std::forward<Args>(args)...);
190  }
191  template <typename ...Args>
192  void LOG_W(Args&&... args) {
193  if (minLevel_ <= L_WARNING)
194  Stream::operator()(LogHeader(L_WARNING), std::forward<Args>(args)...);
195  }
196  template <typename ...Args>
197  void LOG_C(Args&&... args) {
198  if (minLevel_ <= L_CRITICAL)
199  Stream::operator()(LogHeader(L_CRITICAL), std::forward<Args>(args)...);
200  }
201 
202  void handleMessageCb(Logging& logItem) {
203  Stream::dump(log_, logItem);
204  }
205 };
206 
207 template <typename Ctx>
208 std::unique_ptr<Ctx> LoggerT<Ctx>::pCtx_s;
209 
210 /**
211  * @brief a very straightforward logger that works safely
212  * @details when HMBDC_LOG_CONTEXT is undefined, you get functionality of this
213  *
214  */
216 : pattern::GuardedSingleton<SimpleLogger> {
217  friend struct pattern::SingletonGuardian<SimpleLogger>;
218 
219  enum Level {
220  L_DEBUG = 0,
221  L_NOTICE,
222  L_WARNING,
223  L_CRITICAL,
224  L_OFF
225  };
226  template <typename ...Args>
227  void LOG_D(Args&&... args) {
228 #ifndef NDEBUG
229  std::lock_guard<std::recursive_mutex> g(mutex_);
230  log(std::forward<Args>(args)...);
231 #endif
232  }
233 
234  template <typename ...Args>
235  void LOG_N(Args&&... args) {
236  if (minLevel_ <= L_NOTICE) {
237  std::lock_guard<std::recursive_mutex> g(mutex_);
238  log(std::forward<Args>(args)...);
239  }
240  }
241  template <typename ...Args>
242  void LOG_W(Args&&... args) {
243  if (minLevel_ <= L_WARNING) {
244  std::lock_guard<std::recursive_mutex> g(mutex_);
245  log(std::forward<Args>(args)...);
246  }
247  }
248  template <typename ...Args>
249  void LOG_C(Args&&... args) {
250  if (minLevel_ <= L_CRITICAL) {
251  std::lock_guard<std::recursive_mutex> g(mutex_);
252  log(std::forward<Args>(args)...);
253  }
254  }
255 
256 private:
257  template <typename Arg, typename ...Args>
258  void log(Arg&& arg, Args&&... args) {
259  log_ << std::forward<Arg>(arg);
260  log(std::forward<Args>(args)...);
261  }
262  void log() {
263  }
264  template <typename ... NoOpArgs>
265  SimpleLogger(std::ostream& log, NoOpArgs&&...)
266  : log_(log)
267  , minLevel_(L_DEBUG)
268  {}
269  std::ostream& log_;
270  Level minLevel_;
271  std::recursive_mutex mutex_;
272 };
273 }}
274 
a very straightforward logger that works safely
Definition: LoggerT.hpp:215
a high performance async logger that doesn&#39;t penalize logging threads as much when the logging load i...
Definition: LoggerT.hpp:89
Definition: Message.hpp:103
base for the Singleton that works with SingletonGuardian
Definition: GuardedSingleton.hpp:35
RAII representing the lifespan of the underlying Singleton which also ganrantees the singularity of u...
Definition: GuardedSingleton.hpp:20
Definition: LoggerT.hpp:74
Definition: LoggerT.hpp:154
Definition: LfbStream.hpp:82
Definition: Time.hpp:13
Definition: Message.hpp:55
A Client represents a thread of execution/a task. The execution is managed by a Context. a Client object could participate in message dispatching as the receiver of specifed message types.
Definition: Client.hpp:47
Definition: Base.hpp:12
Definition: LoggerT.hpp:61