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