herald  2.0.0
sensor_logger.h
1 // Copyright 2020-2021 Herald Project Contributors
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 
5 #ifndef HERALD_SENSOR_LOGGER_H
6 #define HERALD_SENSOR_LOGGER_H
7 
8 #include "../datatype/bluetooth_state.h"
9 
10 #include <string>
11 #include <memory>
12 #include <ostream>
13 #include <sstream>
14 
15 // Zephyr compile workaround. Not ideal.
16 // #ifndef HERALD_LOG_LEVEL
17 // #define HERALD_LOG_LEVEL 4
18 // #endif
19 
20 #ifdef HERALD_LOG_LEVEL
21 
22 // Defines for within Impl class definitions
23 #if HERALD_LOG_LEVEL != 0
24 #define HLOGGER(_ctxT) \
25  herald::data::SensorLogger<typename _ctxT::logging_sink_type> logger;
26 #define HLOGGERINIT(_ctx,_subsystem,_category) ,logger(_ctx.getLoggingSink(),_subsystem,_category)
27 #endif
28 
29 // HDBG Defines for within main class (more common)
30 // HTDBG Defines for within Impl class
31 #if HERALD_LOG_LEVEL == 4
32 #define HDBG(_msg, ...) mImpl->logger.debug(_msg, ##__VA_ARGS__);
33 #define HTDBG(_msg, ...) logger.debug(_msg, ##__VA_ARGS__);
34 #define HLOG(_msg, ...) mImpl->logger.info(_msg, ##__VA_ARGS__);
35 #define HTLOG(_msg, ...) logger.info(_msg, ##__VA_ARGS__);
36 #define HERR(_msg, ...) mImpl->logger.fault(_msg, ##__VA_ARGS__);
37 #define HTERR(_msg, ...) logger.fault(_msg, ##__VA_ARGS__);
38 #endif
39 
40 #if HERALD_LOG_LEVEL == 3
41 #define HDBG(...) /* No debug log */
42 #define HTDBG(...) /* No debug log */
43 #define HLOG(_msg, ...) mImpl->logger.info(_msg, ##__VA_ARGS__);
44 #define HTLOG(_msg, ...) logger.info(_msg, ##__VA_ARGS__);
45 #define HERR(_msg, ...) mImpl->logger.fault(_msg, ##__VA_ARGS__);
46 #define HTERR(_msg, ...) logger.fault(_msg, ##__VA_ARGS__);
47 #endif
48 
49 // This 'WARN' exists for runtime valid logging. E.g. contacts.log to RTT on Zephyr
50 #if HERALD_LOG_LEVEL == 2
51 #define HDBG(...) /* No debug log */
52 #define HTDBG(...) /* No debug log */
53 #define HLOG(_msg, ...) mImpl->logger.info(_msg, ##__VA_ARGS__);
54 #define HTLOG(_msg, ...) logger.info(_msg, ##__VA_ARGS__);
55 #define HERR(_msg, ...) mImpl->logger.fault(_msg, ##__VA_ARGS__);
56 #define HTERR(_msg, ...) logger.fault(_msg, ##__VA_ARGS__);
57 #endif
58 
59 #if HERALD_LOG_LEVEL == 1
60 #define HDBG(...) /* No debug log */
61 #define HTDBG(...) /* No debug log */
62 #define HLOG(...) /* No info log */
63 #define HTLOG(...) /* No info log */
64 #define HERR(_msg, ...) mImpl->logger.fault(_msg, ##__VA_ARGS__);
65 #define HTERR(_msg, ...) logger.fault(_msg, ##__VA_ARGS__);
66 #endif
67 
68 #if HERALD_LOG_LEVEL == 0
69 
70 #define HLOGGER(_ctxT) /* No logger instance */
71 #define HLOGGERINIT(...) /* No logger init */
72 #define HDBG(...) /* No debug log */
73 #define HERR(...) /* No error log */
74 #define HLOG(...) /* No info log */
75 #define HTDBG(...) /* No debug log */
76 #define HTERR(...) /* No error log */
77 #define HTLOG(...) /* No info log */
78 
79 #endif
80 
81 #else
82 
83 #define HLOGGER(_ctxT) /* No logger instance */
84 #define HLOGGERINIT(...) /* No logger init */
85 #define HDBG(...) /* No debug log */
86 #define HERR(...) /* No error log */
87 #define HLOG(...) /* No info log */
88 #define HTDBG(...) /* No debug log */
89 #define HTERR(...) /* No error log */
90 #define HTLOG(...) /* No info log */
91 
92 #endif
93 
94 namespace herald {
95 
96 namespace data {
97 
98 enum class SensorLoggerLevel : int {
99  debug, info, fault
100 };
101 
102 /*
103 class LoggingSink {
104 public:
105  LoggingSink() = default;
106  ~LoggingSink() = default;
107 
108  void log(const std::string& subsystem, const std::string& category, SensorLoggerLevel level, std::string message);
109 };
110 */
111 
112 // NOTE: HEADER ONLY CLASS AS IT USES VARIABLE TEMPLATE ARGS FOR LOGGING
113 
114 // class SensorLoggingSink {
115 // public:
116 // SensorLoggingSink() = default;
117 // virtual ~SensorLoggingSink() = default;
118 
119 // virtual void log(SensorLoggerLevel level, std::string message) = 0;
120 // };
121 
122 namespace {
123 
124  [[maybe_unused]]
125  void tprintf(std::stringstream& os, const std::string& format) // base function
126  {
127  std::size_t pos = 0;
128  for ( auto c : format ) {
129  if ( c == '{' ) {
130  if (format.size() > pos + 1 && format.at(pos + 1) == '}') {
131  tprintf(os, format.substr(pos + 2)); // recursive call
132  } else {
133  tprintf(os, format.substr(pos + 1)); // recursive call
134  }
135  return;
136  }
137  os << c;
138  ++pos;
139  }
140  }
141 
142  template<typename... Targs>
143  void tprintf(std::stringstream& os, const std::string& format, std::uint8_t value, Targs... Fargs) // recursive variadic function
144  {
145  std::size_t pos = 0;
146  for ( auto c : format ) {
147  if ( c == '{' ) {
148  os << std::uint16_t(value);
149  if (format.size() > pos + 1 && format.at(pos + 1) == '}') {
150  tprintf(os, format.substr(pos + 2), Fargs...); // recursive call
151  } else {
152  tprintf(os, format.substr(pos + 1), Fargs...); // recursive call
153  }
154  return;
155  }
156  os << c;
157  ++pos;
158  }
159  }
160 
161  template<typename... Targs>
162  void tprintf(std::stringstream& os, const std::string& format, std::int8_t value, Targs... Fargs) // recursive variadic function
163  {
164  std::size_t pos = 0;
165  for ( auto c : format ) {
166  if ( c == '{' ) {
167  os << std::int16_t(value);
168  if (format.size() > pos + 1 && format.at(pos + 1) == '}') {
169  tprintf(os, format.substr(pos + 2), Fargs...); // recursive call
170  } else {
171  tprintf(os, format.substr(pos + 1), Fargs...); // recursive call
172  }
173  return;
174  }
175  os << c;
176  ++pos;
177  }
178  }
179 
180  // template<typename... Targs>
181  // void tprintf(std::stringstream& os, const std::string& format, const std::string& value, Targs... Fargs) // recursive variadic function
182  // {
183  // std::size_t pos = 0;
184  // for ( auto c : format ) {
185  // if ( c == '{' ) {
186  // os << value;
187  // if (format.size() > pos + 1 && format.at(pos + 1) == '}') {
188  // tprintf(os, format.substr(pos + 2), Fargs...); // recursive call
189  // } else {
190  // tprintf(os, format.substr(pos + 1), Fargs...); // recursive call
191  // }
192  // return;
193  // }
194  // os << c;
195  // pos++;
196  // }
197  // }
198 
199  // typename std::enable_if_t<std::is_convertible<T, std::string>::value, std::string>
200 
201  template<typename T>
202  void tprintf(std::stringstream& os, const std::string& format, T value) // recursive variadic function
203  {
204  std::size_t pos = 0;
205  for ( auto c : format ) {
206  if ( c == '{' ) {
207  os << value;
208  if (format.size() > pos + 1 && format.at(pos + 1) == '}') {
209  tprintf(os, format.substr(pos + 2)); // recursive call
210  } else {
211  tprintf(os, format.substr(pos + 1)); // recursive call
212  }
213  return;
214  }
215  os << c;
216  ++pos;
217  }
218  }
219 
220  template<typename FirstT, typename SecondT, typename... RestT>
221  void tprintf(std::stringstream& os, const std::string& format, FirstT first, SecondT second, RestT... rest)
222  {
223  std::size_t pos = 0;
224  for ( auto c : format ) {
225  if ( c == '{' ) {
226  os << first;
227  if (format.size() > pos + 1 && format.at(pos + 1) == '}') {
228  tprintf(os, format.substr(pos + 2), second, rest...); // recursive call
229  } else {
230  tprintf(os, format.substr(pos + 1), second, rest...); // recursive call
231  }
232  return;
233  }
234  os << c;
235  ++pos;
236  }
237  }
238 
239  // template<typename T, typename... Targs>
240  // void tprintf(std::stringstream& os, const std::string& format, T value, Targs... Fargs) // recursive variadic function
241  // {
242  // std::size_t pos = 0;
243  // for ( auto c : format ) {
244  // if ( c == '{' ) {
245  // os << value;
246  // if (format.size() > pos + 1 && format.at(pos + 1) == '}') {
247  // tprintf(os, format.substr(pos + 2), Fargs...); // recursive call
248  // } else {
249  // tprintf(os, format.substr(pos + 1), Fargs...); // recursive call
250  // }
251  // return;
252  // }
253  // os << c;
254  // pos++;
255  // }
256  // }
257 
258  // // G++ deduction guide workaround - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80438
259  // template<typename T, typename... Targs>
260  // void tprintf(std::stringstream& os, const std::string& format, Targs... Fargs)
261  // {
262  // tprintf(os, format, Fargs...);
263  // }
264 
265 }
266 
267 template <typename LoggingSinkT>
269 public:
270  SensorLogger(LoggingSinkT& sink, std::string subsystem, std::string category)
271  : mSink(sink), mSubsystem(subsystem), mCategory(category)
272  {
273  ;
274  }
275 
276  SensorLogger(const SensorLogger& other)
277  : mSink(other.mSink), mSubsystem(other.mSubsystem), mCategory(other.mCategory)
278  {
279  ;
280  }
281 
282  SensorLogger(SensorLogger&& other)
283  : mSink(other.mSink), mSubsystem(other.mSubsystem), mCategory(other.mCategory)
284  {
285  ;
286  }
287 
288  SensorLogger& operator=(const SensorLogger& other)
289  {
290  mSink = other.mSink;
291  mSubsystem = other.mSubsystem;
292  mCategory = other.mCategory;
293  return *this;
294  }
295 
296  SensorLogger& operator=(SensorLogger&& other)
297  {
298  mSink = other.mSink;
299  mSubsystem = other.mSubsystem;
300  mCategory = other.mCategory;
301  return *this;
302  }
303 
304  // TODO consider supporting multiple sinks in the context - E.g. USB UART and log file
305 
306  ~SensorLogger() = default;
307 
308  // use std::format to generate the string
309  // std::format in C++20, fmt::format library before that
310  // Note: C++11 Variadic template parameter pack expansion
311  template <typename ... Types>
312  void debug(const std::string& message, const Types&... args) {
313  const int size = sizeof...(args);
314  if (0 == size) {
315  log(SensorLoggerLevel::debug,message);
316  } else {
317  std::stringstream os;
318  tprintf(os,message,args...);
319  os << std::ends;
320  log(SensorLoggerLevel::debug, os.str());
321  }
322  }
323 
324  template <typename ... Types>
325  void info(const std::string& message, const Types&... args) {
326  const int size = sizeof...(args);
327  if (0 == size) {
328  log(SensorLoggerLevel::debug,message);
329  } else {
330  std::stringstream os;
331  tprintf(os,message,args...);
332  os << std::ends;
333  log(SensorLoggerLevel::info, os.str());
334  }
335  }
336 
337  template <typename ... Types>
338  void fault(const std::string& message, const Types&... args) {
339  const int size = sizeof...(args);
340  if (0 == size) {
341  log(SensorLoggerLevel::debug,message);
342  } else {
343  std::stringstream os;
344  tprintf(os,message,args...);
345  os << std::ends;
346  log(SensorLoggerLevel::fault, os.str());
347  }
348  }
349 
350 private:
351  inline void log(SensorLoggerLevel lvl, std::string msg) {
352  mSink.log(mSubsystem, mCategory, lvl, msg);
353  }
354 
355  LoggingSinkT& mSink;
356  std::string mSubsystem;
357  std::string mCategory;
358 };
359 
360 } // end namespace
361 } // end namespace
362 
363 #endif
Definition: sensor_logger.h:268
Acts as a non-global memory arena for arbitrary classes.
Definition: aggregates.h:15