Simulant  21.09-46
A portable game engine for Windows, OSX, Linux, Dreamcast, and PSP
logging.h
1 #pragma once
2 
3 #include <chrono>
4 #include <string>
5 #include <fstream>
6 #include <iostream>
7 #include <memory>
8 #include <vector>
9 #include <sstream>
10 #include <unordered_set>
11 #include <unordered_map>
12 #include <iomanip>
13 
14 #include "utils/string.h"
15 #include "utils/formatter.h"
16 #include "threads/mutex.h"
17 #include "threads/thread.h"
18 
19 #include "compat.h"
20 
21 namespace smlt {
22 
23 enum LogLevel {
24  LOG_LEVEL_NONE = 0,
25  LOG_LEVEL_ERROR = 1,
26  LOG_LEVEL_WARN = 2,
27  LOG_LEVEL_INFO = 3,
28  LOG_LEVEL_DEBUG = 4
29 };
30 
31 class Logger;
32 
33 typedef std::chrono::time_point<std::chrono::system_clock> DateTime;
34 
35 class Handler {
36 public:
37  typedef std::shared_ptr<Handler> ptr;
38 
39  virtual ~Handler() {}
40  void write_message(Logger* logger,
41  const DateTime& time,
42  const std::string& level,
43  const std::string& message);
44 
45 private:
46  virtual void do_write_message(Logger* logger,
47  const DateTime& time,
48  const std::string& level,
49  const std::string& message) = 0;
50 };
51 
52 class StdIOHandler : public Handler {
53 public:
54  StdIOHandler();
55 
56 private:
57 
58  void do_write_message(Logger* logger,
59  const DateTime& time,
60  const std::string& level,
61  const std::string& message) override;
62 
63  thread::Mutex lock_;
64 };
65 
66 class FileHandler : public Handler {
67 public:
68  FileHandler(const std::string& filename);
69 
70 private:
71  void do_write_message(Logger* logger,
72  const DateTime& time,
73  const std::string& level,
74  const std::string& message);
75  std::string filename_;
76  std::ofstream stream_;
77 };
78 
79 class Logger {
80 public:
81  typedef std::shared_ptr<Logger> ptr;
82 
83  /* WARNING: Do not add a destructor - it won't get called! */
84 
85  Logger(const std::string& name):
86  name_(name),
87  level_(LOG_LEVEL_DEBUG) {
88 
89  }
90 
91  void add_handler(Handler::ptr handler) {
92  //FIXME: check it doesn't exist already
93  handlers_.push_back(handler);
94  }
95 
96  void debug(const std::string& text, const std::string& file="None", int32_t line=-1) {
97  if(level_ < LOG_LEVEL_DEBUG) return;
98 
99  write_message("DEBUG", text, file, line);
100  }
101 
102  void info(const std::string& text, const std::string& file="None", int32_t line=-1) {
103  if(level_ < LOG_LEVEL_INFO) return;
104 
105  write_message("INFO", "\x1b[36m" + text + "\x1b[0m", file, line);
106  }
107 
108  void warn(const std::string& text, const std::string& file="None", int32_t line=-1) {
109  if(level_ < LOG_LEVEL_WARN) return;
110 
111  write_message("WARN", "\x1b[33m" + text + "\x1b[0m", file, line);
112  }
113 
114  void warn_once(const std::string& text, const std::string& file="None", int32_t line=-1) {
115  /*
116  * This is *slow*, be aware of that, don't call in performance critical code!
117  */
118 
119  if(line == -1) {
120  warn(text, file, line); //Can't warn once if no line is specified
121  return;
122  }
123 
124  static std::unordered_map<std::string, std::unordered_set<int32_t>> warned;
125 
126  bool already_logged = warned.find(file) != warned.end() && warned[file].count(line);
127 
128  if(already_logged) {
129  return;
130  } else {
131  warned[file].insert(line);
132  warn(text, file, line);
133  }
134  }
135 
136  void error(const std::string& text, const std::string& file="None", int32_t line=-1) {
137  if(level_ < LOG_LEVEL_ERROR) return;
138 
139  write_message("ERROR", "\x1b[31m" + text + "\x1b[0m", file, line);
140  }
141 
142  void set_level(LogLevel level) {
143  level_ = level;
144  }
145 
146 private:
147  void write_message(const std::string& level, const std::string& text,
148  const std::string& file, int32_t line) {
149 
150  std::stringstream s;
151  s << thread::this_thread_id() << ": ";
152 
153  if(line > -1) {
154  s << text << " (" << file << ":" << line << ")";
155  } else {
156  s << text;
157  }
158 
159  for(uint32_t i = 0; i < handlers_.size(); ++i) {
160  handlers_[i]->write_message(this, std::chrono::system_clock::now(), level, s.str());
161  }
162  }
163 
164  std::string name_;
165  std::vector<Handler::ptr> handlers_;
166 
167  LogLevel level_;
168 };
169 
170 Logger* get_logger(const std::string& name);
171 
172 void debug(const std::string& text, const std::string& file="None", int32_t line=-1);
173 void info(const std::string& text, const std::string& file="None", int32_t line=-1);
174 void warn(const std::string& text, const std::string& file="None", int32_t line=-1);
175 void warn_once(const std::string& text, const std::string& file="None", int32_t line=-1);
176 void error(const std::string& text, const std::string& file="None", int32_t line=-1);
177 
178 
180 public:
181  DebugScopedLog(const std::string& text, const std::string& file, uint32_t line):
182  text_(text) {
183 
184  debug(smlt::Formatter("Enter: {0} ({1}, {2})").format(text, file, line));
185  }
186 
187  ~DebugScopedLog() {
188  debug(smlt::Formatter("Exit: {0}").format(text_));
189  }
190 
191 private:
192  std::string text_;
193 };
194 
195 }
196 
197 #ifndef NDEBUG
198 
199 #define S_DEBUG(str, ...) \
200  smlt::debug(_F(str).format(__VA_ARGS__), __FILE__, __LINE__)
201 
202 #define S_INFO(str, ...) \
203  smlt::info(_F(str).format(__VA_ARGS__), __FILE__, __LINE__)
204 
205 #define S_WARN(str, ...) \
206  smlt::warn(_F(str).format(__VA_ARGS__), __FILE__, __LINE__)
207 
208 #define S_ERROR(str, ...) \
209  smlt::error(_F(str).format(__VA_ARGS__), __FILE__, __LINE__)
210 
211 #define S_WARN_ONCE(str, ...) \
212  smlt::warn_once(_F(str).format(__VA_ARGS__), __FILE__, __LINE__)
213 
214 #else
215 
216 #define S_DEBUG(str, ...) \
217  smlt::debug(_F(str).format(__VA_ARGS__))
218 
219 #define S_INFO(str, ...) \
220  smlt::info(_F(str).format(__VA_ARGS__))
221 
222 #define S_WARN(str, ...) \
223  smlt::warn(_F(str).format(__VA_ARGS__))
224 
225 #define S_ERROR(str, ...) \
226  smlt::error(_F(str).format(__VA_ARGS__))
227 
228 #define S_WARN_ONCE(str, ...) \
229  smlt::warn_once(_F(str).format(__VA_ARGS__))
230 
231 #endif
smlt::FileHandler
Definition: logging.h:66
smlt::DebugScopedLog
Definition: logging.h:179
smlt
Definition: animation.cpp:25
smlt::StdIOHandler
Definition: logging.h:52
smlt::Formatter
Definition: formatter.h:11
smlt::Logger
Definition: logging.h:79
smlt::Handler
Definition: logging.h:35
smlt::thread::Mutex
Definition: mutex.h:25