Cuberite
A lightweight, fast and extensible game server for Minecraft
LoggerListeners.cpp
Go to the documentation of this file.
1 
2 #include "Globals.h"
3 
4 #include "LoggerListeners.h"
5 
6 #if defined(_WIN32)
7  #include <io.h> // Needed for _isatty(), not available on Linux
8  #include <time.h>
9 #endif
10 
11 
12 #if defined(_WIN32) || defined (__linux) || defined (__APPLE__)
13  class cColouredConsoleListener
14  : public cLogger::cListener
15  {
16  protected:
17 
18  virtual void SetLogColour(eLogLevel a_LogLevel) = 0;
19  virtual void SetDefaultLogColour() = 0;
20 
21  virtual void Log(std::string_view a_Message, eLogLevel a_LogLevel) override
22  {
23  SetLogColour(a_LogLevel);
24  fwrite(a_Message.data(), sizeof(char), a_Message.size(), stdout);
25  SetDefaultLogColour();
26  }
27  };
28 #endif
29 
30 
31 
32 
33 
34 #ifdef _WIN32
35  class cWindowsConsoleListener:
36  public cColouredConsoleListener
37  {
38  using Super = cColouredConsoleListener;
39 
40  public:
41 
42  cWindowsConsoleListener(HANDLE a_Console, WORD a_DefaultConsoleAttrib):
43  m_Console(a_Console),
44  m_DefaultConsoleAttrib(a_DefaultConsoleAttrib)
45  {
46  }
47 
48  #ifndef NDEBUG
49  virtual void Log(std::string_view a_Message, eLogLevel a_LogLevel) override
50  {
51  Super::Log(a_Message, a_LogLevel);
52  // In a Windows Debug build, output the log to debug console as well:
53  OutputDebugStringA(AString(a_Message).c_str());
54  }
55  #endif
56 
57 
58  virtual void SetLogColour(eLogLevel a_LogLevel) override
59  {
60  // by default, gray on black
61  WORD Attrib = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
62  switch (a_LogLevel)
63  {
64  case eLogLevel::Regular:
65  {
66  // Gray on black
67  Attrib = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
68  break;
69  }
70  case eLogLevel::Info:
71  {
72  // Yellow on black
73  Attrib = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
74  break;
75  }
76  case eLogLevel::Warning:
77  {
78  // Red on black
79  Attrib = FOREGROUND_RED | FOREGROUND_INTENSITY;
80  break;
81  }
82  case eLogLevel::Error:
83  {
84  // Black on red
85  Attrib = BACKGROUND_RED | BACKGROUND_INTENSITY;
86  break;
87  }
88  }
89  SetConsoleTextAttribute(m_Console, Attrib);
90  }
91 
92 
93  virtual void SetDefaultLogColour() override
94  {
95  SetConsoleTextAttribute(m_Console, m_DefaultConsoleAttrib);
96  }
97 
98  private:
99 
100  HANDLE m_Console;
101  WORD m_DefaultConsoleAttrib;
102  };
103 
104 
105 
106 #elif defined (__linux) || defined (__APPLE__)
107 
108 
109 
110  class cANSIConsoleListener
111  : public cColouredConsoleListener
112  {
113  public:
114  virtual void SetLogColour(eLogLevel a_LogLevel) override
115  {
116  switch (a_LogLevel)
117  {
118  case eLogLevel::Regular:
119  {
120  // Whatever the console default is
121  printf("\x1b[0m");
122  break;
123  }
124  case eLogLevel::Info:
125  {
126  // Yellow on black
127  printf("\x1b[33;1m");
128  break;
129  }
130  case eLogLevel::Warning:
131  {
132  // Red on black
133  printf("\x1b[31;1m");
134  break;
135  }
136  case eLogLevel::Error:
137  {
138  // Yellow on red
139  printf("\x1b[1;33;41;1m");
140  break;
141  }
142  }
143  }
144 
145 
146  virtual void SetDefaultLogColour() override
147  {
148  // Whatever the console default is
149  printf("\x1b[0m");
150  fflush(stdout);
151  }
152  };
153 
154 #endif
155 
156 
157 
158 
159 
161  : public cLogger::cListener
162 {
163 public:
164  virtual void Log(std::string_view a_Message, eLogLevel a_LogLevel) override
165  {
166  switch (a_LogLevel)
167  {
168  case eLogLevel::Regular:
169  {
170  fputs("Log: ", stdout);
171  break;
172  }
173  case eLogLevel::Info:
174  {
175  fputs("Info: ", stdout);
176  break;
177  }
178  case eLogLevel::Warning:
179  {
180  fputs("Warning: ", stdout);
181  break;
182  }
183  case eLogLevel::Error:
184  {
185  fputs("Error: ", stdout);
186  break;
187  }
188  }
189  fwrite(a_Message.data(), sizeof(char), a_Message.size(), stdout);
190  }
191 };
192 
193 
194 
195 
196 
197 // Listener for when stdout is closed, i.e. When running as a daemon.
199  : public cLogger::cListener
200 {
201  virtual void Log(std::string_view a_Message, eLogLevel a_LogLevel) override
202  {
203  }
204 };
205 
206 
207 
208 
209 
210 std::unique_ptr<cLogger::cListener> MakeConsoleListener(bool a_IsService)
211 {
212  if (a_IsService)
213  {
214  return std::make_unique<cNullConsoleListener>();
215  }
216 
217  #ifdef _WIN32
218  // See whether we are writing to a console the default console attrib:
219  bool ShouldColorOutput = (_isatty(_fileno(stdin)) != 0);
220  if (ShouldColorOutput)
221  {
222  CONSOLE_SCREEN_BUFFER_INFO sbi;
223  HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE);
224  GetConsoleScreenBufferInfo(Console, &sbi);
225  WORD DefaultConsoleAttrib = sbi.wAttributes;
226  return std::make_unique<cWindowsConsoleListener>(Console, DefaultConsoleAttrib);
227  }
228  else
229  {
230  return std::make_unique<cVanillaCPPConsoleListener>();
231  }
232  #elif defined (__linux) || defined (__APPLE__)
233  // TODO: lookup terminal in terminfo
234  if (isatty(fileno(stdout)))
235  {
236  return std::make_unique<cANSIConsoleListener>();
237  }
238  else
239  {
240  return std::make_unique<cVanillaCPPConsoleListener>();
241  }
242  #else
243  return std::make_unique<cVanillaCPPConsoleListener>();
244  #endif
245 }
246 
247 
248 
249 
250 
252 // cFileListener:
253 
255  : public cLogger::cListener
256 {
257 public:
258 
260 
261  bool Open()
262  {
263  // Assume creation succeeds, as the API does not provide a way to tell if the folder exists.
264  cFile::CreateFolder("logs");
265  bool success = m_File.Open(
266  fmt::format(
267  FMT_STRING("logs/LOG_{}.txt"),
268  std::chrono::duration_cast<std::chrono::duration<int, std::ratio<1>>>(
269  std::chrono::system_clock::now().time_since_epoch()
270  ).count()
271  ),
273  );
274  return success;
275  }
276 
277  virtual void Log(std::string_view a_Message, eLogLevel a_LogLevel) override
278  {
279  std::string_view LogLevelPrefix = "Unkn ";
280  bool ShouldFlush = false;
281  switch (a_LogLevel)
282  {
283  case eLogLevel::Regular:
284  {
285  LogLevelPrefix = " ";
286  break;
287  }
288  case eLogLevel::Info:
289  {
290  LogLevelPrefix = "Info ";
291  break;
292  }
293  case eLogLevel::Warning:
294  {
295  LogLevelPrefix = "Warn ";
296  ShouldFlush = true;
297  break;
298  }
299  case eLogLevel::Error:
300  {
301  LogLevelPrefix = "Err ";
302  ShouldFlush = true;
303  break;
304  }
305  }
306  m_File.Write(LogLevelPrefix);
307  m_File.Write(a_Message);
308 
309  if (ShouldFlush)
310  {
311  m_File.Flush();
312  }
313  }
314 
315 private:
316 
318 };
319 
320 
321 
322 
323 
324 std::pair<bool, std::unique_ptr<cLogger::cListener>> MakeFileListener()
325 {
326  auto listener = std::make_unique<cFileListener>();
327  if (!listener->Open())
328  {
329  return {false, nullptr};
330  }
331  return {true, std::move(listener)};
332 }
333 
334 
std::unique_ptr< cLogger::cListener > MakeConsoleListener(bool a_IsService)
std::pair< bool, std::unique_ptr< cLogger::cListener > > MakeFileListener()
eLogLevel
Definition: LoggerSimple.h:6
std::string AString
Definition: StringUtils.h:11
virtual void Log(std::string_view a_Message, eLogLevel a_LogLevel)=0
virtual void Log(std::string_view a_Message, eLogLevel a_LogLevel) override
virtual void Log(std::string_view a_Message, eLogLevel a_LogLevel) override
virtual void Log(std::string_view a_Message, eLogLevel a_LogLevel) override
Definition: File.h:38
void Flush()
Flushes all the bufferef output into the file (only when writing)
Definition: File.cpp:695
static bool CreateFolder(const AString &a_FolderPath)
Creates a new folder with the specified name.
Definition: File.cpp:454
@ fmAppend
Definition: File.h:57
bool Open(const AString &iFileName, eMode iMode)
Definition: File.cpp:52
int Write(const void *a_Buffer, size_t a_NumBytes)
Writes up to a_NumBytes bytes from a_Buffer, returns the number of bytes actually written,...
Definition: File.cpp:180