remotemono
log.h
1 /*
2  Copyright 2020 David "Alemarius Nexus" Lerch
3 
4  This file is part of RemoteMono.
5 
6  RemoteMono is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  RemoteMono is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public License
17  along with RemoteMono. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #pragma once
21 
22 #include "config.h"
23 
24 #include <cstdarg>
25 #include <ctime>
26 #include <cstdio>
27 #include <string>
28 #include <functional>
29 #include <list>
30 #include <windows.h>
31 
32 
33 
34 
35 namespace remotemono
36 {
37 
38 
49 {
50 public:
51  enum LogLevel
52  {
53  LOG_LEVEL_NONE = 0,
54  LOG_LEVEL_ERROR = 10,
55  LOG_LEVEL_WARNING = 20,
56  LOG_LEVEL_INFO = 30,
57  LOG_LEVEL_DEBUG = 40,
58  LOG_LEVEL_VERBOSE = 50
59  };
60 
61  struct LogMessage
62  {
63  std::string_view msg;
64  LogLevel level;
65  };
66 
67  typedef std::function<void (const LogMessage&)> LogFunction;
68  typedef int LogFunctionID;
69 
70 private:
71  struct LogFuncEntry
72  {
73  LogFuncEntry(LogFunction f, LogFunctionID id) : f(f), id(id) {}
74 
75  LogFunction f;
76  LogFunctionID id;
77  };
78 
79 public:
80  static RMonoLogger& getInstance()
81  {
82  static RMonoLogger inst;
83  return inst;
84  }
85 
86  void setLogLevel(LogLevel level) { this->level = level; }
87  LogLevel getLogLevel() const { return level; }
88 
89  const char* getLogLevelName(LogLevel level)
90  {
91  switch (level) {
92  case LOG_LEVEL_NONE: return "none";
93  case LOG_LEVEL_ERROR: return "error";
94  case LOG_LEVEL_WARNING: return "warning";
95  case LOG_LEVEL_INFO: return "info";
96  case LOG_LEVEL_DEBUG: return "debug";
97  case LOG_LEVEL_VERBOSE: return "verbose";
98  }
99  return "[INVALID]";
100  }
101 
102  bool isLogLevelActive(LogLevel level) { return this->level >= level; }
103 
104  LogFunctionID registerLogFunction(LogFunction f)
105  {
106  LogFunctionID id = nextLogFuncID++;
107  logFuncs.emplace_back(f, id);
108  return id;
109  }
110 
111  bool unregisterLogFunction(LogFunctionID id)
112  {
113  for (auto it = logFuncs.begin() ; it != logFuncs.end() ; it++) {
114  if (it->id == id) {
115  logFuncs.erase(it);
116  return true;
117  }
118  }
119  return false;
120  }
121 
122  template <typename... Args>
123  void logMessageUnchecked(LogLevel level, const char* fmt, Args... args)
124  {
125  logMessagevUnchecked(level, fmt, std::forward<Args>(args)...);
126  }
127 
128  template <typename... Args>
129  void logMessage(LogLevel level, const char* fmt, Args... args)
130  {
131  if (isLogLevelActive(level) && !logFuncs.empty()) {
132  logMessageUnchecked(level, fmt, args...);
133  }
134  }
135 
136 private:
137  RMonoLogger() : level(LOG_LEVEL_INFO), nextLogFuncID(1)
138  {
139  mtx = CreateMutex(NULL, false, NULL);
140  }
141 
142  void lockMutex() { WaitForSingleObject(mtx, INFINITE); }
143  void unlockMutex() { ReleaseMutex(mtx); }
144 
145  void logMessagevlUnchecked(LogLevel level, const char* fmt, va_list args)
146  {
147  // Don't check for log level. This is done by logMessage()
148 
149  lockMutex();
150 
151  va_list argsCpy;
152 
153  va_copy(argsCpy, args);
154  int len = vsnprintf(nullptr, 0, fmt, argsCpy);
155  va_end(argsCpy);
156 
157  char* msgStr = new char[len+1];
158 
159  va_copy(argsCpy, args);
160  vsnprintf(msgStr, len+1, fmt, argsCpy);
161  va_end(argsCpy);
162 
163  LogMessage msg;
164  msg.msg = std::string_view(msgStr, len);
165  msg.level = level;
166 
167  for (LogFuncEntry& e : logFuncs) {
168  e.f(msg);
169  }
170 
171  delete[] msgStr;
172 
173  unlockMutex();
174  }
175 
176  void logMessagevUnchecked(LogLevel level, const char* fmt, ...)
177  {
178  va_list args;
179  va_start(args, fmt);
180  logMessagevlUnchecked(level, fmt, args);
181  va_end(args);
182  }
183 
184 private:
185  LogLevel level;
186  std::list<LogFuncEntry> logFuncs;
187  LogFunctionID nextLogFuncID;
188 
189  HANDLE mtx;
190 };
191 
192 
193 
194 
195 
196 
201 {
202 public:
203  static RMonoStdoutLogFunction& getInstance()
204  {
205  static RMonoStdoutLogFunction inst;
206  return inst;
207  }
208 
209 public:
210  void registerLogFunction()
211  {
212  logFuncID = RMonoLogger::getInstance().registerLogFunction(*this);
213  }
214 
215  bool unregisterLogFunction()
216  {
217  bool res = RMonoLogger::getInstance().unregisterLogFunction(logFuncID);
218  logFuncID = 0;
219  return res;
220  }
221 
222  void operator()(const RMonoLogger::LogMessage& msg)
223  {
224  time_t t = time(NULL);
225 
226  const char* typeCode = "[???]";
227 
228  switch (msg.level) {
229  case RMonoLogger::LOG_LEVEL_ERROR:
230  typeCode = "[ERR]";
231  break;
232  case RMonoLogger::LOG_LEVEL_WARNING:
233  typeCode = "[WRN]";
234  break;
235  case RMonoLogger::LOG_LEVEL_INFO:
236  typeCode = "[INF]";
237  break;
238  case RMonoLogger::LOG_LEVEL_DEBUG:
239  typeCode = "[DBG]";
240  break;
241  case RMonoLogger::LOG_LEVEL_VERBOSE:
242  typeCode = "[VRB]";
243  break;
244  }
245 
246  char timeStr[128];
247 
248  struct tm localTime;
249  localtime_s(&localTime, &t);
250  strftime(timeStr, sizeof(timeStr), timeFormat.data(), &localTime);
251 
252  printf("%s %s - %s\n", typeCode, timeStr, msg.msg.data());
253  fflush(stdout);
254  }
255 
256  void setTimeFormat(const std::string& format) { timeFormat = format; }
257 
258 private:
260 
261 private:
262  std::string timeFormat = "%Y-%m-%d %H:%M:%S";
263  RMonoLogger::LogFunctionID logFuncID = 0;
264 };
265 
266 
267 
268 
269 
270 
271 template <class... T> void RMonoLogError(const char* fmt, T... args) { RMonoLogger::getInstance().logMessage(RMonoLogger::LOG_LEVEL_ERROR, fmt, std::forward<T>(args)...); }
272 template <class... T> void RMonoLogWarning(const char* fmt, T... args) { RMonoLogger::getInstance().logMessage(RMonoLogger::LOG_LEVEL_WARNING, fmt, std::forward<T>(args)...); }
273 template <class... T> void RMonoLogInfo(const char* fmt, T... args) { RMonoLogger::getInstance().logMessage(RMonoLogger::LOG_LEVEL_INFO, fmt, std::forward<T>(args)...); }
274 template <class... T> void RMonoLogDebug(const char* fmt, T... args) { RMonoLogger::getInstance().logMessage(RMonoLogger::LOG_LEVEL_DEBUG, fmt, std::forward<T>(args)...); }
275 template <class... T> void RMonoLogVerbose(const char* fmt, T... args) { RMonoLogger::getInstance().logMessage(RMonoLogger::LOG_LEVEL_VERBOSE, fmt, std::forward<T>(args)...); }
276 
277 
278 }
279 
remotemono::RMonoLogger
Definition: log.h:49
remotemono::RMonoLogger::LogMessage
Definition: log.h:62
remotemono::RMonoStdoutLogFunction
Definition: log.h:201