NAP
logger.h
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 #pragma once
6 
7 // Local Includes
8 #include "utility/stringutils.h"
9 #include "signalslot.h"
10 #include "rtti/object.h"
11 
12 // External Includes
13 #include <memory>
14 #include <mutex>
15 #include <string>
16 #include <queue>
17 #include <thread>
18 #include <fstream>
19 #include <nap/datetime.h>
20 #include <atomic>
21 
29 #define NAP_DECLARE_LOG_LEVEL(LEVEL, NAME) \
30  static LogLevel& NAME##Level() \
31  { \
32  static LogLevel lvl(#NAME, LEVEL); \
33  return lvl; \
34  } \
35  \
36  template<typename T> \
37  static void NAME(T&& msg) \
38  { \
39  instance().log(LogMessage(NAME##Level(), std::forward<T>(msg))); \
40  } \
41  \
42  static void NAME(const rtti::Object& obj, const std::string& msg) \
43  { \
44  instance().log(LogMessage(NAME##Level(), utility::stringFormat("%s: %s", obj.mID.c_str(), msg.c_str()))); \
45  } \
46  \
47  template <typename... Args> \
48  static void NAME(const char* msg, Args&&... args) \
49  { \
50  instance().log(LogMessage(NAME##Level(), utility::stringFormat(msg, std::forward<Args>(args)...))); \
51  } \
52  \
53  template <typename... Args> \
54  static void NAME(rtti::Object& obj, const char* msg, Args&&... args) \
55  { \
56  std::string msg_str = utility::stringFormat("%s: %s", obj.mID.c_str(), msg); \
57  instance().log(LogMessage(NAME##Level(), utility::stringFormat(msg_str.c_str(), std::forward<Args>(args)...))); \
58  }
59 
60 
61 namespace nap
62 {
70  class NAPAPI LogLevel
71  {
72  public:
77  LogLevel(const std::string& name, int level) : mName(name), mLevel(level) {}
78  LogLevel(const LogLevel&) = delete;
79  LogLevel& operator=(const LogLevel&) = delete;
80 
81  // returns the level number to indicate the level's ranking
82  int level() const { return mLevel; }
83  const std::string& name() const { return mName; }
84 
85  // comparison operators
86  bool operator<(const LogLevel& other) const { return this->mLevel < other.mLevel; }
87  bool operator==(const LogLevel& other) const { return this->mLevel == other.mLevel; }
88  bool operator<=(const LogLevel& other) const { return this->mLevel <= other.mLevel; }
89  bool operator>=(const LogLevel& other) const { return this->mLevel >= other.mLevel; }
90 
91  private:
92  std::string mName;
93  int mLevel;
94  };
95 
96 
101  class NAPAPI LogMessage
102  {
103  public:
107  LogMessage(const LogLevel& lvl, const std::string& msg);
108 
112  LogMessage(const LogLevel& lvl , std::string&& msg);
113 
117  const LogLevel& level() const { return *mLevel; }
118 
122  const std::string& text() const { return mMessage; }
123 
127  const SystemTimeStamp& getTimestamp() const { return mTimeStamp; }
128 
129  private:
130  const LogLevel* mLevel;
131  const std::string mMessage;
132  const SystemTimeStamp mTimeStamp;
133  };
134 
135  // Shorthand for string formatter
136  using LogMessageFormatter = std::function<std::string(const LogMessage& msg)>;
137 
143  std::string basicLogMessageFormatter(const LogMessage& msg);
144 
150  std::string timestampLogMessageFormatter(const LogMessage& msg);
151 
155  class NAPAPI LogHandler
156  {
157  public:
158  LogHandler();
159  virtual ~LogHandler() = default;
160 
166  virtual void commit(LogMessage msg) = 0;
167 
173  void setLogLevel(const LogLevel& level) { mLevel = &level; }
174 
179  const LogLevel& getLogLevel() const { return *mLevel; }
180 
185  void setFormatter(LogMessageFormatter formatter);
186 
192  std::string formatMessage(LogMessage& msg);
193 
194  private:
195  const LogLevel* mLevel;
196  LogMessageFormatter mFormatter = nullptr;
197  };
198 
204  class NAPAPI Logger
205  {
206  public:
211  static void setLevel(const LogLevel& lvl) { instance().setCurrentLevel(lvl); }
212 
217  void setCurrentLevel(const LogLevel& level);
218 
222  static Logger& instance();
223 
224  // this signal is emitted every time a log message is output.
226 
227  // all log messages will be displayed
228  NAP_DECLARE_LOG_LEVEL(50, fine)
229 
230  // messages including debug messages
231  NAP_DECLARE_LOG_LEVEL(100, debug)
232 
233  // general information about the proceedings of the app
234  NAP_DECLARE_LOG_LEVEL(200, info)
235 
236  // warnings about unusual situations
237  NAP_DECLARE_LOG_LEVEL(300, warn)
238 
239  // error messages indicate that something broke, but we're not ready to die yet
240  NAP_DECLARE_LOG_LEVEL(400, error)
241 
242  // fatal errors that seriously hinder the functionality of the app
243  NAP_DECLARE_LOG_LEVEL(500, fatal)
244 
245 
248  static const std::vector<const LogLevel*>& getLevels()
249  {
250  // In-line so you will remember to amend this list
251  // when you add or remove a LogLevel above.
252  // Must be ordered by level value.
253  static std::vector<const LogLevel*> lvls = {
254  &fineLevel(),
255  &debugLevel(),
256  &infoLevel(),
257  &warnLevel(),
258  &errorLevel(),
259  &fatalLevel()
260  };
261  return lvls;
262  }
263 
268  static const LogLevel* getLevel(const std::string& name);
269 
274  static void addFileHandler(const std::string& filename);
275 
283  static void logToDirectory(const std::string& directory, const std::string& prefix = "log");
284 
290  void addHandler(std::unique_ptr<LogHandler> handler);
291 
292  private:
293  // The logger is a singleton
294  Logger();
295  Logger(Logger const&);
296  void operator=(Logger const&);
297 
298  void initialize();
299  void onLog(const LogMessage& message);
300 
301  Slot<LogMessage> onLogSlot = {[&](LogMessage message) { onLog(message); }};
302  const LogLevel* mLevel;
303  std::vector<std::unique_ptr<LogHandler>> mHandlers{};
304  };
305 
306 
311  {
312  public:
313  ConsoleLogHandler() = default;
314 
319  void commit(LogMessage message) override;
320 
321  private:
322  std::mutex mOutStreamMutex;
323  };
324 
331  class FileLogHandler : public LogHandler
332  {
333  public:
334  FileLogHandler(const std::string& mFilename);
335  ~FileLogHandler();
336 
341  void commit(LogMessage message) override;
342 
343  private:
344  void writeLoop();
345 
346  const std::string mFilename;
347  std::unique_ptr<std::thread> mWriteThread;
348  std::queue<LogMessage> mMessages;
349  std::mutex mQueueMutex;
350  std::atomic<bool> mRunning;
351  };
352 
353 
354 }
nap::LogLevel::LogLevel
LogLevel(const std::string &name, int level)
Definition: logger.h:77
nap::Logger::log
Signal< LogMessage > log
Definition: logger.h:225
nap::LogLevel
Definition: logger.h:70
nap::FileLogHandler::FileLogHandler
FileLogHandler(const std::string &mFilename)
nap::SystemTimeStamp
std::chrono::time_point< SystemClock > SystemTimeStamp
Point in time associated with the SystemClock.
Definition: datetime.h:28
nap::LogMessage::level
const LogLevel & level() const
Definition: logger.h:117
nap::basicLogMessageFormatter
std::string basicLogMessageFormatter(const LogMessage &msg)
nap::Slot
Slot.
Definition: signalslot.h:21
nap::timestampLogMessageFormatter
std::string timestampLogMessageFormatter(const LogMessage &msg)
nap::LogMessageFormatter
std::function< std::string(const LogMessage &msg)> LogMessageFormatter
Definition: logger.h:136
nap::LogHandler::getLogLevel
const LogLevel & getLogLevel() const
Definition: logger.h:179
nap::LogLevel::operator>=
bool operator>=(const LogLevel &other) const
Definition: logger.h:89
nap::ConsoleLogHandler::commit
void commit(LogMessage message) override
nap::ConsoleLogHandler
Definition: logger.h:310
nap::ConsoleLogHandler::ConsoleLogHandler
ConsoleLogHandler()=default
nap::Logger::setLevel
static void setLevel(const LogLevel &lvl)
Definition: logger.h:211
nap::LogLevel::operator<=
bool operator<=(const LogLevel &other) const
Definition: logger.h:88
nap::Signal
Definition: signalslot.h:28
nap::FileLogHandler::commit
void commit(LogMessage message) override
nap::LogLevel::name
const std::string & name() const
Definition: logger.h:83
nap::FileLogHandler
Definition: logger.h:331
nap::LogLevel::operator==
bool operator==(const LogLevel &other) const
Definition: logger.h:87
nap::LogLevel::operator<
bool operator<(const LogLevel &other) const
Definition: logger.h:86
nap::LogMessage::text
const std::string & text() const
Definition: logger.h:122
nap::LogMessage::getTimestamp
const SystemTimeStamp & getTimestamp() const
Definition: logger.h:127
nap::LogLevel::level
int level() const
Definition: logger.h:82
nap
Definition: templateapp.h:17
nap::LogHandler::setLogLevel
void setLogLevel(const LogLevel &level)
Definition: logger.h:173
nap::LogHandler
Definition: logger.h:155
nap::LogMessage
Definition: logger.h:101
nap::FileLogHandler::~FileLogHandler
~FileLogHandler()
nap::Logger
Definition: logger.h:204