TurtleBrains  0.3.5
High quality, portable, C++ framework for rapid 2D game development.
tb_debug_logger.hpp
1 
9 #ifndef TurtleBrains_DebugLogger_hpp
10 #define TurtleBrains_DebugLogger_hpp
11 
12 #include <turtle_brains/core/debug/tb_debug_tool_set.hpp>
13 #include <turtle_brains/core/tb_formatter.hpp>
14 #include <turtle_brains/core/tb_string.hpp>
15 
16 #include <unordered_map>
17 #include <sstream>
18 #include <string>
19 #include <list>
20 
25 
26 #if defined(tb_enable_debug_set) || defined(tb_generate_documentation)
27 
40  #define tb_log(formattedMessage, ...) \
41  TurtleBrains::Core::Debug::TheLogger() << TurtleBrains::Core::Debug::StartOfEntry() << \
42  TurtleBrains::Core::Formatter(formattedMessage, ##__VA_ARGS__) << TurtleBrains::Core::Debug::EndOfEntry();
43 
59  #define tb_log_if(testResult, formattedMessage, ...) \
60  if (testResult) { \
61  TurtleBrains::Core::Debug::TheLogger() << TurtleBrains::Core::Debug::StartOfEntry() << \
62  TurtleBrains::Core::Formatter(formattedMessage, ##__VA_ARGS__) << TurtleBrains::Core::Debug::EndOfEntry(); }
63 
64  #define tb_debug_log(log_stream) TurtleBrains::Core::Debug::TheLogger() << TurtleBrains::Core::Debug::StartOfEntry() << log_stream << TurtleBrains::Core::Debug::EndOfEntry();
65  #define tb_debug_log_if(testResult, log_stream) if (testResult) { TurtleBrains::Core::Debug::TheLogger() << TurtleBrains::Core::Debug::StartOfEntry() << log_stream << TurtleBrains::Core::Debug::EndOfEntry(); }
66  #define tb_debug_hexdump(message, rawData, dataSize) TurtleBrains::Core::Debug::LogHexDump(message, rawData, dataSize)
67 
68 #else //NO tb_enable_debug_set
69  #define tb_log(formattedMessage, ...) ((void)formattedMessage);
70  #define tb_log_if(test, formattedMessage, ...) ((void)(test));((void)formattedMessage);
71 
72  #define tb_debug_log(log_stream) ;
73  #define tb_debug_log_if(testResult, log_stream) ;
74  #define tb_debug_hexdump(message, rawData, dataSize) ((void)message);((void)rawData);((void)dataSize);
75 
76 #endif /* tb_enable_debug_set */
77 
78 #define tb_always_log(log_stream) TurtleBrains::Core::Debug::TheLogger() << TurtleBrains::Core::Debug::StartOfEntry() << log_stream << TurtleBrains::Core::Debug::EndOfEntry();
79 #define tb_always_log_if(testResult, log_stream) if (testResult) { TurtleBrains::Core::Debug::TheLogger() << TurtleBrains::Core::Debug::StartOfEntry() << log_stream << TurtleBrains::Core::Debug::EndOfEntry(); }
80 #define tb_always_hexdump(message, rawData, dataSize) TurtleBrains::Core::Debug::LogHexDump(message, rawData, dataSize)
81 
85 
86 namespace TurtleBrains
87 {
88  namespace Core
89  {
90  namespace Debug
91  {
92  class StartOfEntry;
93  class EndOfEntry;
94  class ContinueEntry;
95  class LogWithColor;
96  class LogWithQuotes;
97 
98  enum LogLevel
99  {
100  kAlways,
101  kError,
102  kWarning,
103  kInfo,
104  kDebug,
105  kTrace,
106  };
107 
108  enum class LogColor
109  {
110  Black,
111  Red,
112  Green,
113  Yellow,
114  Blue,
115  Magenta,
116  Cyan,
117  White,
118  Gray,
119  BrightRed,
120  BrightGreen,
121  BrightYellow,
122  BrightBlue,
123  BrightMagenta,
124  BrightCyan,
125  BrightWhite,
126  Reset
127  };
128 
138  void OpenLog(const std::string& logFile, bool createConsoleWindow);
139 
143  void SaveLog(void);
144 
149  void CloseLog(void);
150 
161  void Log(const char* formattedMessage, ...);
162 
176  void LogIf(const bool testResult, const char* formattedMessage, ...);
177 
181  void LogHexDump(const char* message, const void* rawData, size_t dataSize);
182 
184 
186  {
187  public:
188  StreamLogger(void);
189  ~StreamLogger(void);
190 
191  LogLevel GetLogLevel(void) const;
192  void SetLogLevel(const LogLevel& level);
193  void LogIfLevel(const LogLevel& level);
194 
195  void SetCurrentChannel(const tbCore::tbString& channel);
196  LogLevel GetLogLevel(const tbCore::tbString& channel) const;
197  void SetLogLevel(const tbCore::tbString& channel, const LogLevel& level);
198 
199  void SaveSettings(void);
200  void LoadSettings(void);
201 
202  private:
203  template<typename Type> friend StreamLogger& operator<<(StreamLogger& stream, const Type& value);
204  //template<> friend StreamLogger& operator<<(StreamLogger& stream, const LogColor& color);
205 
206  friend StreamLogger& operator<<(StreamLogger& stream, const StartOfEntry& startOfEntry);
207  friend StreamLogger& operator<<(StreamLogger& stream, const EndOfEntry& startOfEntry);
208  friend StreamLogger& operator<<(StreamLogger& stream, const ContinueEntry& startOfEntry);
209  friend StreamLogger& operator<<(StreamLogger& stream, const Formatter& formatted);
210  friend StreamLogger& operator<<(StreamLogger& stream, const LogColor& color);
211  friend StreamLogger& operator<<(StreamLogger& stream, const LogWithColor& coloredMessage);
212  friend StreamLogger& operator<<(StreamLogger& stream, const LogWithQuotes& quotedMessage);
213 
214  //Ultimately would like this to go away but C++ template specialization in translation unit wasn't
215  //working very well, though I suspect is very posssible since the type is known.
216  void SetColor(const LogColor& color);
217  void PushColor(const LogColor& color);
218  void PopColor(void);
219 
220  void ClearEntry(void);
221  void PostEntry(void);
222  void ContinueEntry(void);
223 
224 
225  std::stringstream mLogEntry;
226  LogLevel mDebugLevel;
227  LogLevel mDefaultLevel;
228  int mContinueEntry;
229  bool mDoOutput;
230  bool mHasSetChannel;
231 
232  typedef std::unordered_map<std::string, LogLevel> ChannelLevelTable;
233  ChannelLevelTable mChannelLevels;
234  std::string mCurrentChannel;
235  std::list<LogColor> mColorStack;
236  };
237 
238  //This needed to live above where-ever it gets used below because the compiler needs to know about.
239  //This should be considered as a private implementation detail, use the macros tb_debug_log or tb_always_log.
240  StreamLogger& TheLogger(void);
241 
243  {
244  public:
245  LogChannel(const std::string& channelName) :
246  mChannelName(channelName)
247  {
248  }
249 
250  friend StreamLogger& operator<<(StreamLogger& stream, const LogChannel& channel)
251  {
252  stream.SetCurrentChannel(channel.mChannelName);
253  return stream;
254  }
255 
256  private:
257  const std::string mChannelName;
258  };
259 
261  {
262  public:
263  ContinueEntry(void) { }
264  friend StreamLogger& operator<<(StreamLogger& stream, const ContinueEntry& continueEntry);
265  };
266 
268  {
269  public:
270  friend StreamLogger& operator<<(StreamLogger& stream, const StartOfEntry& startOfEntry);
271  };
272 
274  {
275  public:
276  friend StreamLogger& operator<<(StreamLogger& stream, const EndOfEntry& endOfEntry);
277  };
278 
280  {
281  public:
282  LogWithColor(const LogColor color, const tbCore::tbString& message) :
283  mMessage(message),
284  mColor(color)
285  {
286  }
287 
288  static LogWithColor Black(const tbCore::tbString& message) { return LogWithColor(LogColor::Black, message); }
289  static LogWithColor Red(const tbCore::tbString& message) { return LogWithColor(LogColor::Red, message); }
290  static LogWithColor Green(const tbCore::tbString& message) { return LogWithColor(LogColor::Green, message); }
291  static LogWithColor Yellow(const tbCore::tbString& message) { return LogWithColor(LogColor::Yellow, message); }
292  static LogWithColor Blue(const tbCore::tbString& message) { return LogWithColor(LogColor::Blue, message); }
293  static LogWithColor Magenta(const tbCore::tbString& message) { return LogWithColor(LogColor::Magenta, message); }
294  static LogWithColor Cyan(const tbCore::tbString& message) { return LogWithColor(LogColor::Cyan, message); }
295  static LogWithColor White(const tbCore::tbString& message) { return LogWithColor(LogColor::White, message); }
296  static LogWithColor Gray(const tbCore::tbString& message) { return LogWithColor(LogColor::Gray, message); }
297  static LogWithColor BrightRed(const tbCore::tbString& message) { return LogWithColor(LogColor::BrightRed, message); }
298  static LogWithColor BrightGreen(const tbCore::tbString& message) { return LogWithColor(LogColor::BrightGreen, message); }
299  static LogWithColor BrightYellow(const tbCore::tbString& message) { return LogWithColor(LogColor::BrightYellow, message); }
300  static LogWithColor BrightBlue(const tbCore::tbString& message) { return LogWithColor(LogColor::BrightBlue, message); }
301  static LogWithColor BrightMagenta(const tbCore::tbString& message) { return LogWithColor(LogColor::BrightMagenta, message); }
302  static LogWithColor BrightCyan(const tbCore::tbString& message) { return LogWithColor(LogColor::BrightCyan, message); }
303  static LogWithColor BrightWhite(const tbCore::tbString& message) { return LogWithColor(LogColor::BrightWhite, message); }
304 
305  private:
306  friend StreamLogger& operator<<(StreamLogger& stream, const LogWithColor& coloredMessage);
307 
308  const tbCore::tbString mMessage;
309  const LogColor mColor;
310  };
311 
313  {
314  public:
315  LogWithQuotes(const tbCore::tbString& message) :
316  mMessage(message)
317  {
318  }
319 
320  private:
321  friend StreamLogger& operator<<(StreamLogger& stream, const LogWithQuotes& quotedMessage);
322 
323  const tbCore::tbString mMessage;
324  };
325 
336  template<typename ChannelNamer> class LogChannelLevel
337  {
338  public:
339  class Setter
340  {
341  public:
342  explicit Setter(const LogLevel& level) : mLogLevel(level) { }
343 
344  friend tbCore::Debug::StreamLogger& operator<<(tbCore::Debug::StreamLogger& stream, const Setter& setter)
345  {
346  stream.SetCurrentChannel(ChannelNamer::AsString());
347  stream.LogIfLevel(setter.mLogLevel);
348  return stream;
349  }
350 
351  LogLevel mLogLevel;
352  };
353 
354  //Hacking a test in to see if printf can work with channels / severities...
355  //
356  // Would love to see all log calls go through the same macro "tb_log" (always/debug) and always support
357  // the option of Channels / Severity by doing some magic to include printf formatting in a friendly way.
358  //
359  // This should NOT call tb_always_out from within, as that would not allow it to work.
360  //
361  // Most desired call:
362  // tb_always_out(LogInit::LogAlways("Hello %s with %d bytes.", "turtles", 42));
363  // might be able to do the above with a Formatter object that holds a buffer to output in
364  // StreamLogger<< operator quite like the below line, and return this Format
365  // object from LogAlways() or AlwaysFormat() or whatever. This would give channel/severity
366  // to printf like-logging; and keep tb_log defined to the Formatter object. (though still deprecate).
367  //
368  // tb_always_out(LogInit::Always() << Formatter("Hello %s with %d bytes.", "turtles", 42));
369  //
370  // Note the coloring thing could work quite the same way.
371  //
372  // Currently this is working:
373  // LogInit::LogAlways("Hello %s with %d bytes.", "turtles", 42);
374 
375  //The following is the desired way to call it:
376  //
377 
378  //NOTE: If this is supported we should do all severity levels like it. If it is not supported then it
379  // should be removed and the ContinueEntry/StartOfEntry/EndOfEntry should be moved below the object.
380  static void LogAlways(const char* formattedMessage, ...)
381  {
382  va_list argumentsList;
383  va_start(argumentsList, formattedMessage);
384  TheLogger() << StartOfEntry() << Formatter::FromArgumentList(formattedMessage, argumentsList) << EndOfEntry();
385  va_end(argumentsList);
386  }
387 
388  static LogWithQuotes Quote(const tbCore::tbString& message) { return LogWithQuotes(message); }
389 
390  static LogLevel GetLogLevel(void)
391  {
392  return TheLogger().GetLogLevel(ChannelNamer::AsString());
393  }
394 
395  static void SetLogLevel(const LogLevel& logLevel)
396  {
397  TheLogger().SetLogLevel(ChannelNamer::AsString(), logLevel);
398  }
399 
400  static inline tbCore::tbString AsString(void) { return ChannelNamer::AsString(); }
401 
402  static inline Setter Always(void) { return Setter(LogLevel::kAlways); }
403  static inline Setter Error(void) { return Setter(LogLevel::kError); }
404  static inline Setter Warning(void) { return Setter(LogLevel::kWarning); }
405  static inline Setter Info(void) { return Setter(LogLevel::kInfo); }
406  static inline Setter Debug(void) { return Setter(LogLevel::kDebug); }
407  static inline Setter Trace(void) { return Setter(LogLevel::kTrace); }
408  };
409 
410 
411  template<typename Type> StreamLogger& operator<<(StreamLogger& stream, const Type& value)
412  {
413  if (stream.mDoOutput)
414  {
415  stream.mLogEntry << value;
416  }
417  return stream;
418  }
419 
420  inline StreamLogger& operator<<(StreamLogger& stream, const LogColor& color)
421  {
422  //SetColor(color);
423  if (LogColor::Reset == color)
424  {
425  stream.PopColor();
426  }
427  else
428  {
429  stream.PushColor(color);
430  }
431  return stream;
432  }
433 
434 
435  inline StreamLogger& operator<<(StreamLogger& stream, const LogLevel& logLevel)
436  {
437  stream.LogIfLevel(logLevel);
438  return stream;
439  }
440 
441  inline StreamLogger& operator<<(StreamLogger& stream, const ContinueEntry& continueEntry)
442  {
443  (void(continueEntry));
444  stream.ContinueEntry();
445  return stream;
446  }
447 
448  inline StreamLogger& operator<<(StreamLogger& stream, const StartOfEntry& startOfEntry)
449  {
450  (void(startOfEntry));
451  stream.ClearEntry();
452  return stream;
453  }
454 
455  inline StreamLogger& operator<<(StreamLogger& stream, const EndOfEntry& endOfEntry)
456  {
457  (void(endOfEntry));
458  stream.PostEntry();
459  return stream;
460  }
461 
462  inline StreamLogger& operator<<(StreamLogger& stream, const Formatter& formatted)
463  {
464  if (true == stream.mDoOutput)
465  {
466  stream.mLogEntry << formatted.c_str();
467  }
468  return stream;
469  }
470 
471  inline StreamLogger& operator<<(StreamLogger& stream, const LogWithColor& coloredMessage)
472  {
473  if (true == stream.mDoOutput)
474  {
475  stream << coloredMessage.mColor << coloredMessage.mMessage << LogColor::Reset;
476  }
477  return stream;
478  }
479 
480  inline StreamLogger& operator<<(StreamLogger& stream, const LogWithQuotes& quotedMessage)
481  {
482  if (true == stream.mDoOutput)
483  {
484  stream << "\"" << quotedMessage << "\"";
485  }
486  return stream;
487  }
488 
489  //struct SystemChannel { static tbCore::tbString AsString(void) { return ("System"); } };
490  //typedef TurtleBrains::Core::Debug::LogChannelLevel<SystemChannel> LogSystem;
491  struct GraphicsChannel { static tbCore::tbString AsString(void) { return ("Graphics"); } };
493  struct GameplayChannel { static std::string AsString(void) { return ("Gameplay"); } };
495  struct NetworkChannel { static std::string AsString(void) { return ("Network"); } };
497  struct PhysicsChannel { static std::string AsString(void) { return ("Physics"); } };
499 
500  }; /* namespace Debug */
501  }; /* namespace Core */
502 }; /* namespace TurtleBrains */
503 
504 #endif /* TurtleBrains_DebugLogger_hpp */
void OpenLog(const std::string &logFile, bool createConsoleWindow)
Definition: tb_debug_logger.hpp:491
Definition: tb_debug_logger.hpp:339
Definition: tb_debug_logger.hpp:493
Definition: tb_debug_logger.hpp:267
Definition: tb_formatter.hpp:23
Definition: tb_debug_logger.hpp:497
void LogIf(const bool testResult, const char *formattedMessage,...)
Here is some information about the primary namespace.
Definition: tb_application_dialog.hpp:21
Definition: tb_debug_logger.hpp:185
void LogHexDump(const char *message, const void *rawData, size_t dataSize)
Definition: tb_debug_logger.hpp:312
Definition: tb_debug_logger.hpp:273
static Formatter FromArgumentList(const char *formattedMessage, va_list argumentsList)
void Log(const char *formattedMessage,...)
Definition: tb_debug_logger.hpp:336
Definition: tb_debug_logger.hpp:242
std::string tbString
Definition: tb_string.hpp:335
Definition: tb_debug_logger.hpp:495
Definition: tb_debug_logger.hpp:279
Definition: tb_debug_logger.hpp:260